如何真正来区分一下 不可重复读和幻读

发布时间:2025-05-21 20:51:32 作者:益华网络 来源:undefined 浏览量(0) 点赞(0)
摘要:幻读 (间隙锁) 1.由于很多人(当然也包括本人), 容易搞混 不可重复读 和 幻读 , 这两者确实非常相似。 但 不可重复读 的重点是针对 update, delete。 而 幻读 的重点针对的是 insert。(可以参考MySQL官方文档对 Phantom Rows 的介绍) 2.虽然网上有不少资料提到幻读

幻读 (间隙锁)

1.由于很多人(当然也包括本人), 容易搞混 不可重复读 和 幻读 , 这两者确实非常相似。

但 不可重复读 的重点是针对 update, delete。 而 幻读 的重点针对的是 insert。(可以参考MySQL官方文档对 Phantom Rows 的介绍)

2.虽然网上有不少资料提到幻读, 但是可能表达的都不太准确, 比如这样一段对幻读的解释 同样的条件, ***次和第二次读出来的记录不一样 在网络上随处可见, 但其实并不准确, 因为 delete 其实并不是幻读的范畴(MySQL官方文档对 Phantom Rows 的介绍)也一点都没涉及到delete)。

3.如果手动加锁来演示, 你便会看清他们的本质:

如果 insert, 则操作被阻塞, 并且可以看到具体加的是X锁+GAP锁:

如果 delete或者update, 则操作被阻塞, 但是可以看到具体加的只有X锁:

可以看到, 其他事务只有在 insert 的时候, 才会加GAP锁来防止幻读, 所以delete/update 和 insert 是要区分开的.

不过, 后面学到mvcc的时候, 你会知道加锁的低效性, 所以还有两种解决方案:

使用隔离性的***隔离级别SERIALIZABLE, 但该隔离级别在实际中很少使用;

其实 REPEATABLE READ 就可以防止幻读, 《高性能MySQL》中也说了, REPEATABLE READ 理论是是不能防止幻读的, 但是由于该隔离级别还使用了MVCC, 可以做到非锁定一致性读取, 所以, 只要你真的确定你明白幻读的意思, 你在 REPEATABLE READ隔离级别下是模拟不出幻读效果的;

至于网络上如下所谓的幻读现象, 本人觉得是误导, 刚开始本人也认为这就是REPEATABLE-READ隔离级别无法解决幻读的铁证, 后来发现错了, 幻读是指两次读操作发现记录增多导致的不一致, 而如下是多次insert, 虽然也是个问题, 但已经不是 幻读 问题了:

打开客户端1查看隔离级别及初始数据

mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ        | +------------------------+ 1 row in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>   打开客户端2查看隔离级别及初始数据 mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ        | +------------------------+ 1 row in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>

在客户端2中开启事务, 然后查询数据

mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>

在客户端1中插入一条id为4的新数据 (直接自动提交)

mysql> insert into test_transaction (`id`,`user_name`,`age`,`gender`,`desctiption`) values (4, 死侍, 18, 0, A bad boy); Query OK, 1 row affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | |  4 | 死侍    |  18 |      0 | A bad boy          | +----+-----------+-----+--------+--------------------+ 4 rows in set (0.00 sec) mysql>

在客户端2事务中再次查询数据, 发现数据没有变化(表示可以重复读, 并且克服了幻读), 但是在客户端2事务中插入一条id为4的新数据, 发现提示数据已经存在, 注意, 虽然爆出问题来了, 但不是 幻读 范畴

mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption        | +----+-----------+-----+--------+--------------------+ |  1 | 金刚狼 | 127 |      1 | 我有一双铁爪 | |  2 | 钢铁侠 | 120 |      1 | 我有一身铁甲 | |  3 | 绿巨人 |   0 |      2 | 我有一身肉    | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql> insert into test_transaction (`id`,`user_name`,`age`,`gender`,`desctiption`) values (4, 死侍, 18, 0, A bad boy); 1062 - Duplicate entry 4 for key PRIMARY mysql>

那么这是什么问题呢?

个人认为, 如果你的表中真的会出现两条完全相同的记录, 考虑一下, 最起码的表规范(第二范式)是否先满足一下?

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!