幻读(间隙锁)1.因为很多人(当然也包括我自己),很容易把不可重复读和幻读混淆,两者确实非常相似。但是不可重复读的重点是更新,删除。幻读的重点是insert。(可以参考MySQL官方文档中PhantomRows的介绍)2.虽然网上有很多资料提到幻读,但是表述可能不准确,比如这个对幻读的解释'同样的条件,***网上到处都可以看到,但其实并不准确,因为delete其实不属于幻读的范畴(MySQL官方文档中对PhantomRows的介绍根本不涉及delete)。3.如果手动加锁来演示,就会看清它们的本质:如果是insert,则阻塞操作,可以看到具体加锁是X锁+GAP锁:如果是delete或update,则阻塞,但是可以看到具体只加了X锁:可以看到,加了GAP锁是为了防止只有在其他事务插入的时候才会出现幻读,所以必须区分delete/update和insert。但是,在后面学习mvcc的时候,就会知道加锁的低效性,所以有两种解决方案:使用隔离级别SERIALIZABLE,但是这种隔离级别在实践中很少使用;其实REPEATABLEREAD是可以防止Phantomreading的,在《高性能MySQL》中有提到,REPEATABLEREAD理论是不能防止Phantomreading的,但是由于这个隔离级别也使用了MVCC,所以可以实现非锁一致性读,所以,只要你确实确定你理解幻读的含义,你无法在REPEATABLEREAD隔离级别下模拟幻读的效果;至于网上所谓的幻读现象,我觉得是误导。一开始我还以为这是REPEATABLE-READ隔离级别。解决了幻读的硬证据,后来发现不对。幻读是指两次读操作发现记录增加导致的不一致,下面是多次插入。虽然也是问题,但已经不是幻读问题了:打开client1查看隔离级别和初始数据mysql>SELECT@@SESSION.tx_isolation;+--------------------------+|@@SESSION.tx_isolation|+-----------------------+|可重复阅读|+-------------------+1rowinset(0.00sec)mysql>select*fromtest_transaction;+----+-----------+-----+--------+-------------------+|id|user_name|age|gender|desctiption|+----+------------+------+--------+------------------+|1|金刚狼|127|1|我有一双铁爪||2|钢铁侠|120|1|我有盔甲||3|绿巨人|0|2|我有身体|+----+------------+-----+--------+--------------------+3rowsinset(0.00sec)mysql>打开客户端2查看隔离级别和初始数据mysql>SELECT@@SESSION.tx_isolation;+------------------------+|@@SESSION.tx_isolation|+----------------------+|REPEATABLE-READ|+------------------------+1rowinset(0.00sec)mysql>select*fromtest_transaction;+----+------------+-----+--------+--------------------+|id|user_name|age|gender|desctiption|+----+------------+-----+--------+--------------------+|1|金刚狼|127|1|我有一对铁爪||2|钢铁侠|120|1|我有铁盔甲||3|绿巨人|0|2|我有血肉|+----+------------+-----+--------+----------------+3rowsinset(0.00sec)mysql>在客户端2开启事务,然后查询数据mysql>begin;QueryOK,0rowsaffected(0.00sec)mysql>select*fromtest_transaction;+----+------------+-----+--------+--------------------+|id|user_name|age|gender|desctiption|+----+------------+-----+--------+--------------------+|1|金刚狼|127|1|我有一对铁爪||2|钢铁侠|120|1|我有盔甲||3|绿巨人|0|2|我有身体|+----+------------+-----+--------+--------------------+3rowsinset(0.00sec)mysql>Insertanewdatawithid4inclient1(directlyandautomaticallysubmit)mysql>insertintotest_transaction(`id`,`user_name`,`age`,`gender`,`desctiption`)values(4,'Deadpool',18,0,'Abadboy');QueryOK,1rowaffected(0.00sec)mysql>select*fromtest_transaction;+----+------------+-----+--------+------------------+|id|user_name|age|gender|desctiption|+----+------------+-----+--------+--------------------+|1|金刚狼|127|1|我有一对铁爪||2|钢铁侠|120|1|我有铁甲||3|绿巨人|0|2|我有肉||4|死侍|18|0|阿巴德男孩|+----+------------+-----+--------+----------------+4rowsinset(0.00sec)mysql>客户端2事务中再次查询数据,发现数据没有变化(说明可以重复读,克服幻读),但是在client2事务中插入一条id为4的新数据,发现数据已经存在。注意虽然出现了问题,但不属于幻读mysql>begin;QueryOK,0rowsaffected(0.00sec)mysql>select*fromtest_transaction;+----+-----------+-----+--------+------------------+|id|user_name|age|gender|desctiption|+----+------------+-----+--------+----------------+|1|金刚狼|127|1|我有一对铁爪||2|钢铁侠|120|1|我有铁甲||3|绿巨人|0|2|我有血肉|+----+------------+-----+--------+--------------------+3rowsinset(0.00sec)mysql>select*fromtest_transaction;+----+------------+-----+--------+----------------+|id|用户名|年龄|性别|描述|+----+------------+-----+--------+------------------+|1|金刚狼|127|1|我有一对铁爪||2|钢铁侠|120|1|我有铁甲||3|绿巨人|0|2|我有肉|+----+------------+-----+--------+--------------------+3rowsinset(0.00sec)mysql>insertintotest_transaction(`id`,`user_name`,`age`,`gender`,`desctiption`)values(4,'Deadpool',18,0,'阿巴男孩');1062-Duplicateentry'4'forkey'PRIMARY'mysql>那么问题是什么?我个人认为,如果你的表中真的有两条相同的记录,想一想,是不是应该先满足表的最小规范(第二范式)?
