1 问题引入
看下图:
根据Mysql技术内幕:InnoDB引擎,不同事务隔离级别可以解决不同问题,分别是脏读,不可重复读(幻读),丢失更新,和上图不一致
而不可重复读其实就是幻读(上述说两者有细微差别),丢失更新是业务层面的丢失(对同一银行账户进行余额查询并修改,导致最后结果不一致,后续有相关例子)
依稀记得,在大学期间的数据库教材Sql Server中学过这个知识点(如今找不到这本书),
这本书将不可重复读和幻读区别开,就和上图一样,而此文以mysql的InnoDB为例,介绍不同之处
个人理解:后续会介绍,此处只是为了全文的完整性
上述的那张图的来源就是,当没有范围锁机制且使用行级锁(而InnoDB将范围锁加入到RR隔离级别中解决幻读问题)。
修改记录,对其加x锁就可避免(不可重复读);而插入则不能,因为插入的不是同一行记录,插入不会锁冲突(幻读)。
2 不同级别的实现
1)先验知识
1)MVCC:读不加锁,读取的被锁住数据的历史快照版本数据,RC,RR级别读取的版本不一致,RC读取最新历史版本,RR读取事务开始前的那个版本。
2)Next-key Lock:Record Lock只锁住要操作的那个记录,而Next-key则锁住记录及其周边的记录
-----更具体的相关知识自行百度即可
RC(Read-Commited),RR级别的事务隔离级别的默认读操作使用的是MVCC,即读不加锁,读取被锁住的数据的历史快照版本,且RR级别的MVCC才能解决后续的不可重复读(幻读)。
为什么RR的MVCC能解决幻读,因为不管其他事务如何修改,RR的读只读取自己事务开始前的那个版本的数据,故不会产生幻读
故可知后续需要分需不需要对读操作加锁来讨论,当需要及时读取最新的数据时,需要对读数据进行加锁,这时候就不能使用MVCC来解决幻读了,需要Next-Key机制来解决
举例:事务1读取 >2 的数只有4,此时事务2插入一条数据5,
如果是一般的Record Lock,只锁住记录4,记录5照样可以插入;
而范围锁(NextKey)则锁住了(2,+∞)这个范围,记录5无法插入
2)实现
1)Read UnCommited:脏读都解决不了,事务之间没有隔离性,不加任何锁
2)Read Commited:
i)不加锁:MVCC,读最新的历史数据,可以判断读取的数据是否已经提交
ii)加锁:读加S锁(读完就放),加了写锁,就可以保证不出现脏读,因为只有事务提交了(事务不会回滚了),锁才会释放(读加锁可以读取到写修改的数据)
3)Read Repeated:解决幻读问题
i)不加锁:MVCC
ii)加锁:读加s锁并保持到事务结束,写加x锁(一样);并使用Next-key Lock机制解决幻读
(最开始的那张图的来源就是,没有范围锁机制,修改记录,对其加x锁就可避免;而插入则不能,因为插入的不是同一行记录,所以不会冲突,依旧可插入)
4)Serializable:解决更新丢失问题
必须对事务中的读操作加X锁,使得其他事务阻塞,事务之间无法并发
下面举例说明丢失更新问题:
//一般业务都需要先读取余额,然后查看是否可以转账,在进行update操作
事务1:读取数据---------------------数据更新
事务2:----------读取数据---------------------数据更新
这在数据库层面是没问题的,因为数据更新会加X锁,数据会是事务2修改的
但在业务层面,事务1转账1000,事务2转账2000,最终的结果是只转账了2000
对读操作也加X锁,事务1读取后,事务2就阻塞,当事务1转账1000完成后,事务2才进行转账2000
3 总结
InnoDB引擎: