先看结论:(重要)
MySQL中innodb事务默认的隔离级别为REPEATABLE READ,可重读隔离级别,配合MySQLinnodb的行锁机制Next-Key Lock的实现,在REPEATABLE READ的级别下,就避免了幻读的产生,因此几乎达到了SERIALIZABLE级别的要求。
那么问题来了:
- 以上这句话是什么意思,什么是隔离级别?
- 什么是REPEATABLE READ?
- 还有哪些隔离级别?
- 不同的级别解决了什么问题?
- 什么是脏读,不可重复读,幻读?
大家请看下面的内容,都有详细的介绍。(如果可能,会录制相应的视频)
隔离性,事务必须要满足的ACID四特性之一,其中的I,Isolation,就是隔离性。
隔离性指的是:一个事务在提交前,对其他事务都不可见。
MySQL是利用 锁机制来实现隔离性的。
而本文要说的隔离级别,指的就是,隔离性要实现到什么程度,就是事务间,要隔离到什么程度。
ANSI SQL(标准SQL),制订了4种事务的隔离级别,分别为:
(由低到高,隔离程度由低到高)
- READ UNCOMMITTED,读未提交
- READ COMMITTED,读提交
- REPEATABLE READ,可重复读
- SERIALIZABLE,序列化(串行化)
以下测试用到的表:
— 表结构
drop table if exists kang_tx;
create table kang_tx (
num int,
primary key (num)
) engine=innodb;
— 初始测试数据
insert into kang_tx values (1), (2), (4);
使用两个客户端连接,执行两个事务进行测试(注意:以下测试都是在初始数据环境下测试的,重复执行上面的代码,就可以获得初始数据)
不同隔离级别具体含义为:
READ UNCOMMITTED,读未提交。顾名思义,就是事务可以读取到其他事务中未提交的修改。
这个级别相当低了,几乎没有隔离而言。不能满足事务对隔离性的需求的。
session-A,开启离级别为 read uncommitted的事务A
— session-A
— 设置隔离级别为 read uncommitted
set transaction isolation level read uncommitted;
start transaction;
session-B,开启另一个事务B,更新数据
— session-B
start transaction;
— 插入一条新的记录
insert into kang_tx values (5);
— 不执行commit提交
session-A,回到第一个事务A
— session-A
— 等待session-B插入新数据后执行
select * from kang_tx;
结果:
解释:session-A开启了隔离级别为read uncommitted的事务。过程中,session-B执行了修改表kang_tx,插入一条记录的操作,但是没有执行提交。之后,在 session-A中查看全部的数据,结果发现session-B中未提交的操作数据,可以在session-A中查询的到。这就是读未提交的隔离级别。
该隔离级别会存在,脏读问题。其实同时会出现,不可重复读和幻读问题。不同的级别会分别解决这些问题,先看脏读。
脏读:未提交的数据,也就是没有被持久化到磁盘的数据,被称之为脏数据,因此,在当前隔离级别下,会出现脏读的问题。也就是读取到了未提交的脏数据。
READ COMMITTED,读提交,指的是事务可以读取到其他事务已经提交的数据。
测试如下,恢复到初始数据。
session-A,开启离级别为 read committed的事务A
— session-A
— 设置隔离级别为 read committed
set transaction isolation level read committed;
start transaction;
session-B,开启另一个事务B,更新数据
— session-B
start transaction;
— 更新一条记录
update kang_tx set num=20 where num=2;
— 不执行commit提交
session-A,测试是否可以读取到其他事务未提交的数据
— session-A
— 等待session-B更新数据后执行
select * from kang_tx;
结果:
session-B,提交事务B
— session-B
— 执行提交
commit;
session-A,测试是否可以读取到其他事务提交的数据
— session-A
— 等待session-B提交后执行
select * from kang_tx;
结果:
解释:事务A,级别为read committed,以为这可以读取到其他事务提交的数据。当事务B更新数据后,直接在事务A中读取,没有读取到,因为此时没有提交,但当事务B执行提交后,事务A读取到了更新的数据,因为数据已经提交了,这就是读提交隔离级别!
该隔离级别会解决脏读问题,但是会出现不可重复读和幻读问题,先来看不可重复读问题:
不可重复读: 读到提交的数据,会带来一个问题,就是当事务A读取到某数据后,此时事务B提交了对该数据的修改,之后事务A再次读取该数据,数据就变成了事务B提交后数 据。也就意味着事务A内前后两次读取的同一数据是不相同的。这就是不可重复读。因为不能保证重复读的结果一致。也是当前隔离级别所带来的问题。
REPEATABLE READ,重复读,指的是同一事务内,可以反复的读取同一数据,不会被其他事务所修改。就是刚刚读提交时的问题。
测数如下,重新初始数据:
session-A,开启repeatable read级别的事务,先读取一次:
— session-A
— 设置隔离级别为 repeatable read
set transaction isolation level repeatable read;
start transaction;
— session-A
— 等待session-B提交后执行
select * from kang_tx;
结果:
session-B,开启事务,并提交
— session-B
start transaction;
— 更新一条记录
update kang_tx set num=20 where num=2;
— 执行提交
commit;
session-A,测试读取到的数据是什么:
解释:当前隔离级别下,如果事务A已经读取过了某些数据,此时事务B更新了这些数据。事务A再次读取该数据时,不会受事务B的影响,即使事务B已经提交了修改。这就是可重复读的隔离级别。
注意,如果事务A没有读取的数据,事务B提交了,事务A再后读取的话,是可以读取到提交的数据的。因为这不是重复读。
在标准的可重复读级别下,会出现幻读问题。
幻读: 假设初始数据有:1,2,4,5,8。事务A先读取的小于等于4(where num<=4)的数据1,2,4,此时事务B向表中插入数据3并提交,然后事务A再次读取小于等于4的数据,就会发现会读取到事务B插入的3数据。 这个现象就是幻读,这个3数据,对于事务A来说,就像幽灵,像幻觉一样,刚刚还没有呢,重复读取同一条件(where num<=4)的数据,就又出现了。称之为幻读。
使用MySQL的一定注意:MySQL 的默认事务隔离级别就是该REPEATABLE READ级别,但由于MySQL的实现使用了next-Key Lock模式(一种行锁模式,同时锁定某个范围内的全部行和范围,可以参考我的关于MySQL锁的介绍),导致一旦读取了某些条件的数据例如(where num<=4),就会锁定这些记录行以及<=4这个范围,意味着就不能插入3这个数据了。从而避免了幻读的问题。因此对于MySQL来说,使 用REPEATABLE READ级别就达到了消除:脏读,不可重复读,幻读的典型隔离性问题。
SERIALIZABLE,序列化(串行化),指的是事务A在执行时,不允许事务B对数据执行更新操作。
测试如下,恢复初始数据:
session-A,开启serializable隔离级别的事务,但是未提交
— session-A
— 设置隔离级别为 serializable
set transaction isolation level serializable;
start transaction;
— session-A
— 等待session-B提交后执行
select * from kang_tx;
session-B,开启事务,尝试获取和更新数据
— session-B
start transaction;
— 读取内容
select * from kang_tx;
— 更新一条记录
update kang_tx set num=20 where num=2;
结果:
session-A,提交事务
— session-A
commit;
session-B,刚刚被阻塞的执行,在事务A提交后,执行了
结果:
解释:在串行化级别的事务A开启后,添加了锁定,阻塞了所有的其他事务写操作,意味着只有在事务A,提交后,其他事务才能对数据进行写操作。这就是串行化级别。该级别就避免了,脏读,不可重复读,幻读的问题。是最理想的隔离级别。
但是并一定最常用,因为,隔离性理想了,但是为实现隔离性从而的锁开销增大了。因此为了兼顾平衡,较多数据库实现的是:read committed或repeatable read级别。
通常只有在XA分布式事务时,才会使用该级别。关于分布式XA事务,可以参考文章。
来个总结:
看完之后,试着回答本文最开头的问题。
一家之言,欢迎拍砖。
韩忠康
任意转载,请保留出处