select * from table_user where id between 1 and 10
,这条sql本应查出 1~9 的数据,id=10 此时不存在,之后其他事务再插入了一条 id=10 的记录。然后当前事务再次查询则会查出 10 条记录。这就是幻读MVCC 全称Multi-Version Concurrency Control,其好处是读不加锁,读写不冲突,并发性能好
对于使用READ COMMITTED
和REPEATABLE READ
隔离级别的事务来说,都必须保证读到已提交事务修改过的记录,也就是说假如另一个事务修改了记录但尚未提交,是不能读取最新版本的记录的,其核心问题:需要判断 MVCC 版本链中的哪个版本是当前事务可见的。innodb 的解决方案 readView,readView 包含4个比较重要的属性
m_ids
:在生成ReadView
时,当前系统中活跃的读写事务 id 列表min_trx_id
:表示在生成ReadView
时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids
中的最小值max_trx_id
:表示生成ReadView
时系统中应该分配给下一个事务的 id 值creator_trx_id
:对应生成该ReadView
事务的idtrx_id
属性值与ReadView
中的creator_trx_id
值相同,表示当前事务在访问它自己修改过的记录,该版本可以被当前事务访问。trx_id
属性值小于ReadView
中的min_trx_id
值,表明生成该版本的事务在当前事务生成ReadView
前已经提交,所以该版本可以被当前事务访问。trx_id
属性值在ReadView
的min_trx_id
和max_trx_id
之间,那就需要判断一下trx_id
属性值是不是在m_ids
列表中,如果在,说明创建ReadView
时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView
时生成该版本的事务已经被提交,该版本可以被访问trx_id
属性值大于或等于ReadView
中的max_trx_id
值,表明生成该版本的事务在当前事务生成ReadView
后才开启,该版本不可被当前事务访问。反之可见READ UNCOMMITTED
:直接读取记录的最新版本就好READ COMMITTED
:每次读取数据前都生成一个ReadViewREPEATABLE READ
:在第一次读取数据时生成一个ReadViewSERIALIZABLE
隔离级别的事务来说,InnoDB
规定使用加锁的方式来访问记录