事务的隔离级别

MySql学习笔记–事务的隔离级别



一、事务的特性

   原子性:事务中的操作要么全都执行要么全都不执行。
   一致性:数据的完整性。数据库中A有100,B有200。他俩无论怎么转账,都要保证A和B共有300。
   隔离性:数据库中多个事务对数据进行修改时,保证每个事务在使用数据时对其他事务是隔离的。(每个事务都有一个完整的数据空间)
   持久性:事务结束后,对数据的修改是永久的(系统故障也不会丢失)。

InnoDB引擎通过redolog(重做日志)-------------------------保证------------------------》持久性
        undolog(回滚日志)-------------------------保证------------------------》原子性
          MVCC     --------------------------保证------------------------》隔离性
        持久性+原子性+隔离性------------------------保证------------------------》一致性

二、并行事务引发的问题

   脏读----事务A读到了别的事务没提交的数据。
   不可重复读----事务A对记录X前后读取的数据不一致(两次读取之间有事务B对X修改了)
   幻读----事务A查询符合条件f的记录数量前后不一致(两次查询之间有事务B插入了符合f条件的记录)
与上面对应的MySql四种隔离级别来规避这些现象:
在这里插入图片描述
这里给出一个不同隔离级别能解决的问题:
在这里插入图片描述
反正锤子(读未提交)是一点用没有,要想解决脏读就得用刀(读提交),要想解决不可重复读就得用宝剑(可重复读),要想解决幻读就得掏出真理(串行化)。但是这些天赋一个比一个贵啊(性能开销大),一般不使用真理,MySql一般使用宝剑(可重复读)就能很大程度避免幻读,但不是完全。这个下一节笔记再说。。。。。

三、四种隔离级别具体的实现

1.读未提交(锤子)和串行化(真理)

   读未提交什么问题都无法解决,只是在读取最新的数据而已,执行命令了就去机械的读。
   串行化通过加读写锁的方式避免并行访问。

2.读已提交(刀)和可重复读(宝剑)

   这两个都是通过MVCC来完成。区别就在于创建MVCC中的快照ReadView的时机不同。读已提交是在执行每个sql语句前都要生成一个新的快照;而可重复读是在启动事务时生成一个快照,并在这个事务执行期间一直就用这一个。
注意:开始事务和启动事务不是一回事
在这里插入图片描述

3.MVCC以及ReadView

先说下MVCC:
  MVCC(多版本并发控制),解决多线程并发问题,基于undolog、版本链和ReadView实现。在InnoDB中一条记录的字段包括两个隐藏列
在这里插入图片描述
  trx_id:修改这条记录的事务id;
  roll_pointer:指向旧版本的记录,记录都存到undo日志中,通过这个指针找到之前版本的该行记录。
在这里插入图片描述
  这个就是一条记录的改动日志了。日志中记录了不同版本这条记录的样子以及该版本出自的事务。

我们再来介绍ReadView:
  ReadView的作用就是让事务知道应该读取的是哪个版本的记录。
在这里插入图片描述
  活跃的读写事务指的是还没提交的事务。举个例子来说明下这个字段的意思:
假设现在处在MySql的默认隔离级别(可重复读级别下)现在只有一个事务A,对记录X要进行访问。那么A先生成对X的ReadView,且A事务没结束之前一直用这个ReadView、X现在的记录字段:
在这里插入图片描述
m_ids:生成这个ReadView时,只有事务A是活跃的(没提交)。
min_trx_id:m_ids中最小值就是51.
max_trx_id:若待会还有别的事务要对X访问,那么他的id应为52.
creator_trx_id:生成该ReadView的事务是51.
以上就是字段的解读,

而具体ReadView如何判断哪个版本是对事务可见的需要对比字段
在这里插入图片描述
解释:trx_id:就表示创建该版本记录时的事务id
   trx_id == creator_trx_id :我读取我创建的记录;
   trx_id < min_trx_id :创建该版本记录的事务不在活跃事务中(已提交了),那在读已提交(刀)和可重复读(剑)下就能访问该版本的记录。
   trx_id > max_trx_id :创建该版本记录的事务是在未来的事务中(也即我来晚了。。。),所以不能访问该版本的记录。
   min_trx_id <= trx_id <= max_trx_id:
                  (1)如果trx_id在m_ids中,说明创建该版本记录的事务还活跃,没提交,不可访问。
                  (2)如果trx_id不在m_ids中,说明创建该版本记录的事务已提交,可访问。
接下来继续用这个例子说明可重复读的工作机制。

四、那么可重复读下是如何工作的呢

现在紧接着另一个事务B也启动了(上面的还是那个状态)。
m_ids:生成view2时,事务A和事务B都没提交;
max_rex_id:再有事务C的话他的id应为53;

在这里插入图片描述
ReadView判断是否可见的具体分析:
  事务B读取时,trx_id = 50。进行判断:trx_id < min_trx_id,则可访问。读到余额100万。
  事务A要修改,trx_id = 50,先进行判断:trx_id < min_trx_id,则可访问,修改成了200万,但还没提交。但会把这个记录串到undolog中:
在这里插入图片描述

  事务B再读时,trx_id 变成了51。进行判断:min_trx_id <= trx_id <= max_trx_id,且trx_id 还在 m_ids中,则说明还没提交,不能访问这个版本的记录,根据roll_pointer指针找旧版本的记录,读到余额还是100万。
  事务A提交了,由于是在可重复读的隔离级别下,事务B使用的视图始终是View2。
  事务B再次读取,由于视图没变,所以最后还是min_trx_id <= trx_id <= max_trx_id,且trx_id 还在 m_ids中,则说明还没提交,不能访问这个版本的记录,根据roll_pointer指针找旧版本的记录,读到余额还是100万。

五、那么读已提交下是如何工作的呢

读提交时,最大的区别就是,每次读取数据都创建新的视图。还是上边的例子:
m_ids:生成view2时,事务A和事务B都没提交;
max_rex_id:再有事务C的话他的id应为53;


在这里插入图片描述
事务B第二次读取时,创建的视图始终是View2这样子的,在A事务提交完之后,B再查询时,视图变成了View3。

在View3中,事务B读数据时,先是trx_id=51这条版本的记录,trx_id < min_trx_id,说明事务51即事务A已经提交了,所以该版本的记录可见,则成功读取到修改后的余额200万。

总结

以上就是今天总结的学习笔记,感谢两位up主优质的文章,本文中的大部分图都来自于这两篇文章中,感谢。。
https://xiaolincoding.com/mysql/transaction/mvcc.html;
https://blog.youkuaiyun.com/lans_g/article/details/124232192。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值