什么是幻读
一个事务查询2次,得到的记录条数不同。
MVCC 多版本并发控制
undo log
undo log有两个作用:提供回滚和多个行版本控制(MVCC)。
undo log主要存储的也是逻辑日志,比如我们要insert一条数据了,那undo log会记录的一条对应的delete日志。我们要update一条记录时,它会记录一条对应相反的update记录。
insert undo log – 记录insert
update undo log – 记录update和delete,undo 是逻辑记录,记录了每一行修改的值(前后项)。
实现原理
而MVVC则引入了另外一种并发控制,它让读写操作互不阻塞,每一个写操作都会创建一个新版本的数据,读操作会从有限多个版本的数据中挑选一个最合适的结果直接返回,由此解决了事务的竞争条件。
多版本控制的核心是数据快照,而 InnoDB 则是通过 undo log 来存储数据快照。InnoDB 通过 undo log 保存了已更改行的旧版本的信息的快照。
InnoDB 的内部实现中为每一行数据增加了三个隐藏列用于实现 MVCC 。
DB_ROW_ID: 行标识(隐藏单调自增id)
DB_TRX_ID: 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除)
DB_ROLL_PTR:写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息)
根据事物标识符及撤销记录决定读取的快照版本。
如何解决幻读
Mysql的默认隔离级别是Repeatable read(可重复读),这种隔离级别下会产生幻读问题,Mysql通过锁机制及多版本控制解决了幻读现象的发生,主要手段如下:
快照读
事务每次取数据的时候都会取创建版本小于当前事务版本的数据,以及过期版本大于当前版本的快照数据。普通的 select 就是快照读。
当执行select操作是innodb默认会执行快照读,会记录下这次select后的结果,之后select 的时候就会返回这次快照的数据,即使其他事务提交了不会影响当前select的数据,这就实现了可重复读了。
当前读
在 InnoDB 中,默认为 Repeatable 级别,InnoDB 中使用一种被称为 next-key locking 的策略来避免幻读(phantom)现象的产生。
主要采用next-key锁,即行锁和gap锁来控制。
如select * from user where id =1 for update;
如果有id为1的记录则会被加排它(X)锁,如果不存在,则会加上next-key锁,此时插入记录是会排出异常的,以此解决幻读问题。