首先上mysql 官方文档对于Phantom的解释
15.7.4 Phantom Rows
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
翻译过来就是同一事务内相同select语句返回不同的纪录集.
对于InnoDb引擎,隔离级别默认为RepeatbleRead,在这种情况下,因为mvcc(多版本并发控制)机制,在同一事务内,我们执行两次select语句,是不会出现不同的纪录集的,那么我们就可以得出InnoDb在RR级别上解决了幻读问题的结论吗?并没有
幻读的两种解释
- 对于select返回的结果无法做正确的操作
相关图片 对于RR隔离级别来说,多次select查询的结果集是一样的,但从另一方面来说,其查询的结果集是不新鲜的,其他事务可能已经修改了纪录集,但在select结果中没有得到反应,在事务接下来的操作中(update, delete, insert)可能会出现异常(冲突), - 两次select返回不同的纪录集
例如在InnoDb readCommited隔离级别来说,同一事务相同select语句是有可能返回不同的结果集的,比较符合上面官方文档的解释
如何避免幻读
在RR隔离级别下,推荐使用select for update 或 select lock in share mode, for update 对检索到的行加排他锁, lock in share mode 对检索到的行加共享锁. 在update较为频繁的情况下,尽量使用for update, 因为lock in share mode导致死锁的几率较大,例如两个事务(事务1, 事务2), 事务1获得s锁,事务2也获得s锁(s锁之间不冲突), 这时事务1执行update语句,会导致事务1 blocking,因为x锁需要等待事务2的s锁释放,紧接着事务2也执行update,两个事务都在等待对方释放s锁,即发生了死锁。而死锁所带来的性能影响是很大的。