事务
要么都执行,要么都不执行。
事务的四个特性
- 原子性 Atomicity
- 一致性 Consistency
- 隔离性 Isolation 一个事务的执行不能被其他事务干扰。
- 持久性 Durability 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
TODO MySQL 是如何实现这四个特性的呢?
- atomicity 原子性是通过
undo log
来保证的 - consistency 一致性通过
持久性+原子性+隔离性
来保证的 - isolation MVCC 隔离性是通过
多并发版本控制
或锁机制
来保证的。 - durability 持久性是通过
redo log
来保证的。
事务传播行为
解决多个事务嵌套调用的问题。
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,就在嵌套事务(外层事务的子事务) 内执行;如果当前没有事务,就创建一个新的事务。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
PROPAGATION_REQUIRED、PROPAGATION_REQUIRED_NEW、PROPAGATION_NESTED 的区别
PROPAGATION_REQUIRED
内层方法会加入到这个事务中。即内外层方法使用同一个事务。
PROPAGATION_REQUIRED_NEW
内层方法会新建一个事务,内层方法新建的事务并不会影响到外层事务。
即,如果内层事务回滚,不会影响到外层事务。
PROPAGATION_NESTED
内层方法新的事务是外层方法事务的子事务。外层的事务回滚,嵌套的子事务一定会回滚。
嵌套的子事务回滚,不会影响到外层的事务。
事务传播行为测试
事务的隔离级别
事务的隔离级别有四种
- READ_UNCOMMITTED:未提交读,可以看到其他事务未提交的数据。
- READ_COMMITTED:已提交读,可以看到其他事务已提交的数据。
- REPEATABLE_READ:可重复读,能确保同一事务多次查询的结果一致。 (MySQL 的默认隔离级别)
- SERIALIZABLE:串行化,最高的事务隔离级别,它会强制事务排序,使之不发生冲突。 对记录上锁,在多个事务对这条记录操作时,如果出现读写冲突的情况,后访问的事务必须等前一个事务 执行完成,才能继续执行。
未提交读 read uncommitted
- 事务在读数据的时候并未对数据加锁。
- 事务在修改数据的时候只对数据增加行级共享锁。
提交读 read committed
- 事务对当前被读取的数据加行级共享锁(当读到时加锁),一旦读完该行,立即释放该行级共享锁。
- 事务在更新某数据瞬间(就是发送更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
可重复度 repeatable read
- 事务在读取某数据的瞬间(就是 开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放。
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
可序列化 serializable
- 事务在读取数据时,必须先对其加表级共享锁 ,直到事务结束才释放;
- 事务在更新数据时,必须先对其加表级排他锁 ,直到事务结束才释放。
脏读、不可重复读、幻读
- 脏读 dirty read:一个事务读取到了另外一个事务修改的数据之后,另外一个事务出现了回滚的操作,导致第一个事务读取的数据是错误的。
查询到了其他事务未提交的数据。
- 不可重复读 non-repeatable:一个事务两次查询得到的结果不同,因为在两次查询中间,有另一个事务修改了数据。
查询到了其他事务已提交的数据。
- 幻读 phantom read:一个事务两次查询中得到的结果集不同,因为在两次查询中间,有另一个事务新增了数据。
前后读取的数据条数不一致。
MySQL 的 REPEATABLE READ
级别很大程度上避免了幻读问题。
- 快照读(
select .. 语句
)通过 MVCC(多版本控制) - 当前读(
select .. for update 语句
) next-key lock 记录锁+间隙锁
READ COMMITTED
和 REPEATABLE READ
是通过 READ VIEW
(理解为一个数据快照)来实现的。
- READ COMMITTED 在每个语句执行前生成一个 READ VIEW
- REPEATABLE READ 在每个事务启动时生成一个 READ VIEW
事务的启动时机 开启事务有两种方式
begin/start transaction
在执行第一条 SQL 时,才是事务真正启动的时机。start transaction with consistent snapshot
命令执行时,事务就会启动。