事务隔离级别

211 阅读3分钟
锁的类型

事务隔离级别通过对数据行(或范围)加不同的锁,实现事务之间对相同数据读写顺序的控制,以实现事务的隔离性。

  • 读锁(X-lock):多个事务可以对同一行数据加读锁,其他事务不得加写锁
  • 写锁(S-lock):只有一个事务能对一行数据加读锁,其他事务均不能加读锁和写锁
  • 范围锁(Range lock):只有一个事务能加范围锁,锁住一个范围(如:select * from t_user where id<10 for update),其他事务不得在此范围修改、增加、删除数据
隔离性(Isolation)被破坏
  • 幻读:完全相同的范围读取,同一个事务前后读取到的数据集不同(其他事务增删数据行)
  • 不可重复读:同一个事务内部两次读取同一数据行得到结果不同(其他事务更新对应数据行)
  • 脏读:读到其他事务未提交的改动
  • 脏写:多个事务对相同数据的写操作出现丢失(任何隔离级别都不应该出现脏写)
级别读锁写锁范围锁问题
串行YYY
可重复读YYN幻读
读已提交Y(读取后释放) YN幻读 + 不可重复读
读未提交N(完全不加锁)YN幻读 + 不可重复读 + 脏读

Note:读未提交完全不加读锁,导致它反而能读到其他事务加了写锁的数据。写锁的含义:假设T1添加了写锁,写锁禁止T2事务对相同数据添加读锁和写锁,但并不是禁止T2读取数据,如果T2根本不需要加锁则可以直接读取到T1加了写锁但未提交的数据

MySQL隔离级别与MVCC

MySQL默认隔离级别是可重复读,在只读事务避免了幻读,读写事务中仍然会幻读,如:本应该更新3条数据,实际更新了超过3条,参考下面MVCC内容

MVCC(Multi-Version Concurrency Control)

多版本并发控制,是一种读取优化策略,它的无锁特指读取时不需要加锁,基本思路:修改操作不会覆盖之前数据,而是产生一个新版本。版本理解为每一行数据都有create_version、delete_version两个看不见的字段记录了事务id,事务操作数据时严格递增遵循以下规则:

  • 插入数据:create_version为事务id,delete_version为空
  • 删除数据:delete_version为事务id,create_version为空
  • 修改数据:等价于插入+删除的组合,原数据复制一份,原有数据delete_version为事务id,create_version为空,复制的新数据create_version为事务id,delete_version为空

另一个事务根据隔离级别决定应该读取哪个版本数据:

  • 可重复读:create_version<=当前事务id的记录,数据有多版本则取事务id最大的
  • 读已提交:总是读取最新的数据,即:最近被提交的那个版本
  • 串行和读未提交不适用MVCC MVCC的帮助下,MYSQL的事务隔离级别与加锁方式: | 级别 | 读锁 | 写锁 | 范围锁 | 问题| | --- | --- | --- | --- | --- | | 可重复读 | N | Y | N | 读写事务有幻读(只读事务避免了幻读,容易理解:因为不会读取事务id大于当前事务id的数据行) | | 读已提交 | N | Y | N | 读写事务有幻读 + 不可重复读 |

Note:如果T1读取某个范围后,T2对此范围增删数据,T1又读取一次则不会出现幻读,因为T1是只读事务,但如果T1最后是更新这个范围的数据,则T2增删的数据就会受到影响,也就是读写事务有幻读(或者应该叫幻写)

参考:《凤凰架构》3.1.2 实现隔离性