MySQL中的四个隔离级别


MySQL中的四个隔离级别

SQL标准定义了4类隔离级别 低级别的隔离级一般支持更高的并发处理 并拥有更低的系统开销

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读) 💡这是MySQL默认的隔离级别

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

Serializable(可串行化)

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

四种级别 采取不同的锁类型实现

脏读(Drity Read)

某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):

在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):

在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就有几列数据是未查询出来的,如果此时插入和另外一个事务插入的数据,就会报错。

隔离级别分别有可能出现问题 如下:

测试MySQL四种隔离级别

数据库创建 请在声明中下载sql语句 以下测试为两个终端 a b

-- 部分语句 完整版 请在声明中下载
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `num` int(11) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

读未提交 (read uncommitted)

读未提交 (read uncommitted)

  • a 设置隔离级别 开启事务
-- a 设置隔离级别为 read uncommitted
set session transaction isolation level read uncommitted;
-- a 查看是否改变 隔离级别
select @@tx_isolation;
-- a 启动事务
start transaction;
  • b 开启事务 修改数据 不提交
-- b 开启事务
start transaction;
-- b 修改其中一条数据 但不提交
update test set num=3 where id=3;

b修改

  • a 查看数据 发现数据已经发生了修改
-- 
select * from test;

a读取 脏读

  • 回滚事务
rollback;

读已提交 (read committed)

读已提交 (read committed)

  • a 设置隔离级别 开启事务
-- a 设置隔离级别
set session transaction isolation level read committed;
-- a 启动事务
start transaction;
  • b 开启事务 修改数据 不提交
-- b 修改数 不提交
update test set num=100 where id=3;

b修改RC

  • a 查询数据 发现并未修改 因为 b 未提交
select * from test;

第一次读取未修改

  • b 提交 a再查看数据
-- b commit; 提交事务
commit;

-- a 再次读取数据 发现数据已发生变化,说明B提交的修改被事务中的A读到了,这就是所谓的“不可重复读”
select * from test;

数据更改

经过上面的实验可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,
即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。已提交读只允许读取已提交的记录,但不要求可重复读。

Repeatable Read(可重读)

Repeatable Read(可重读)

  • a 设置隔离级别 开启事务
-- a 设置隔离级别为 repeatable read
set session transaction isolation level repeatable read;
-- a 启动事务
start transaction;
  • b 开启事务 修改数据 但不提交
-- b 开启事务
start transaction;

-- b 修改数据 但并不提交
update test set num=100 where id=3;

b-repeatable read

  • a 去读取数据的时候 发现数据库数据未修改
-- a 读取数据 并未更改
select * from test;
-- b commit; 提交
commit;
-- a 读取 还是没变化
select * from test;
-- b 插入一条新的数据
insert into test(num) value(4);
-- a 读取数据 还是没变化
select * from test;

a-幻读

再次读取数据,发现数据依然未发生变化,虽然可以重复读了,但是却发现读的不是最新数据,这就是所谓的“幻读”
由以上的实验可以得出结论,可重复读隔离级别只允许读取已提交记录,而且在一个事务两次读取一个记录期间,
其他事务部的更新该记录。但该事务不要求与其他事务可串行化。
例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题
(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。

serializable(可串行化)

serializable(可串行化)

  • a 设置隔离级别 开启事务
-- a 设置隔离级别为 serializable
set session transaction isolation level serializable;

-- a 启动事务
start transaction;

-- a 查询当前所有数据 但不提交
select * from test;

a不提交

  • b 开启事务 锁超时
-- b 开启事务
start transaction;

-- b 添加数据 发现B此时进入了等待状态,原因是因为A的事务尚未提交,只能等待(此时,B可能会发生等待超时)
insert into test(num) value(8);

-- a 提交
commit;

b等待超时

b锁超时

serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止。是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。

文章作者: 柒仔
项目连接: 下载链接
文章链接: /article/31/
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 XiaoLiu!
侵权声明: 若无意对您的文章造成侵权,请您留言,博主看到后会及时处理,谢谢。
评论-----昵称和邮箱必填,网址选填
  目录