一、MySQL的事务隔离级别
1、定义
为了解决多个并行事务竞争导致的数据安全问题的一个规范
可能会出现的现象:脏读、不可重复读、幻读
2、脏读
问题:两个事务T1/T2同时执行,T1可能会读到T2未提交的数据,但是T2可能会回滚,导致T1读到了错误的数据,产生脏读的现象
解决方案:用读已提交的隔离级别,但还是存在不可重复读和幻读
3、不可重复读
问题:两个事务T1/T2同时执行,T1在不同时刻(比如T2提交前和提交后)读到的数据不一致,导致不可重复读的问题
解决方案:用可重复读的隔离级别,但还是存在幻读
4、幻读
问题:两个事务T1/T2同时执行,T1执行返回查询/范围修改时,T2插入了T1查询范围内的数据并且提交了,T1再次查询/修改时,发现数据多出来一条/没修改成功,看起来像出现了幻觉,即为幻读
解决方案:用串行化的隔离级别,但是性能是最低的
5、总结
- 读未提交:可能会出现脏读、不可重复读、幻读
- 读已提交:可能会出现不可重复读、幻读
- 可重复读:可能会出现幻读(InnoDB不可能),是InnoDB引擎默认的隔离级别,保证了事务ACID特性中的隔离性特征
- 串行化:多个并行事务串行化执行,不会出现安全性问题,但是性能是最低的
二、MySQL事务特性(ACID)
参考链接:https://juejin.cn/post/7153626309338333220
- A(Atomicity)原子性:指事务是一个不可分割的工作单位,也就是事务中的操作要么都发生,要么都不发送
- C(Consistency)一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态
- I(Isolation)隔离性:事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务的操作及使用的数据对并发的其他事务是隔离的(并发执行的各个事务之间不能相互干扰)
- D(Durability)持久性:指一个事务一旦提交,它对数据库的改变就是永久的,不会因为其他操作和数据库故障而有影响
比如A和B各有1000元,A给B转账500元,对应的语句应该是:
update table set A.amount = 500 where name = ‘A’;
update table set B.amount = 1500 where name = ‘B’;
- A:要么两个sql都执行,要么都不执行
- C:A和B加起来有2000元,A给B转账500元后,加起来还是2000元,保持一致
- I:A给B转账时,B进行了消费操作,这时候需要等转账事务执行成功或失败后,消费事务才能执行,保证两事务互不干扰
- D:A给B转账成功后,不能再回滚到事务开始前,除非再执行一遍B转账给A
实现原理:
- A:undo log日志实现,用于撤销回滚,存储了数据库更新之前的数据
- C:redo log日志实现,用于灾难恢复,存储了修改但未提交的数据,当发生断电等异常灾难时,会通过redo log日志做一个提交恢复
- I:MVCC实现,主要由索引的隐藏列和undo log日志实现,其中索引的隐藏列包含了该行数据的版本号、删除时间、指向undo log的指针等。当读取数据时,mysql根据隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现MVCC
- D:一致性是事务的最终目标(由前面的A、C、I实现)
三、Q&A
1、不可重复读和幻读的区别是什么?
不可重复读和幻读都是MySQL事务中的数据安全问题,他们的区别在于:
- 结果:不可重复读和幻读的结果都是数据不一致
- 影响对象:不可重复读一般是单一记录查询,幻读一般是范围查询
- 操作类型:不可重复读一般是update操作,幻读一般是insert、delete操作
- 锁:执行update、delete时可以用记录锁(Record Lock)保证不出现不可重复读,但因为只能锁住已存在的数据,所以在执行insert时需要额外依赖间隙锁(Gap Lock),也就是间隙锁(Next-Key Lock=Record Lock + Gap Lock)才能保证不出现幻读