MySQL 事务的默认隔离级别是啥?可以解决幻读问题么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 事务的默认隔离级别是啥?可以解决幻读问题么?相关的知识,希望对你有一定的参考价值。

参考技术A

我们设想一个场景,这个场景中我们需要插入多条相关联的数据到数据库,不幸的是,这个过程可能会遇到下面这些问题:

上面的任何一个问题都可能会导致数据的不一致性。为了保证数据的一致性,系统必须能够处理这些问题。事务就是我们抽象出来简化这些问题的首选机制。事务的概念起源于数据库,目前,已经成为一个比较广泛的概念。

何为事务? 一言蔽之, 事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作,这两个操作必须都成功或者都失败。

事务会把这两个操作就可以看成逻辑上的一个整体,这个整体包含的操作要么都成功,要么都要失败。这样就不会出现小明余额减少而小红的余额却并没有增加的情况。

大多数情况下,我们在谈论事务的时候,如果没有特指 分布式事务 ,往往指的就是 数据库事务

数据库事务在我们日常开发中接触的最多了。如果你的项目属于单体架构的话,你接触到的往往就是数据库事务了。

那数据库事务有什么作用呢?

简单来说,数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循: 要么全部执行成功,要么全部不执行

另外,关系型数据库(例如: mysql 、 SQL Server 、 Oracle 等)事务都有 ACID 特性:

ACID

这里要额外补充一点: 只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。

不可重复读和幻读区别 :不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次查询同一条查询语句(DQL)时,记录发现记录增多或减少了。

SQL 标准定义了四个隔离级别:

隔离级别脏读不可重复读幻读 READ-UNCOMMITTED READ-COMMITTED REPEATABLE-READ SERIALIZABLE

MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。

SERIALIZABLE 隔离级别,是通过锁来实现的。除了 SERIALIZABLE 隔离级别,其他的隔离级别都是基于 MVCC 实现。

不过, SERIALIZABLE 之外的其他隔离级别可能也需要用到锁机制,就比如 REPEATABLE-READ 在当前读情况下需要使用加锁读来保证不会出现幻读。

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 。我们可以通过 SELECT @@tx_isolation; 命令来查看,MySQL 8.0 该命令改为 SELECT @@transaction_isolation;

从上面对 SQL 标准定义了四个隔离级别的介绍可以看出,标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。

但是!InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失。

InnoDB 存储引擎在分布式事务的情况下一般会用到 SERIALIZABLE 隔离级别。

三连问:MySQL如何实现可重复读又为什么会出现幻读是否解决了幻读问题?


大家好,这里是【通学技术】天地












新的一年,一起学习,共同进步


https://blog.csdn.net/sanyuesan0000

事务隔离级别有四种,mysql默认使用的是可重复读,mysql是怎么实现可重复读的?为什么会出现幻读?是否解决了幻读的问题?

一、事务的隔离级别

Read Uncommitted(未提交读)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。读取未提交的数据,也被称之为脏读(Dirty Read)。该级别用的很少。

Read Committed(提交读)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变,换句话说就是事务提交之前对其余事务不可见。

这种隔离级别也支持不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select查询可能返回不同结果。

