MySQL mvcc

242 阅读4分钟

目标:MySQL mvcc

分析:

详解:

第一部分 隔离级别

1、隔离级别
脏读不可重复读幻读加锁
读未提交
读已提交
不可重复读
序列化
  • 脏读:指读取到其他事务正在处理的未提交数据。
  • 不可重复读:指并发更新时,另一个事务前后查询相同数据时的数据不符合预期。
  • 幻读:指并发新增、删除这种会产生数量变化的操作时,另一个事务前后查询相同数据时的不符合预期。
2、脏读
事务A事务B
beginbegin
select name from s where id=1(张三)
update s set name='李四' where id=1
select name from s where id=1(李四)
rollback
commit

事务A读取到了事务B未提交(新增、更新、删除)的数据。

3、不可重复读
事务A事务B
beginbegin
select name from s where id=1(张三)
update s set name='李四' where id=1
commit
select name from s where id=1(李四)
commit

事务A在同一事务中的两次读取数据不一致。事务B已提交(更新

4、幻读
事务A事务B
beginbegin
select * from s(all)
delete from s(清空)
insert into s set values(1,'张三')
commit
select * from s(all)(张三)
commit

事务A最后一次读取到了数据。事务B已提交(新增、删除

第二部分 mvcc

1、ReadView是什么?

ReadView是快照读SQL执行时MVCC提取数据的依据,快照读就是普通的Select查询语句。使用mvcc。
当前读指代执行下列语句时进行数据读取的方式,insert、update、delete、select...for update、select...lock in share mode。使用间隙锁。

ReadView是一个数据结构,包含4个字段

  • m_ids: 当前活跃的事务编号集合
  • min_trx_id: 最小活跃事务编号
  • max_trx_id: 预分配事务编号,当前最大事务编号+1
  • creator_trx_id: ReadView创建者的事务编号

undo log版本链数据访问规则: 当前事务id : trx_id

  • 1、判断 trx_id=creator_trx_id 吗?成立说明数据就是自己这个事务更改的,可以访问。
  • 2、判断 trx_id<min_trx_id 吗?成立说明数据已经提交了,可以访问。
  • 3、判断 trx_id>max_trx_id 吗?成立说明该事务是在ReadView生成以后才开启,不允许访问。
  • 4、判断 min_trx_id<=trx_id<=max_trx_id 吗?成立在m_ids数据中对比,不存在的则代表数据已提交,可以访问。
2、读已提交

读已提交:在每一次执行快照读时生成ReadView。

事务A trx_id=1事务B trx_id=2事务C trx_id=3事务D trx_id=4
beginbeginbeginbegin
update s set name="李四" where id=1
commitupdate s set name="王五" where id=1
select * from s where id=1(李四) (ReadView 1)
commit
update s set name="赵六" where id=1
select * from s where id=1(王五) (ReadView 2)
commit
commit
ReadView 1
- m_ids={2,3,4}
- min_trx_id=2
- max_trx_id=5
- creator_trx_id=4
版本链数据访问顺序 : \
trx_id=3  不符合 \
trx_id=2  不符合
trx_id=1  符合   (读取到数据 李四)
ReadView 2
- m_ids={3,4}
- min_trx_id=3
- max_trx_id=5
- creator_trx_id=4
版本链数据访问顺序 : \
trx_id=3  不符合 \
trx_id=2  符合  (读取到数据 王五)
3、不可重复读
3-1 不可重复读:仅在第一次执行快照读时生成ReadView,后续快照读复用。
事务A trx_id=1事务B trx_id=2事务C trx_id=3事务D trx_id=4
beginbeginbeginbegin
update s set name="李四" where id=1
commitupdate s set name="王五" where id=1
select * from s where id=1(李四) (ReadView 1)
commit
update s set name="赵六" where id=1
select * from s where id=1(王五) (ReadView 1)
commit
commit
ReadView 1
- m_ids={2,3,4}
- min_trx_id=2
- max_trx_id=5
- creator_trx_id=4
版本链数据访问顺序 : \
trx_id=3  不符合 \
trx_id=2  不符合
trx_id=1  符合   (读取到数据 李四)
ReadView 1
- m_ids={2,3,4}
- min_trx_id=2
- max_trx_id=5
- creator_trx_id=4
版本链数据访问顺序 : \
trx_id=3  不符合 \
trx_id=2  不符合
trx_id=1  符合   (读取到数据 李四)
3-2 连续多次快照读,ReadView会产生复用,没有幻读问题。特例:当两次快照读之间存在当前读ReadView会更新,导致产生幻读。

当前读,使用的是最新的记录。如果修改了其他事务变更的记录,下次快照读读的数据就包含被修改的记录,只包含被修改的记录,如果都未被修改,下次快照读读的就是上次的ReadView。

事务A trx_id=1事务B trx_id=2
beginbegin
select * from s where name="张三" ReadView 1
insert into s values (2,"张三",20)
insert into s values (3,"张三",20)
commit
update s set age=25 where id=3 ReadView 2
select * from s where name="张三" (ReadView 2)
commit
ReadView 1
- m_ids={1}
- min_trx_id=1
- max_trx_id=3
- creator_trx_id=2
版本链数据访问顺序 : \
trx_id=1  不符合 \
trx_id=0  符合   (读取到数据 1条)
ReadView 2
=
ReadView 1 
+ 
id=3的这条数据

不包括id=2的这条数据,因为当前读(update s set age=25 where id=3)并未修改id=2