跳到主要内容

事务

要么都执行,要么都不执行。

事务的四个特性

  • 原子性 Atomicity
  • 一致性 Consistency
  • 隔离性 Isolation 一个事务的执行不能被其他事务干扰。
  • 持久性 Durability 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

TODO MySQL 是如何实现这四个特性的呢?

  • atomicity 原子性是通过 undo log 来保证的
  • consistency 一致性通过持久性+原子性+隔离性来保证的
  • isolation MVCC 隔离性是通过多并发版本控制锁机制来保证的。
  • durability 持久性是通过 redo log 来保证的。

事务传播行为

解决多个事务嵌套调用的问题。

  1. TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务。
  3. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,就在嵌套事务(外层事务的子事务) 内执行;如果当前没有事务,就创建一个新的事务。
  4. TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
  5. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  6. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  7. 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 COMMITTEDREPEATABLE READ 是通过 READ VIEW (理解为一个数据快照)来实现的。

  • READ COMMITTED 在每个语句执行前生成一个 READ VIEW
  • REPEATABLE READ 在每个事务启动时生成一个 READ VIEW

事务的启动时机 开启事务有两种方式

  1. begin/start transaction 在执行第一条 SQL 时,才是事务真正启动的时机。
  2. start transaction with consistent snapshot 命令执行时,事务就会启动。