mysql ReadView 事务笔记
事务
Java工程师
hah,遇到过一样的问题,总结的是:
事务的实际开启并不仅仅是START TRANSACTION 语句,而是在执行了START TRANSACTION 语句后的第一个操作,推测其原因是,如果没有做任何操作,这个事务的开启就没有意义,只是空占了一断间隔时间阻塞其他的操作。
ReadView 是关键!
除MVCC 外,MySQL InnoDB 引擎设计了 ReadView(可读视图) 的概念。
ReadView 判断记录的可见性,ReadView 实际上是当前系统中所有活跃事务的列表,主要包含以下组成部分:
m_ids:在生成 ReadView 时当前系统中活跃的事务 ID 列表;
min_trx_id:在生成 ReadView 时当前系统中活跃的事务中最小的事务 ID,也就是 m_ids 中的最小值;
max_trx_id:在生成 ReadView 时系统中应该分配给下一个事务的 ID 值;
creator_trx_id:生成 ReadView 的事务对应的事务 ID,也就是当前事务 ID。 有了这个 ReadView 之后,在访问某条记录时,只需要按照下边的步骤判断该记录的某个版本是否可见:
如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本记录可以被当前事务访问。
如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本记录可以被当前事务访问。
如果被访问版本的 trx_id 属性值大于或等于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本记录不可以被当前事务访问。
如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本记录可以被访问。 如果某个版本的记录对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。
总结一下就是: 如果当前事务id的生成时间发生在 记录的更新之后,那么当前事务就可以看见这个记录,否则看不见!避免幻读问题
那 ReadView 又是何时生成的呢?
在 Read committed RC 隔离级别下,每个事务执行第一个 SELECT 语句时,会将当前系统中的所有的活跃事务拷贝到一个列表生成 ReadView,后续所有的 SELECT 都是复用这个 ReadView。
REPEATABLE READ RR 隔离级别下,只有第一次 SELECT 才会生成 ReadView,后续 SELECT 都会复用这个 ReadView,也就不存在新提交事务对这个 ReadView 的影响了。
所以 当我在 事务 1 新增select语句,会生成一个ReadView,这个ReadView 生成时间要早于 事务2的时间,所以事务1 的后续所有查询都不会看到事务2的记录,从而避免幻读问题发生。
总结
MySQL innodb 插入记录是并发的。
MySQL innodb 插入记录不存在幻读问题,MySQL 通过 mvcc+ ReadView解决幻读问题。