Repeatable Read(可重复读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。

简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题(mysql彻底解决了幻读问题?请往下看)。

Serializable(可串行化)

这是最高的隔离级别,它强制事务都是串行执行的,使之不可能相互冲突,从而解决幻读问题。换言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

在MySQL的众多存储引擎中,只有InnoDB支持事务,所有这里说的事务隔离级别指的是InnoDB下的事务隔离级别。

二、mysql怎么实现的可重复读

MVCC多版本并发控制(Multi-Version Concurrency Control)是MySQL中基于乐观锁理论实现隔离级别的方式,用于实现读已提交和可重复读取隔离级别。

在《高性能MySQL》中对MVCC的解释如下

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

举例说明MVCC的实现

新建一张表test_zq如下

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

MVCC逻辑流程-插入

在插入数据的时候,假设系统的全局事务ID从1开始,以下SQL语句执行分析参考注释信息:

begin;-- 获取到全局事务ID
insert into `test_zq` (`id``test_id`values(\'5\',\'68\');
insert into `test_zq` (`id``test_id`values(\'6\',\'78\');
commit;-- 提交事务

当执行完以上SQL语句之后,表格中的内容会变成:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

可以看到,插入的过程中会把全局事务ID记录到列 DB_TRX_ID 中去

MVCC逻辑流程-删除

对上述表格做删除逻辑,执行以下SQL语句(假设获取到的事务逻辑ID为 3)

begin--获得全局事务ID = 3
delete test_zq where id = 6;
commit;

执行完上述SQL之后数据并没有被真正删除,而是对删除版本号做改变,如下所示:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

MVCC逻辑流程-修改

修改逻辑和删除逻辑有点相似,修改数据的时候 会先复制一条当前记录行数据,同事标记这条数据的数据行版本号为当前是事务版本号,最后把原来的数据行的删除版本号标记为当前是事务。推荐:

执行以下SQL语句:

begin;-- 获取全局系统事务ID 假设为 10
update test_zq set test_id = 22 where id = 5;
commit;

执行后表格实际数据应该是:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

MVCC逻辑流程-查询

此时,数据查询规则如下:

查找数据行版本号早于当前事务版本号的数据行记录

也就是说,数据行的版本号要小于或等于当前是事务的系统版本号,这样也就确保了读取到的数据是当前事务开始前已经存在的数据,或者是自身事务改变过的数据

查找删除版本号要么为NULL,要么大于当前事务版本号的记录

这样确保查询出来的数据行记录在事务开启之前没有被删除

根据上述规则,我们继续以上张表格为例,对此做查询操作

begin;-- 假设拿到的系统事务ID为 12
select * from test_zq;
commit;

执行结果应该是:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

这样,同一个事务中,就实现了可重复读。

三、幻读

什么是幻读,如下:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

InnoDB实现的RR通过mvcc机制避免了这种幻读现象。

另一种幻读:

三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

姑且把左边的事务命名为事务A,右边的命名为事务B。

事务B执行后,在事务A中查询没有查到B添加的数据行,这就是可重复读。

但是,在事务A执行了update后,再查询时就查到了事务A中添加的数据,这就是幻读。

这种结果告诉我们其实在MySQL可重复读的隔离级别中并不是完全解决了幻读的问题,而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题,就是说MVCC对于幻读的解决是不彻底的。

原以为这也是一种幻读,但经过多次研究资料,这只是对数据修改的操作(update、insert、delete)当前读产生的结果,他其实不是幻读。

快照读和当前读

出现了上面的情况我们需要知道为什么会出现这种情况。在查阅了一些资料后发现在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,不是数据库最新的数据。这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库最新版本数据的方式,叫当前读 (current read)。

select 快照读

当执行select操作是innodb默认会执行快照读,会记录下这次select后的结果,之后select 的时候就会返回这次快照的数据,即使其他事务提交了不会影响当前select的数据,这就实现了可重复读了。

快照的生成当在第一次执行select的时候,也就是说假设当A开启了事务,然后没有执行任何操作,这时候B insert了一条数据然后commit,这时候A执行 select,那么返回的数据中就会有B添加的那条数据。之后无论再有其他事务commit都没有关系,因为快照已经生成了,后面的select都是根据快照来的。

当前读

对于会对数据修改的操作(update、insert、delete)都是采用当前读的模式。在执行这几个操作时会读取最新的版本号记录,写操作后把版本号改为了当前事务的版本号,所以即使是别的事务提交的数据也可以查询到。

假设要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据。也正是因为这样所以才导致幻读。推荐:

四、如何解决幻读

在快照读情况下,mysql通过mvcc来避免幻读。

在当前读情况下,mysql通过X锁或next-key来避免其他事务修改:

  • 使用串行化读的隔离级别
  • (update、delete)当where条件为主键时,通过对主键索引加record locks(索引加锁/行锁)处理幻读。
  • (update、delete)当where条件为非主键索引时,通过next-key锁处理。next-key是record locks(索引加锁/行锁) 和 gap locks(间隙锁,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据)的结合。
三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

Next-Key Lock即在事务中select时使用如下方法加锁,这样在另一个事务对范围内的数据进行修改时就会阻塞(为什么有共享锁会阻塞?不能在有共享锁的记录上加X锁):

select * from table where id<6 lock in share mode;--共享锁
select * from table where id<6 for update;--排他锁

关于next-key locks请参考

https://www.cnblogs.com/zhoujinyi/p/3435982.html

参考文章

  • https://juejin.im/post/5c68a4056fb9a049e063e0ab
  • https://zhuanlan.zhihu.com/p/35500144
  • https://www.jianshu.com/p/69fd2ca17cfd
  • https://blog.csdn.net/AAA821/article/details/81017704
  • https://dbaplus.cn/news-11-2518-1.html
  • 《高性能MySQL》

END

重磅!程序员交流群(无广告)成立

在群里和大家分享一些程序员开发相关的知识,包括部分自己的实战项目,基础入门知识,spring,jvm,mysql等等.

一定要备注:Java,更快被通过且邀请进群


三连问:MySQL如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?

“通学技术”欢迎广大技术人员投稿,投稿邮箱:491783100@qq.com



哦,对了,关注【通学技术】加我微信好友,后台

回复【资料】:领取Java核心知识总结~
回复【面试】 : 领取最新各大厂面试题~

回复【P6】 : 领取最新超详细面试题~
回复【文档】:领取 最新智能合约系列文档(持续更新中)~


点击【阅读原文】,获取更多精彩文章哦。如有收获,请帮忙转发或留言,您的鼓励是作者最大的动力,谢谢!

                       在看点这里好文分享给更多人↓↓

以上是关于MySQL 事务的默认隔离级别是啥?可以解决幻读问题么?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL的RR隔离级别与幻读问题

MySQL 基础 事务 -- 事务简介事务操作事务四大特性(原子性一致性隔离性持久性)并发事务问题(不可重复读脏读幻读)事务隔离级别(解决并发事务问题)

MySQL5.7 Read Committed事务隔离级别的研究-出现幻读

Mysql在可重复读事务隔离级别下怎么解决幻读的

MySQL的默认事务隔离级别是?

MySQL InnoDB事务隔离级别脏读可重复读幻读