本文以一系列詳細的sql語句,使你徹底理解mysql的四種隔離級別。
下面的例子中,用到了兩個會話(session),每個session中各開啓了一個事務,分別記作session A, 事務A;session B, 事務B。
準備工作
首先新建一個表‘student’,並插入兩行數據
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
insert student(id, name) values(1, 'zhangsan'), (2, 'lisi');
表中數據如下圖:
id | name |
---|---|
1 | zhangsan |
2 | lisi |
然後用以下sql查詢數據庫的默認隔離級別
select @@transaction_isolation;
可以看到默認隔離級別是“可重複讀”
REPEATABLE-READ
接下來通過sql詳細地說明每一個隔離級別的含義。
read-uncommitted
驗證“讀未提交”, 首先我們開啓一個會話(session A),
設置數據庫的隔離級別爲’read-uncommitted’。
set session transaction_isolation = 'read-uncommitted';
開啓事務A
start transaction;
更新name,並查詢student表
update student set name = 'hehe';
select * from student;
結果如下:
id | name |
---|---|
1 | hehe |
2 | hehe |
注意,當前事務A中,我們還未提交以上update語句的修改(start transaction之後,需要手動執行commit,纔是提交)
現在我們另開一個會話(session B),並設置隔離級別爲"讀未提交"。
set session transaction_isolation = 'read-uncommitted';
接下來開啓事務B,並查詢表’student’
start transaction;
select * from student;
結果如下
id | name |
---|---|
1 | hehe |
2 | hehe |
此時,事務A和B均未提交,我們在事務B中查到了事務A中未提交的數據,此爲髒讀。
read-committed
接下來驗證“讀已提交”, 首先提交以上未提交的事務A和事務B(執行commit;),並設置session A和session B的隔離級別爲“讀已提交”:
set session transaction_isolation = 'read-committed';
在session B中開啓事務B,並查詢表’student’
start transaction;
select * from student;
結果如下:
id | name |
---|---|
1 | hehe |
2 | hehe |
在session A中開啓事務A, 並更新student
start transaction;
update student set name = 'zhangsan' where id = 1;
事務A未提交的時候,再次在事務 B中查詢student
select * from student;
結果與上面一樣:
id | name |
---|---|
1 | hehe |
2 | hehe |
說明“讀已提交”避免了髒讀。
事務A中執行提交
commit;
事務B中再次執行查詢student
select * from student;
結果如下:
id | name |
---|---|
1 | zhangsan |
2 | hehe |
此時,事務A已提交,事務B未提交。而事務B能讀到事務A已提交的數據,並且和前面一次查詢的結果不同了,此爲不可重複讀。“讀已提交”已經可以避免髒讀問題。
repeatable-read
接下來驗證“可重複讀”。首先提交上面的事務A和事務B,然後設置session A和session B的隔離級別是“可重複讀”:
set session transaction_isolation = 'repeatable-read';
session B中,開啓事務B並查詢student
start transaction;
select * from student;
結果如下:
id | name |
---|---|
1 | zhangsan |
2 | hehe |
session A中開啓事務A,並插入一條數據
start transaction;
insert student (id, name)values(3, 'haha');
此時事務A未提交,在事務B中插入同樣id爲3的一行數據
insert student (id, name)values(3, 'haha');
報錯如下:
[Err] 1062 - Duplicate entry '3' for key 'PRIMARY'
再次查詢student
select * from student;
還是
id | name |
---|---|
1 | zhangsan |
2 | hehe |
可以看出,事務B中查詢student,只有兩行數據(id是1和2),可是插入id是3的數據,卻報錯說已存在id爲3的數據,此爲幻讀。“可重複讀”已經可以避免不可重複讀問題。
serializable
這個就不驗證了,很容易理解,“串行化”可以避免幻讀問題。
總結
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
read-uncommitted | 1 | 1 | 1 |
read-committed | 0 | 1 | 1 |
repeatable-read | 0 | 0 | 1 |
serializable | 0 | 0 | 0 |
特別說明,不可重複讀和幻讀的區別不是太明顯,不可重複讀是指一個事務讀到了另一個事務提交的修改,幻讀是指一個事務感受到了另一個事務的增刪數據。也就是說前者指的是“增刪改查”中的“改”,後者指的是“增刪”。