欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MySQL中事务的隔离级别亲测

程序员文章站 2022-06-14 17:00:48
...

事务的隔离级别

四种隔离级别

事务的隔离级别分为四种:

  1. READ-UNCOMMITED:读未提交。
  2. READ-COMMITED:读已提交。
  3. REPEATABLE-READ:可重复读。
  4. SERIALIZABLE:串行化。

隔离级别作用

上述四种事务的隔离级别,是为了解决以下三种问题:

  1. 脏读:读到其他事务未提交的数据。
  2. 幻读:同一事务中,多次读取的记录条数不等,其他事务添加或删除的数据会影响到当前事务。
  3. 不可重复读:同一事务中,多次读取相同记录的内容会有所不同,当前事务在执行的过程中会读取到其他事务已经修改的数据。

各隔离级别解决的问题为(SQL规范中定义如下解决范围):

  1. READ-UNCOMMITED:脏读,幻读,不可重复读,都会发生。
  2. READ-COMMITED:可以解决脏读的问题。
  3. REPEATABLE-READ:脏读,不可重复读都不会发生了。
  4. SERIALIZABLE:以上问题都不会发生了。

验证以上理论

好了,纸上谈兵终觉浅,绝知此事要躬行。换句话说Talk is cheap,Show me the code。我们来验证下吧。

准备工作

先创建一张表:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
	`username` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

向其中插入一条数据:

INSERT INTO `user` (username, age ) VALUES("jxl", 18);

READ-UNCOMMITED

在你的MySQL客户端中,执行以下语句,将隔离级别设置为READ-UNCOMMITED:

select @@global.tx_isolation,@@session.tx_isolation;

set @@global.tx_isolation='READ-UNCOMMITTED', @@session.tx_isolation='READ-UNCOMMITTED';

select @@global.tx_isolation,@@session.tx_isolation;

打开一个新的窗口,也就是打开一个新的会话,确定隔离级别已经生效:

-- --------------------------查看当前隔离级别-----------------------------------
select @@global.tx_isolation,@@session.tx_isolation;
START TRANSACTION;

-- --------------------------更改当前值,不提交-----------------------------------
SELECT `age` FROM `user` WHERE id = 1;

UPDATE `age` set `age`=`age`+ 1 WHERE id = 1;

SELECT `age` FROM `user` WHERE id = 1;

我们再打开一个窗口,查看age的值是否变化:

SELECT `age` FROM `user` WHERE id = 1;

经过验证,虽然第二个会话中update后,没有提交,但是在READ-UNCOMMITED的隔离级别下,其他的事务还是可以读到没有提交的变化,age已经由18变成了19。也就是出现了脏读的情况。

READ-COMMITED

在你的MySQL客户端中,执行以下语句,将隔离级别设置为READ-COMMITED:

select @@global.tx_isolation,@@session.tx_isolation;

set @@global.tx_isolation='READ-COMMITTED', @@session.tx_isolation='READ-COMMITTED';

select @@global.tx_isolation,@@session.tx_isolation;

打开一个新的窗口,也就是打开一个新的会话,确定隔离级别已经生效

-- --------------------------查看当前隔离级别-----------------------------------
select @@global.tx_isolation,@@session.tx_isolation;
START TRANSACTION;

-- --------------------------更改当前值,不提交-----------------------------------
SELECT `age` FROM `user` WHERE id = 1;

UPDATE `age` set `age`=`age`+ 1 WHERE id = 1;

SELECT `age` FROM `user` WHERE id = 1;

我们再打开一个窗口,查看age的值是否变化:

START TRANSACTION;

SELECT `age` FROM `user` WHERE id = 1;

经过验证,age还是18,没有查到另一个会话中已经更改的19,脏读的问题已经没有了。

验证幻读是否会发生:

我们update的那个会话,进行commit,我们在刚才的会话中继续select,看是否可以查询到update后的值:

经过验证,在查询的会话中,可以查询到更新的值,说明出现了不可重复读。

REPEATABLE-READ

在你的MySQL客户端中,执行以下语句,将隔离级别设置为REPEATABLE-READ:

select @@global.tx_isolation,@@session.tx_isolation;

set @@global.tx_isolation='REPEATABLE-READ', @@session.tx_isolation='REPEATABLE-READ';

select @@global.tx_isolation,@@session.tx_isolation;

打开一个新的窗口,也就是打开一个新的会话,确定隔离级别已经生效

-- --------------------------查看当前隔离级别-----------------------------------
select @@global.tx_isolation,@@session.tx_isolation;
START TRANSACTION;

-- --------------------------更改当前值,不提交-----------------------------------
SELECT `age` FROM `user` WHERE id = 1;

UPDATE `age` set `age`=`age`+ 1 WHERE id = 1;

SELECT `age` FROM `user` WHERE id = 1;

我们再打开一个窗口,查看age的值是否变化:

START TRANSACTION;

SELECT `age` FROM `user` WHERE id = 1;

经过验证,age还是18,没有查到另一个会话中已经更改的19,脏读的问题已经没有了。

这时,将update的那个会话的事务commit,在查询的这个会话中继续select,发现还是update之前的值,说明在同一个会话中,查询同一条数据的内容,总是相同的,即使过程中其他的会话已经提交了事务。说明不可重复读已经没有发生了。

下面才是幻读,一个会话中:

START TRANSACTION;

SELECT * FROM `user` WHERE age >= 18;

SELECT SLEEP(20);

SELECT * FROM `user` WHERE age >= 18;

另一个会话中:

START TRANSACTION;

SELECT * FROM `user` WHERE age >= 18;

INSERT INTO `user`(username, age) VALUES ('jxl', 21);

SELECT * FROM `user` WHERE age >= 18;

COMMIT;

我们会发现第一个会话中,两次查询的结果相同,说明没有幻读的情况发生了,这是因为我们采用的InnoDB引擎,它采用了MVCC(多版本并发控制)与next-key locks(范围锁)来解决幻读的问题。

快照读:也就是单存select的情况下,使用MVCC(多版本并发控制)来解决幻读的问题。

当前读:select加锁,及insert,update,delete情况下,使用next-key locks及Gap(范围锁)来解决幻读问题。

这时你一定疑惑了,那么READ-COMMITED的隔离级别是否有幻读的问题,经过笔者的验证,读提交的隔离级别还是会发生幻读的问题。因为以上说的解决幻读的问题,指的都是InnoDB引擎在可重复读REPEATABLE-READ的隔离级别下采用的解决幻读的方法。

SERIALIZABLE

此级别就不用验证了,因为他的级别比重复读的级别高,根本就是串行的,所以脏读,不可重复读,以及幻读的问题都不会发生了。