InnoDB默认的隔离级别是RR(可重复读),可以解决脏读和不可重复读只解决了快照读情况下的幻读问题,当前读情况下解决幻读问题得靠next-key锁。

mysql如何实现避免幻读:

  • 在快照读读情况下,mysql通过mvcc来避免幻读。
  • 在当前读读情况下,mysql通过next-key来避免幻读

- 快照读, 读取专门的快照 (对于RC,快照(ReadView)会在每个语句中创建。对于RR,快照是在事务启动时创建的)
```
简单的select操作即可(不需要加锁,如: select ... lock in share mode, select ... for update)
```
针对的也是select操作

- 当前读, 读取最新版本的记录, 没有快照。 在InnoDB中,当前读取根本不会创建任何快照。
```
select ... lock in share mode

select ... for update

insert

update

delete

针对如下操作, 会让如下操作阻塞:    
```
insert
update
delete
```
- 在RR(可重复读)级别下, 快照读是通过MVVC(多版本控制)和undo log来实现的,

当前读是通过手动加record lock(记录锁)和gap lock(间隙锁)来实现的。所以从上面的显示来看,如果需要实时显示数据,还是需要通过加锁来实现。这个时候会使用next-key技术来实现。所以说

事务的隔离级别:

快照读的情况下解决幻读问题:

脏读:事务A读取了事务B提交的数据,但是B事务由于某种原因导致事务回滚,但是A读取的仍然是事务B回滚之前的数据

不可重复读:事务A读取了一条数据,这时事务B将该条数据修改,事务A再次读取该条数据时,和最开始读取的数据不一致

幻读:事务A读取了一批数据,例如select * from user where age =10;读取出了5调数据。这时事务B又向user表插入了一条数据

insert into user(age)values(10)。事务A再次查询时,会发现多了一条数据,这就是幻读。

什么是MVCC?
多版本并发控制。InnoDB为每行记录添加了一个版本号(系统版本号),每当修改数据时,版本号加一。
在读取事务开始时,系统会给事务一个当前版本号,事务会读取版本号<=当前版本号的数据,这时就算另一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,因此读取事务读的数据还是不会变。
例如:

此时books表中有5条数据,版本号为1
事务A,系统版本号2:select * from books;因为1<=2所以此时会读取5条数据。
事务B,系统版本号3:insert into books ...,插入一条数据,新插入的数据版本号为3,而其他的数据的版本号仍然是2,插入完成之后commit,事务结束。
事务A,系统版本号2:再次select * from books;只能读取<=2的数据,事务B新插入的那条数据版本号为3,因此读不出来,解决了幻读的问题。

当前读 (current read)解决幻读问题:

1.打开客户端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.打开客户端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> 

3.在客户端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> 

4.在客户端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> 

5.在客户端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> //并且, 此时`update/delete`也是可以操作这条在事务中看不到的记录的!

6.那么这是什么问题呢?

  • 可以参考MySQL官方文档 -- 一致性非阻塞读

The snapshot of the database state applies to SELECT statements within a transaction, not necessarily to DML statements. If you insert or modify some rows and then commit that transaction, a DELETE or UPDATE statement issued from another concurrent REPEATABLE READ transaction could affect those just-committed rows, even though the session could not query them. If a transaction does update or delete rows committed by a different transaction, those changes do become visible to the current transaction. 
个人认为应该翻译为: 数据库状态的快照适用于事务中的SELECT语句, 而不一定适用于所有DML语句。 如果您插入或修改某些行, 然后提交该事务, 则从另一个并发REPEATABLE READ事务发出的DELETE或UPDATE语句就可能会影响那些刚刚提交的行, 即使该事务无法查询它们。 如果事务更新或删除由不同事务提交的行, 则这些更改对当前事务变得可见。

当然, 使用隔离性的最高隔离级别SERIALIZABLE也可以解决幻读, 但该隔离级别在实际中很少使用!

InnoDB的MVCC如何解决不可重复读和快照读的幻读,当前读用next-key解决幻读相关推荐

  1. mysql 快照读 幻读,InnoDB的MVCC如何解决不可重复读和快照读的幻读,当前读用next-key解决幻读...

    InnoDB默认的隔离级别是RR(可重复读),可以解决脏读和不可重复读,只解决了快照读情况下的幻读问题,当前读情况下解决幻读问题得靠next-key锁. mysql如何实现避免幻读: 在快照读读情况下 ...

  2. mysql中mvcc解决不可重复读

    最近在了解了mysql中事务的隔离级别,记录一下 事务的隔离级别: 隔离级别 脏读 不可重复读 幻读 读未提交 read-uncommitted 是 是 是 读已提交 read-committed 否 ...

  3. 一图看懂MVCC机制,RC级别解决脏读问题,RR级别怎么解决不可重复读问题【MySQL系列】

    说起事务隔离级别和各自解决的问题,相信学过MySQL的人都倒背如流, 三类问题:脏读.不可重复读.幻读问题: 四种隔离级别: 读不提交,最低的隔离级别,存在脏读.不可重复读.幻读问题: 读已提交,能解 ...

  4. mysql解决不可重复读_mysql怎么解决不可重复读

    mysql解决不可重复读的方法:采用了mvcc多版本并发控制,mvcc是利用在每条数据后面加了隐藏的两列,即创建版本号和删除版本号,每个事务在开始的时候都会有一个递增的版本号. [相关学习推荐:mys ...

  5. mysql防止不可重复读_mysql怎么解决不可重复读

    mysql解决不可重复读的方法:采用了mvcc多版本并发控制,mvcc是利用在每条数据后面加了隐藏的两列,即创建版本号和删除版本号,每个事务在开始的时候都会有一个递增的版本号. [相关学习推荐: my ...

  6. MYSQL的REPEATABLE-READ解决不可重复读和幻读

    做了一个实验 create table t (id number, mount number); insert into t value(1,1);   A B 1 begin;   2 select ...

  7. mysql 不让读的锁_MySQL锁问题(脏读、不可重复读、幻读)

    锁问题 通过锁定机制可以实现事务的隔离性要求,使得事务可以并发地工作.锁提高了并发,但是却会带来潜在地问题.不过好在因为事务隔离性地要求.锁只会带来三种问题,如果可以防止这三种情况地发生,那将不会产生 ...

  8. 可重复读(Repeatable read)能防住幻读吗?

    文章目录 可重复读(Repeatable read)能防住幻读吗? 事务隔离级别 事务的并发问题 概念 幻读和不可重复读的区别 乐观锁与悲观锁 悲观锁 乐观锁 数据版本 MVCC 当前读 Next-K ...

  9. MySQL MVCC多版本并发控制(脏读和不可重复读解决原理)

    文章目录 一.MVCC概念 二.MVCC应用于已提交读隔离级别 1. 解决脏读 2. 无法解决不可重复读 3. 无法解决幻读 三.MVCC应用于可重复读隔离级别 1. 解决脏读 2. 解决不可重复读 ...

  10. mysql重复读导致余额不对_我所理解的MySQL之四:事务、隔离级别及MVCC

    mysql教程栏目介绍MySQL相关的事务.隔离级别及MVCC. MySQL 系列的第四篇,主要内容是事务,包括事务 ACID 特性,隔离级别,脏读.不可重复读.幻读的理解以及多版本并发控制(MVCC ...

最新文章

  1. 利用Nginx实现简易负载均衡
  2. matlab验证对称三相电路,不对称三相电路中,中线的电流为()。 A.0 B. C. D....
  3. ES6基础(var let const 箭头函数)-学习笔记
  4. fileinputstream_从Java中的FileInputStream读取字节
  5. SVM支持向量机习题解答
  6. RH Linux 企业5+apache+mysql+php+phpmyadmin的简单配置.
  7. Spark开发指南(0.8.1中文版)
  8. 亲测:三个值得练手的Java实战项目
  9. 《趣谈网络协议》学习笔记
  10. 小米抢购软件_「晓满晓满晓」今日科技新鲜事:小米11、鸿蒙os、摩拜单车
  11. suse linux 10 下载,SUSE Linux 10下载
  12. Android学习笔记-隐藏app图标
  13. 学会感谢--谈辞职信的写法
  14. Python之组合数据类型(列表、元组、集合、字典)
  15. Linux中cat、more、less、head、tail的区别
  16. 移远EC20--1 AT命令初始2
  17. Python爬虫(2.网络爬虫的实现原理及技术)
  18. Jedis的基本使用
  19. Ubuntu中使用john the ripper口令破解
  20. MODIS数据批量投影工具

热门文章

  1. 成功的自动化测试:测试员的故事
  2. html360全景图原理,通过HTML5 Canvas实现360度全景图
  3. 岩棉墙能用CAN/ULC-S101-M89标准测试吗?
  4. win10创建新的计算机用户名和密码,win10电脑怎么新建用户?高手教你在win10新建用户的方法...
  5. 查看正在运行python进程_查找Python中每个正在运行的进程的路径 - python
  6. 计算机应用与基础教学计划,计算机应用基础教学计划完整版.docx
  7. FDTD的PML设置
  8. 人工神经元再进一步,存储记忆已成现实
  9. 拼音转汉字算法(隐马尔科夫、维特比算法)
  10. 将Ubuntu装入移动硬盘