Mysql InnoDB多版本并发控制MVCC

参考书籍《mysql是怎样运行的》系列文章目录和关于我
一丶为什么需要事务隔离级别mysql是一个客户端/服务断软件 , 对于同一个服务器来说,可以有多个客户端进行连接,每一个客户端进行连接之后就形成一个会话,每一个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某一个事务的一部分,服务器可以同时处理多个事务 。
如果事务时一个接着一个进行 , 那么下一个事务是在上一个事务的一致性前提下进行的,就没用一致性的问题,但是事务是并发进行且可能访问到相同的数据这时候就会出现如下问题

Mysql InnoDB多版本并发控制MVCC

文章插图
可以看到AB最开始总和13元,最后AB总和18元,银行血亏五元,这显然违背了一致性——钱的总量不变 。这就是并发情况下两个事务的影响,所以需要事务隔离让事务隔离的进行,互不干涉 。
1.实现事务隔离的方式:串行执行【Mysql InnoDB多版本并发控制MVCC】最简单直接的方式,同一时间只能有一个事务运行,这样必然不会有上述不一致的情况,但是大大降低了吞吐率并增加了事务的等待时间
2.实现事务隔离的方式:可串行执行并发事务之所以出现不一致的情况,就是由于多个事务访问相同的数据,需要实现多个事务在访问相同数据的时候进行限制,比方说上图中事务2想访问A账户的值需要等待事务提交事务之后,这样可以让并发事务的执行如同串行执行的效果一样 。
二丶并发事务执行的问题:脏写 , 脏读,不可重复读,幻读1.脏写一个事务修改了另外一个未提交事务修改过的数据
  • 脏写导致一致性无法保证
    Mysql InnoDB多版本并发控制MVCC

    文章插图
    上图事务A和事务B都更新紫色数据,其中事务A首先更新为A,然后事务B过来更新为B,这时候事务A回滚后更新为Null,事务 B 明明正常写了一行数据,但是写完之后发现值变了,有点丢失更新的意思 。(比如A表示余额,这时候在将余额A判断是否足以支付,判断得到可以,事务B执行扣费写入A-5,商家收到5元,结果这时候回滚了,A变成Null,事务A中转钱的一方钱变为A,钱的总额变为A+5了)
  • 脏写导致原子性受到破坏
    假如上述的事务B还操作了另外的数据,比如插入一条数据C,并且更改为B写入C是在一个事务下面的,需要具备原子性 , 但是脏写让B的更改需要部分回滚为Null,这样插入C和更改B就不具备原子性(比如A表示余额,这时候在将余额A判断是否足以支付,判断得到可以 , 事务B执行扣费写入A-5 , 商家收到5元 , 结果这时候回滚了,A变成Null , 这时候部分回滚,商家的5元没用回滚,商家的库存也没用回滚,原子性被破坏)
2.脏读如果一个事务读取到另外一个事务未提交的数据,意味着发生了脏读
Mysql InnoDB多版本并发控制MVCC

文章插图
比如事务A先写数据A,然后事务B督导数据A后在内存中使用A进行一系列操作(比如A表示余额,这时候在将余额A判断是否足以支付,判断得到可以)但是事务A这时候回滚了,事务B再次读取数据发现为null,这就是脏读 。
脏读可能引发一致性的问题:比如事务操作时修改x和y的值,并且二者总是相等的,A修改x为1,还没来得及修改y也没用提交事务,这时候事务B读取x=1,y=0,二者不等,事务B读取到了数据库不一致的状态,读取到未提交事务的值
3.不可重复读假如一个事务修改了另外一个事务未提交的数据,意味发生了不可重复读
Mysql InnoDB多版本并发控制MVCC

文章插图
比如事务A第一次读取到值为A,接着事务B修改为B , 并且提交了事务B,然后事务A再次读取得到的数据是B,同一行数据多次读取值并不相同,这称作不可重复读 。它是指在同一个事务里面查询同一行数据,每次查到的数据都不一样 。和脏读区别在于脏读是由于别的事务回滚导致,而不可重复读读到的其实是已经提交的数据 。
事务A读到事务B提交后的数据似乎很合理 , 但是我们想象这样一种场景:你有一个流水表和用户余额 , 其中记录用户每天的流水,你在月初0点的时候核对流水和库存,但是流水很多,你的程序选择一个一个用户的进行核对,核对用户甲,甲没做任何消费,但是当你核对B的时候,你将B的流水load到内存中,但是B这时候(0点30分,这一笔数据新的一个余额)进行了扣除余额的操作,导致B余额和流水对不上了 。

推荐阅读