数据库事务的概念理论网上已经铺天盖地了,此处只是自己学习做一些笔记,以及自己的一些理解。其中有一些譬如不可重复读和幻读的区别、RR为什么没有彻底解决幻读等自己比较模糊的概念进行了记录以及实验。
一、事务的四大特征(ACID)
-
解释:A:原子性; C一致性; I:隔离性; D:持久性;
-
C是结果,为了实现C,需要AID进行保证;
-
原子性和一致性的区别:
原子性只是针对本次事务要么都成功,要么都失败;一致性针对的是业务上数据和逻辑的一致性。譬如在并发情况下,每个单独事务有了原子性,但是业务上的数据会产生很多不一致情况,此时就需要隔离性进行控制。
参考链接:https://blog.csdn.net/y1391625461/article/details/125171996
二、 mysql的策略模式
- steal:允许在事务commit之前把内存中的数据写入磁盘。此时需要undo,因为系统在commit之前崩溃时,已经有数据写入到磁盘了,要恢复到崩溃前的状态,必须undo这些写入操作,否则磁盘上的数据就是不正确的。
- no steal:不允许在事务commit之前把内存中的数据写入磁盘。不需要undo。
- force:内存中的数据最晚在commit的时候写入磁盘。不需要redo。
- no force:内存中的数据可以一直保留,在commit之后过一段时间再写入磁盘。此时需要redo,因为数据在系统崩溃的时候可能还没写入到磁盘,如果不redo,磁盘上的数据就是不完整的。
- 总结:
a、steal/no-steal主要决定了磁盘上是否会包含uncommitted的数据。force/no-force主要决定了磁盘上是否会不包含已经committed的数据。
b、现在DBMS常用的是steal/no-force策略,因此一般都需要记录redo log和undo log。这样可以获得较快的运行时性能,代价就是在数据库恢复(recovery)的时候需要做很多的事情,增大了系统重启的时间。
三、undo和redo的概念及作用
- 流程(在使用steal/no force策略下)
a、开启一个事务,数据会先进入log buffer,然后顺序写 到 redo log,再从redo log写到磁盘。
b、由于往磁盘写是离散写,所以为了提高效率,可能会进行边写日志边写数据。
c、由于日志还没写完就写了数据,所以如果出现错误就需要找以前是数据进行恢复,这时就有了undo log的出现。
d、先把事务之前的数据存在undo log之中(存储数据到undo有是作用之一),这时就可以边写redo log,边写数据了。
e、这样在事务提交之前(即redo log没有写最后的 commit record)产生了崩溃,因为是边写日志边写数据(steal策略),数据库可能已经存在部分本次事务写入的数据,这时就会从undo log中拿到之前的数据进行回滚。在事务提交之后(即 redo log写了最后的 commit record)产生了崩溃,因为事务已经成功提交了,但是数据没有持久化完毕(no-force策略,commit之后数据保存在redo log,不强制立马将数据写磁盘,由线程慢慢刷到磁盘,所以磁盘中的数据并不完整),所以会从redo log之中拿到数据进行往数据库恢复。 - undo的作用
a、和mysql缓存区的四个策略有关,使用steal模式就需要undo log来恢复数据
b、为了mvcc(多版本控制),在RC和RR的事务中需要建立这种快照。
四、隔离级别:
- 四个隔离级别
读未提交:存在脏读、不可重复读、幻读问题
读已提交:存在不可重复读、幻读问题
可重复读:存在幻读问题
串行:最高隔离级别,不存在脏读、不可重复读、幻读问题 - 脏读、不可重复读、幻读
a、脏读的含义是读到了他人事务中增删改的数据,这样的问题最大;
b、不可重复读是同一个事务中执行两条相同查询,查询结果数据不一致的情况;
c、幻读是同一个事务中执行两条相同查询,读取到的行数不一致的情况; - 不可重复读和幻读的区别
这两者有些相似,但不可重复读重点在于update和delete,而幻读的重点在于insert。(如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。原文链接:https://blog.csdn.net/xiaolong7713/article/details/105622354)
参考链接:
这里说明了mysql的RR没有彻底解决幻读问题
这里有mvcc的实现概念
这里说明了不可重复读和幻读的区别 - 脏读和幻读
a、脏读的条件:是否提交了;事务中读取数据,是否读取的字段不一致
2、幻读主要针对的是条数:范围查询或者count的时候,所以RR中加入了间隙锁解决幻读这个问题 - 串行的两阶段加锁(可串行化)
两阶段加锁解决了并行的问题:所有的数据加锁阶段不能释放锁, 所有的数据释放锁阶段不能加锁(这是对于所有在事务内的数据,而不是针对单条数据) - Mvcc解决的是读和写不能同时存在的问题
快照读和快照写 都是undo
当前读和当前写 都是表数据中进行
五、实验
- (读已提交)Read Committed VS (可重复读)REPEATABLE READ
测试:读已提交和可重复读,事务未提交的修改其他事务是否能看到;其他事务提交后的数据,在另一个未提交的事务中能否看到。
(1)首先测试读已提交
所以读已提交隔离级别中,是读取其他事务提交后的数据,但是事务1读取该表数据两次的结果是不一致的,所以读已提交不能防止可重复读。
(2)测试可重复读
所以可重复读隔离级别中,事务1未提交前,无论其他事务如何变更均不影响查询结果,那么此处mysql就为事务建立了“快照”,复制了该表的多个版本。
2.(读已提交)Read Committed VS (可重复读)REPEATABLE READ
测试:读已提交和可重复读,是否含有间隙锁,一个事务做范围更新,另一个事务能否在范围内插入数据
(1)读已提交
所以读已提交中没有间隙锁,只在数据上加锁了
(2)可重复读
所以可重复读是有间隙锁,是在第一个事务操作的范围全部加锁
- RR是否解决了幻读问题测试
所以RR解决了可重复读,但是没有彻底解决幻读问题。