读提交和可重复读
Read Commit
读提交,事务可以读取已经提交的数据。
存在的问题:事务前后读取不一致。
Repeatable Read
可重复读,事务前后读取数据是一致的。
存在的问题:无法处理插入或者删除的数据。
MySql如何实现读提交和可重复读的?
innodb的聚簇索引中有2个隐藏字段,叫做trx_id (transaction id)
,和roll_pointer
。
trx_id
描述一个事务编号,roll_pointer
指向当前数据行的上一个历史版本,对于insert
而言不存在历史版本
同时存在一个ReadView对象,里面有个队列描述当前正在处理的事务id。当有事务读取时,会先判断读取的行是否在这个队列中,如果存在就不会读取,会根据roll_pointer读取上一个版本的数据。
例:当事务id为100 的事务在修改id为1的行时,ReadView中队列会有[100]在里面,这时另一个事务读取id 为1 的数据行会首先发现trx_id 100的 小明2号 数据,随后会和ReadView的队列元素比较,发现存在,则随着roll_pointer去查询trx_id为60的数据行 小明1 ,发现60 不在队列中,于是读取值为小明1。
Read Commit的问题复现:
以上图为例,当事务trx_id 为 100 的事务修改 小明1 -> 小明2 时,此时未提交,于是另一个事务B在读取时会读取到 小明1,然后trx_id 为100的事务提交,这时刚才B又读了一遍,发现值变成 小明2。
Repeatable Read 复现 Read Commit
当事务trx_id 为 100 的事务修改 小明1 -> 小明2 时,此时未提交,于是另一个事务B在读取时会读取到 小明1,然后trx_id 为100的事务提交,这时刚才B又读了一遍,值还是 小明1。
导致差异的原理
Read Commit在查询时每次会生成新的ReadView,而Repeatable Read 则只会在第一次查询时生成ReadView,随后复用。
这导致Read Commit 每次查询时那个队列总是空的,就导致会读取到刚更新的数据。