- 1 概述
- 2 实验环境
- 3 实验方法
- 4 测试结果
- 4.1 READ UNCOMMITTED 对 READ UNCOMMITTED
- 4.2 READ COMMITTED 对 READ COMMITTED
- 4.3 REPEATABLE READ 对 REPEATABLE READ
- 4.4 SERIALIZABLE 对 SERIALIZABLE
- 4.5 READ UNCOMMITTED 对 READ COMMITTED
- 4.6 READ COMMITTED 对 READ UNCOMMITTED
- 4.7 READ UNCOMMITTED 对 SERIALIZABLE
- 4.8 SERIALIZABLE 对 READ UNCOMMITTED
- 4.9 READ COMMITTED 对 REPEATABLE READ
- 4.10 REPEATABLE READ 对 READ COMMITTED
- 4.11 REPEATABLE READ 对 SERIALIZABLE
- 4.12 SERIALIZABLE 对 REPEATABLE READ
- 5 结论
1 概述
本实验测试MySQL事务隔离级别的可见性,即在各种事务隔离级别下,一个事务的修改操作(UPDATE, INSERT 和 DELETE 3种操作)对另一个事务是否可见。用于测试的2个事务可能处于相同事务隔离级别,也可能处于不同事务隔离级别。
2 实验环境
MySQL版本: 8.0.25 MySQL Community Server
MySQL存储引擎: InnoDB
操作系统: Windows 10 家庭中文版,版本号1803
3 实验方法
- 打开2个会话:打开2个CMD窗口,在2个窗口中分别用mysql程序连接MySQL服务器,开启2个会话。
- 启用 autocommit:在2个会话中都执行”set session autocommit = 1” 语句,启用autocommit。
- 设置事务隔离级别:用 “set session transaction_isolation = value” 语句设置各会话中事务的事务隔离级别,其中 value为下列之一:’READ-UNCOMMITTED’, ‘READ-COMMITTED’, ‘REPEATABLE-READ’, ‘SERIALIZABLE’。
- 测试修改操作的可见性:在会话1中执行修改操作,在会话2中执行查询操作,看是否能看到会话1中的修改操作结果。
测试表如下:
CREATE TABLE tmp
(
id
int unsigned NOT NULL AUTO_INCREMENT,
status
int NOT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
4 测试结果
本节中”事务隔离级别1 对 事务隔离级别2”表示事务隔离级别1下的事务的修改操作对事务隔离级别2下的事务的可见性。
S1, S2, S3… 是步骤标识。T1, T2, T3…是事务标识。
开始每组实验前,都用以下语句重置表的数据:
truncate table tmp;
insert into tmp (id, status) values (1, 1), (2, 1);
4.1 READ UNCOMMITTED 对 READ UNCOMMITTED
步骤 | 会话1(READ UNCOMMITTED) | 会话2(READ UNCOMMITTED) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | ||||
5 | select * from tmp where id between 1 and 3 | (1,2), (2,1) | S4中T1的UPDATE操作对T2可见 | ||
6 | insert into tmp (id, status) values (3, 1) | ||||
7 | select * from tmp where id between 1 and 3 | (1,2), (2,1), (3,1) | S6中T1的INSERT操作对T2可见 | ||
8 | delete from tmp where id = 2 | ||||
9 | select * from tmp where id between 1 and 3 | (1,2), (3,1) | S8中T1的DELETE操作对T2可见 | ||
10 | rollback | ||||
11 | select * from tmp where id between 1 and 3 | (1,1), (2,1) |
4.2 READ COMMITTED 对 READ COMMITTED
步骤 | 会话1(READ COMMITTED) | 会话2(READ COMMITTED) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | ||||
5 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S4中T1的UPDATE操作对T2不可见 | ||
6 | insert into tmp (id, status) values (3, 1) | ||||
7 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S6中T1的INSERT操作对T2不可见 | ||
8 | delete from tmp where id = 2 | ||||
9 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S8中T1的DELETE操作对T2不可见 | ||
10 | commit | ||||
11 | select * from tmp where id between 1 and 3 | (1,2), (3,1) | S4,S6,S8中T1的UPDATE,INSERT,DELETE操作对T2可见 |
4.3 REPEATABLE READ 对 REPEATABLE READ
步骤 | 会话1(REPEATABLE READ) | 会话2(REPEATABLE READ) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 34 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | ||||
5 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S4中T1的UPDATE操作对T2不可见 | ||
6 | insert into tmp (id, status) values (3, 1) | ||||
7 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S6中T1的INSERT操作对T2不可见 | ||
8 | delete from tmp where id = 2 | ||||
9 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S8中T1的DELETE操作对T2不可见 | ||
10 | commit | ||||
11 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S4,S6,S8中T1的UPDATE,INSERT,DELETE操作对T2不可见 |
4.4 SERIALIZABLE 对 SERIALIZABLE
步骤 | 会话1(SERIALIZABLE) | 会话务2(SERIALIZABLE) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | truncate table tmp; insert into tmp (id, status) values (1,1),(2,1),(4,1),(5,1); |
||||
2 | begin | T1开始 | |||
select * from tmp where id between 1 and 3 | (1,1), (2,1) | ||||
3 | begin | T2开始 | |||
4 | update tmp set status = status + 1 where id = 1 | 锁等待超时,T2回滚 | |||
5 | begin | T3开始 | |||
6 | insert into tmp (id, status) values (3, 1) | 锁等待超时,T3回滚 | |||
7 | begin | T4开始 | |||
8 | delete from tmp where id = 2 | 锁等待超时,T4回滚 | |||
9 | begin | T5开始 | |||
10 | update tmp set status = status + 1 where id = 4 | ||||
11 | insert into tmp (id, status) values (6, 1) | ||||
12 | delete from tmp where id = 5 | ||||
13 | commit | ||||
14 | select * from tmp where id between 1 and 6 | (1,1),(2,1),(4,2),(6,1) | |||
15 | begin | T6开始 | |||
16 | insert into tmp (id, status) values (7, 1) | 锁等待超时,T6回滚 |
4.5 READ UNCOMMITTED 对 READ COMMITTED
步骤 | 会话1(READ UNCOMMITTED) | 会话2(READ COMMITTED) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | ||||
5 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S4中T1的UPDATE操作对T2不可见 | ||
6 | insert into tmp (id, status) values (3, 1) | ||||
7 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S6中T1的INSERT操作对T2不可见 | ||
8 | delete from tmp where id = 2 | ||||
9 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | S8中T1的DELETE操作对T2不可见 | ||
10 | commit | ||||
11 | select * from tmp where id between 1 and 3 | (1,2), (3,1) | S4,S6,S8中T1的UPDATE,INSERT,DELETE操作对T2可见 |
4.6 READ COMMITTED 对 READ UNCOMMITTED
步骤 | 会话1(READ COMMITTED) | 会话2(READ UNCOMMITTED) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | ||||
5 | select * from tmp where id between 1 and 3 | (1,2), (2,1) | S4中T1的UPDATE操作对T2可见 | ||
6 | insert into tmp (id, status) values (3, 1) | ||||
7 | select * from tmp where id between 1 and 3 | (1,2), (2,1), (3,1) | S6中T1的INSERT操作对T2可见 | ||
8 | delete from tmp where id = 2 | ||||
9 | select * from tmp where id between 1 and 3 | (1,2), (3,1) | S8中T1的DELETE操作对T2可见 | ||
10 | rollback | ||||
11 | select * from tmp where id between 1 and 3 | (1,1), (2,1) |
4.7 READ UNCOMMITTED 对 SERIALIZABLE
步骤 | 会话1(READ UNCOMMITTED) | 会话2(SERIALIZABLE) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | 锁等待超时,T1回滚 | |||
5 | begin | T3开始 | |||
6 | insert into tmp (id, status) values (3, 1) | 锁等待超时,T3回滚 | |||
7 | begin | T4开始 | |||
8 | delete from tmp where id = 2 | 锁等待超时,T4回滚 |
4.8 SERIALIZABLE 对 READ UNCOMMITTED
步骤 | 会话1(SERIALIZABLE) | 会话2(READ UNCOMMITTED) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | 锁等待超时,T1回滚 | |||
5 | begin | T3开始 | |||
6 | insert into tmp (id, status) values (3, 1) | 锁等待超时,T3回滚 | |||
7 | begin | T4开始 | |||
8 | delete from tmp where id = 2 | 锁等待超时,T4回滚 |
4.9 READ COMMITTED 对 REPEATABLE READ
4.10 REPEATABLE READ 对 READ COMMITTED
4.11 REPEATABLE READ 对 SERIALIZABLE
步骤 | 会话1(REPEATABLE READ) | 会话2(SERIALIZABLE) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | 锁等待超时,T1回滚 | |||
5 | begin | T3开始 | |||
6 | insert into tmp (id, status) values (3, 1) | 锁等待超时,T3回滚 | |||
7 | begin | T4开始 | |||
8 | delete from tmp where id = 2 | 锁等待超时,T4回滚 |
4.12 SERIALIZABLE 对 REPEATABLE READ
步骤 | 会话1(SERIALIZABLE) | 会话2(REPEATABLE READ) | 可见性 | ||
---|---|---|---|---|---|
操作 | 结果 | 操作 | 结果 | ||
1 | begin | T1开始 | |||
2 | begin | T2开始 | |||
3 | select * from tmp where id between 1 and 3 | (1,1), (2,1) | |||
4 | update tmp set status = status + 1 where id = 1 | 锁等待超时,T1回滚 | |||
5 | begin | T3开始 | |||
6 | insert into tmp (id, status) values (3, 1) | 锁等待超时,T3回滚 | |||
7 | begin | T4开始 | |||
8 | delete from tmp where id = 2 | 锁等待超时,T4回滚 |
5 结论
一个事务的事务隔离级别定义了其他并发事务(无论其事务隔离级别是什么)的修改对该事务的可见性,而没有定义该事务的修改对其他并发事务的可见性。
对于MySQL 8.0 和 InnoDB 存储引擎,假设有T1-T100 共100个并发事务,其中T1-T4的事务隔离级别分别是 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ 和 SERIALIZABLE,T5-T100的事务隔离级别任意,则T1-T4 4个事务的行为如下表所示:
事务 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
T1 | 可能 | 可能 | 可能 |
T2 | 不可能 | 可能 | 可能 |
T3 | 不可能 | 不可能 | 不可能 |
T4 | 不可能 | 不可能 | 不可能 |