武漢哪家網(wǎng)站建設(shè)公司好怎么用手機(jī)創(chuàng)建網(wǎng)站
事務(wù)的隔離級別
- 1. 如何理解事務(wù)的隔離性
- 2. 事務(wù)隔離級別的分類
- 3. 查看和設(shè)置事務(wù)隔離級別
- 3.1 全局和會話隔離級別
- 3.2 查看和設(shè)置隔離級別
- 4. 事務(wù)隔離級別的演示
- 4.1 讀未提交(Read Uncommitted)
- 4.2 讀已提交(Read Committed)
- 4.3 可重復(fù)讀(Repeatable Read)
- 4.3.1 為什么要有可重復(fù)讀?
- 4.3.2 可重復(fù)讀
- 4.3.3 說明
- 4.4 序列化(Serializable)
- 5. 總結(jié)
1. 如何理解事務(wù)的隔離性
MySQL服務(wù)可能會同時(shí)被多個(gè)客戶端進(jìn)程(線程)訪問,訪問的方式以事務(wù)方式進(jìn)行。
一個(gè)事務(wù)可能由多條SQL構(gòu)成,也就意味著,任何一個(gè)事務(wù),都有執(zhí)行前,執(zhí)行中,執(zhí)行后的階段。而所謂的原子性,其實(shí)就是讓用戶層,要么看到執(zhí)行前,要么看到執(zhí)行后。執(zhí)行中出現(xiàn)問題,可以隨時(shí)回滾。所以單個(gè)事務(wù),對用戶表現(xiàn)出來的特性,就是原子性。
但,畢竟所有事務(wù)都要有個(gè)執(zhí)行過程,那么在多個(gè)事務(wù)各自執(zhí)行多個(gè)SQL的時(shí)候,就還是有可能會出現(xiàn)互相影響的情況。比如:多個(gè)事務(wù)同時(shí)訪問同一張表,甚至同一行數(shù)據(jù)。
比如,給同學(xué)1的任務(wù)是一次性把水池裝滿,給同學(xué)2的任務(wù)是一次性把水池的水放干:同學(xué)1在裝水池的過程中而同學(xué)2放水池的水,結(jié)果他們倆個(gè)相互影響都完成不了任務(wù)。如果他們兩個(gè)的工作隔離起來,先讓同學(xué)1裝水,水裝滿后同學(xué)2在進(jìn)行放水,這樣他們兩個(gè)都可以完成任務(wù)。
數(shù)據(jù)庫中,為了保證事務(wù)執(zhí)行過程中盡量不受干擾,就有了一個(gè)重要特征:隔離性
數(shù)據(jù)庫中,允許事務(wù)受不同程度的干擾,就有了一種重要特征:隔離級別
2. 事務(wù)隔離級別的分類
-
讀未提交(Read Uncommitted)
定義:在讀未提交隔離級別下,一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)尚未提交的數(shù)據(jù)。這是最低的隔離級別,允許臟讀(Dirty Reads)。- 優(yōu)點(diǎn):提供最高的并發(fā)性能,因?yàn)槭聞?wù)之間幾乎沒有阻塞。
- 缺點(diǎn):可能會出現(xiàn)臟讀,即讀取到其他事務(wù)未提交的數(shù)據(jù)??赡軙霈F(xiàn)不可重復(fù)讀(Non-repeatable Reads)和幻讀(Phantom Reads)。
-
讀已提交(Read Committed)
定義:在讀已提交隔離級別下,一個(gè)事務(wù)只能讀取到另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。事務(wù)在每次讀取數(shù)據(jù)時(shí)都會看到最新的已提交數(shù)據(jù)。- 優(yōu)點(diǎn):避免了臟讀。提供了較好的并發(fā)性能。
- 缺點(diǎn):可能會出現(xiàn)不可重復(fù)讀,即在同一事務(wù)中多次讀取同一數(shù)據(jù)時(shí),可能會得到不同的結(jié)果??赡軙霈F(xiàn)幻讀。
-
可重復(fù)讀(Repeatable Read)
定義:在可重復(fù)讀隔離級別下,一個(gè)事務(wù)在執(zhí)行期間多次讀取同一數(shù)據(jù)時(shí),結(jié)果始終相同,即使其他事務(wù)對這些數(shù)據(jù)進(jìn)行了修改并提交。這是 MySQL 的默認(rèn)隔離級別。- 優(yōu)點(diǎn):避免了臟讀和不可重復(fù)讀。提供了較高的數(shù)據(jù)一致性。
- 缺點(diǎn):可能會出現(xiàn)幻讀,即在同一事務(wù)中多次執(zhí)行相同的查詢,可能會出現(xiàn)新的記錄。
-
序列化(Serializable)
定義:在序列化隔離級別下,事務(wù)被完全隔離,每個(gè)事務(wù)都按順序執(zhí)行,如同在單線程環(huán)境中一樣。這是最高的隔離級別,確保了數(shù)據(jù)的一致性,但并發(fā)性能較差。- 優(yōu)點(diǎn):避免了臟讀、不可重復(fù)讀和幻讀。提供了最高的數(shù)據(jù)一致性。
- 缺點(diǎn):并發(fā)性能較低,因?yàn)槭聞?wù)之間會有更多的阻塞和等待。
3. 查看和設(shè)置事務(wù)隔離級別
3.1 全局和會話隔離級別
-
全局隔離級別(Global Isolation Level)
- 全局隔離級別為所有新會話的默認(rèn)隔離級別。
- 一旦設(shè)置,所有新的會話將使用這個(gè)隔離級別,除非在會話中明確更改。
- 對已經(jīng)存在的會話沒有影響。
-
會話隔離級別(Session Isolation Level)
- 會話隔離級別為當(dāng)前會話的隔離級別。
- 默認(rèn)的使用是全局隔離級別。
- 修改后僅對當(dāng)前會話有效,不影響其他會話。
3.2 查看和設(shè)置隔離級別
全局隔離級別的設(shè)置:
SET GLOBAL TRANSACTION ISOLATION LEVEL isolation_level;
查詢?nèi)值母綦x級別:
SELECT @@GLOBAL.TX_ISOLATION;
會話隔離級別的設(shè)置:
SET SESSION TRANSACTION ISOLATION LEVEL isolation_level;
查詢會話的隔離級別:
SELECT @@SESSION.TX_ISOLATION;
SELECT @@TX_ISOLATION;
示例:
查詢?nèi)指綦x級別和會話隔離級別:
# 查看全局隔離級別
select @@global.tx_isolation;# 查看會話
select @@session.tx_isolation;
顯然,全局隔離級別和會話隔離級別是一樣的,因?yàn)?mark>當(dāng)開啟一個(gè)新的客戶端時(shí),會話隔離級別默認(rèn)是全局的隔離級別。
修改會話的隔離級別為 read uncommitted
讀未提交,再次查詢會話隔離級別
set session transaction isolation level read uncommitted;
顯然,會話的隔離級別已經(jīng)變?yōu)?code>讀未提交。
修改全局的隔離級別為 read committed
讀提交,然后查詢?nèi)趾蜁捀綦x級別
# 設(shè)置全局隔離級別
set global transaction isolation level read committed;# 查詢隔離級別
select @@global.tx_isolation;
select @@session.tx_isolation;
為什么全局隔離級別后,全局隔離級別和會話隔離接不一樣呢?
答:會話隔離級別默認(rèn)的是上次修改的(創(chuàng)建會話時(shí)默認(rèn)全局隔離級別),修改全局隔離級別不能直接修改當(dāng)前會話的隔離級別。
退出MySQL,再次進(jìn)入MySQL客戶端,查看隔離級別
# 退出
quit# 查詢隔離級別
select @@global.tx_isolation;
select @@session.tx_isolation;
這時(shí)兩者的隔離級別全是讀提交
。
4. 事務(wù)隔離級別的演示
4.1 讀未提交(Read Uncommitted)
字面的意思來理解,當(dāng)一個(gè)客戶端在事務(wù)中操作時(shí)并未提交事務(wù),另一個(gè)客戶端能讀到該客戶端操作的數(shù)據(jù)。
定義:在讀未提交隔離級別下,一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)尚未提交的數(shù)據(jù)。這是最低的隔離級別,允許臟讀(Dirty Reads)。
優(yōu)點(diǎn):提供最高的并發(fā)性能,因?yàn)槭聞?wù)之間幾乎沒有阻塞。
缺點(diǎn):可能會出現(xiàn)臟讀,即讀取到其他事務(wù)未提交的數(shù)據(jù)。可能會出現(xiàn)不可重復(fù)讀(Non-repeatable Reads)和幻讀(Phantom Reads)。
幾乎沒有加鎖,雖然效率高,但是問題太多,嚴(yán)重不建議采用
舉例:
創(chuàng)建兩個(gè)會話。左側(cè)為客戶端1,右側(cè)為客戶端2。
設(shè)置讀未提交
隔離級別:
set session transaction isolation level read uncommitted;
在客戶端1插入數(shù)據(jù),客戶端2查詢數(shù)據(jù)。
# 客戶端1insert into students values(1, '李明', 18);# 創(chuàng)建保存點(diǎn)savepoint p1;# 客戶端2select * from students;
顯然,在客戶端1執(zhí)行命令后未使用commit提交,在客戶端2就可以直接查詢出操作結(jié)果。
一個(gè)事務(wù)在執(zhí)行中,讀到另一個(gè)執(zhí)行中事務(wù)的更新(或其他操作)但是未commit的數(shù)據(jù),這種現(xiàn)象叫做臟讀(dirty read)
在客戶端1模擬崩潰;然后在客戶端2查詢數(shù)據(jù)
# 客戶端1`CRRL + D`# 客戶端2select * from students;
很容易看出,當(dāng)客戶端1沒有提交時(shí)系統(tǒng)崩潰后就會回滾到事務(wù)的開始,事務(wù)中所有的操作都會被撤銷。
4.2 讀已提交(Read Committed)
字面的意思來理解,當(dāng)一個(gè)客戶端在事務(wù)中操作時(shí)并提交事務(wù)后,另一個(gè)客戶端才能讀到該客戶端操作的數(shù)據(jù)。
定義:在讀已提交隔離級別下,一個(gè)事務(wù)只能讀取到另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。事務(wù)在每次讀取數(shù)據(jù)時(shí)都會看到最新的已提交數(shù)據(jù)。
優(yōu)點(diǎn):避免了臟讀。提供了較好的并發(fā)性能。
缺點(diǎn):可能會出現(xiàn)不可重復(fù)讀,即在同一事務(wù)中多次讀取同一數(shù)據(jù)時(shí),可能會得到不同的結(jié)果。可能會出現(xiàn)幻讀。
舉例:
創(chuàng)建兩個(gè)會話。左側(cè)為客戶端1,右側(cè)為客戶端2。
設(shè)置讀已提交
隔離級別:
set session transaction isolation level read committed;
在客戶端1插入數(shù)據(jù),客戶端2查詢數(shù)據(jù)。
# 客戶端1insert into students values(1, '李明', 18);# 客戶端2select * from students;
可以發(fā)現(xiàn),客戶端1插入數(shù)據(jù)后在客戶端2并不能查詢到插入的數(shù)據(jù)。
這是為什么呢?
答:讀已提交隔離級別,一個(gè)事務(wù)只能讀取到另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù),在事務(wù)未提交之前其他的客戶端時(shí)不能讀取到的。
在客戶端1插入數(shù)據(jù)并提交,客戶端2查詢數(shù)據(jù)。
# 客戶端1insert into students values(2, '諸葛亮', 20);commit;# 客戶端2select * from students;
顯然,當(dāng)客戶端1提交事務(wù)后客戶端2就能讀到數(shù)據(jù)了。
4.3 可重復(fù)讀(Repeatable Read)
4.3.1 為什么要有可重復(fù)讀?
讀已提交
不是已經(jīng)夠完美了嗎,為什么還要有可重復(fù)讀
呢?
答:讀已提交
只是看上去很方便,但是在實(shí)際的工作中會有很大的問題,主要的問題就在于在事務(wù)中能讀到其他已經(jīng)提交的事務(wù)。
比如,學(xué)校要給考試成績好的學(xué)生發(fā)獎(jiǎng)品,工作人員在篩選成績的同時(shí),另一工作人員發(fā)現(xiàn)第9名同學(xué)的成績多算分了,正在減分。如圖:
通過上圖可知,小明的名字篩選出兩次,那么生成的表中會有兩個(gè)成績不一樣的名字。這肯定是不合適的。
4.3.2 可重復(fù)讀
定義:在可重復(fù)讀隔離級別下,一個(gè)事務(wù)在執(zhí)行期間多次讀取同一數(shù)據(jù)時(shí),結(jié)果始終相同,即使其他事務(wù)對這些數(shù)據(jù)進(jìn)行了修改并提交。這是 MySQL 的默認(rèn)隔離級別。
優(yōu)點(diǎn):避免了臟讀和不可重復(fù)讀。提供了較高的數(shù)據(jù)一致性。
缺點(diǎn):可能會出現(xiàn)幻讀,即在同一事務(wù)中多次執(zhí)行相同的查詢,可能會出現(xiàn)新的記錄。
舉例:
創(chuàng)建兩個(gè)會話。左側(cè)為客戶端1,右側(cè)為客戶端2。
設(shè)置可重復(fù)讀
隔離級別:
set session transaction isolation level repeatable read;
在客戶端1插入數(shù)據(jù),客戶端2查詢數(shù)據(jù)。
# 客戶端1insert into students values(3, '李白', 20);# 客戶端2select * from students;
可以發(fā)現(xiàn),客戶端1插入數(shù)據(jù)后在客戶端2并不能查詢到插入的數(shù)據(jù)。
在客戶端1插入數(shù)據(jù)后提交事務(wù),客戶端2查詢數(shù)據(jù)。
# 客戶端1insert into students values(4, '嫦娥, 18);commit;# 客戶端2select * from students;
客戶端1把事務(wù)提交后客戶端2為什么還是不能查詢到數(shù)據(jù)呢?
答:在可重復(fù)讀隔離級別下,一個(gè)事務(wù)在執(zhí)行期間多次讀取同一數(shù)據(jù)時(shí),結(jié)果始終相同,即使其他事務(wù)對這些數(shù)據(jù)進(jìn)行了修改并提交。只用把自己的事務(wù)提交后,才能查詢到其他已提交事務(wù)更改后的數(shù)據(jù)。
提交客戶端2的事務(wù)后再進(jìn)行查詢
# 客戶端2
#提交事務(wù)
commit;# 查詢
select * from students;
顯然,當(dāng)客戶端2提交事務(wù)后就可以查詢到客戶端已插入的數(shù)據(jù)。
4.3.3 說明
多次查看,發(fā)現(xiàn)終端A在對應(yīng)事務(wù)中insert
的數(shù)據(jù),在終端B的事務(wù)周期中,也沒有什么影響,也符合可重復(fù)的特點(diǎn)。
但是,一般的數(shù)據(jù)庫在可重復(fù)讀情況的時(shí)候,無法屏蔽其他事務(wù)insert
的數(shù)據(jù)。
為什么?因?yàn)楦綦x性實(shí)現(xiàn)是對數(shù)據(jù)加鎖完成的,而insert
待插入的數(shù)據(jù)因?yàn)椴⒉淮嬖?#xff0c;那么一般加鎖無法屏蔽這類問題,會造成雖然大部分內(nèi)容是可重復(fù)讀的,但是insert的數(shù)據(jù)在可重復(fù)讀情況被讀取出來,導(dǎo)致多次查找時(shí),會多查找出來新的記錄,就如同產(chǎn)生了幻覺。
這種現(xiàn)象,叫做幻讀(phantom read)。很明顯,MySQL在RR級別的時(shí)候,是解決了幻讀問題的
(解決的方式是用Next-Key鎖(GAP+行鎖)解決的。這塊比較難,有興趣同學(xué)了解一下)。
4.4 序列化(Serializable)
序列化也叫做串行化。
定義:在序列化隔離級別下,事務(wù)被完全隔離,每個(gè)事務(wù)都按順序執(zhí)行,如同在單線程環(huán)境中一樣。這是最高的隔離級別,確保了數(shù)據(jù)的一致性,但并發(fā)性能較差。
優(yōu)點(diǎn):避免了臟讀、不可重復(fù)讀和幻讀。提供了最高的數(shù)據(jù)一致性。
缺點(diǎn):并發(fā)性能較低,因?yàn)槭聞?wù)之間會有更多的阻塞和等待。
舉例:
創(chuàng)建兩個(gè)會話。左側(cè)為客戶端1,右側(cè)為客戶端2。
-
場景1:兩個(gè)客戶端都查詢數(shù)據(jù),客戶端2提交事務(wù),然后客戶端1提交事務(wù)。
設(shè)置序列化隔離級別,開啟事務(wù)set session transaction isolation level serializable;
客戶端1和客戶端2都對表進(jìn)行查詢(此時(shí)兩個(gè)事務(wù)都對表進(jìn)行了加鎖)
# 客戶端1 select * from students;# 客戶端2 select * from students;
客戶端1進(jìn)行插入表操作時(shí)發(fā)生
鎖等待
客戶端1輸入插入命令按回車鍵后,光標(biāo)在命令的下面,說明該命令正在等待執(zhí)行,這是為什么呢?答:在
序列化
隔離級別下,產(chǎn)生鎖是為了確保數(shù)據(jù)的一致性;事務(wù)中的命令只要對表進(jìn)行查詢,就會鎖住表,但是其他事務(wù)中的命令對此表也可以進(jìn)行鎖住;表只有被一個(gè)事務(wù)鎖住時(shí)才能進(jìn)行修改操作。此時(shí)的表被兩個(gè)事務(wù)鎖住了,客戶端1想進(jìn)行修改操作只有先讓客戶端2把事務(wù)提交(解開對表的鎖)。
客戶端2對表解鎖:
# 客戶端2 commit;
-
場景2:兩個(gè)客戶端都查詢數(shù)據(jù),然后都插入數(shù)據(jù)
設(shè)置序列化隔離級別,開啟事務(wù)set session transaction isolation level serializable;
客戶端1和客戶端2都對表進(jìn)行查詢(此時(shí)兩個(gè)事務(wù)都對表進(jìn)行了加鎖)
# 客戶端1 select * from students;# 客戶端2 select * from students;
客戶端1對表進(jìn)行插入,客戶端2對表進(jìn)行插入
# 客戶端1 insert into students values(6,'6',6);# 客戶端2 insert into students values(7,'7',7);
當(dāng)客戶端2對表進(jìn)行插入的時(shí)候發(fā)生死鎖,什么是死鎖呢?
答:兩個(gè)事務(wù)試圖同時(shí)訪問同一個(gè)資源(比如插入同一條記錄,就會發(fā)生死鎖。MySQL會檢測到這種情況,并返回錯(cuò)誤信息,提示用戶重啟其中一個(gè)事務(wù)。當(dāng)客戶端2發(fā)生死鎖的時(shí)候就會被強(qiáng)制重啟事務(wù),此時(shí)讀表的鎖就只有客戶端2了,客戶端1對表的操作就可以進(jìn)行了。
-
場景3:兩個(gè)客戶端都查詢數(shù)據(jù),客戶端1插入數(shù)據(jù)(等待插入數(shù)據(jù))
設(shè)置序列化隔離級別,開啟事務(wù)set session transaction isolation level serializable;
客戶端1和客戶端2都對表進(jìn)行查詢(此時(shí)兩個(gè)事務(wù)都對表進(jìn)行了加鎖)
# 客戶端1 select * from students;# 客戶端2 select * from students;
客戶端1對表進(jìn)行插入,客戶端2不進(jìn)行任何操作
# 客戶端1 insert into students values(6,'6',6);# 客戶端2
執(zhí)行步驟3后不在進(jìn)行任何的操作,過幾分鐘后步驟4會自動彈出,修改表的操作嘗試獲取鎖時(shí)超過了等待時(shí)間限制,導(dǎo)致事務(wù)無法繼續(xù)執(zhí)行。
5. 總結(jié)
- 其中隔離級別越嚴(yán)格,安全性越高,但數(shù)據(jù)庫的并發(fā)性能也就越低,往往需要在兩者之間找一個(gè)平
衡點(diǎn)。 - 不可重復(fù)讀的重點(diǎn)是修改和刪除:同樣的條件, 你讀取過的數(shù)據(jù),再次讀取出來發(fā)現(xiàn)值不一樣了
- 幻讀的重點(diǎn)在于新增:同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣
- 說明:mysql 默認(rèn)的隔離級別是可重復(fù)讀,一般情況下不要修改
- 上面的例子可以看出,事務(wù)也有長短事務(wù)這樣的概念。事務(wù)間互相影響,指的是事務(wù)在并行執(zhí)行的時(shí)候,即都沒有commit的時(shí)候,影響會比較大。
隔離級別 | 臟讀 | 不可重復(fù)讀 | 幻讀 | 加鎖讀 |
---|---|---|---|---|
讀未提交(read uncommit) | YES | YES | YES | NO |
讀已提交(read commit) | NO | YES | YES | NO |
可重復(fù)讀(repeatable) | NO | NO | NO | NO |
序列化(可串行化)(serializable) | NO | NO | NO | YES |
一致性(Consistency) :
- 事務(wù)執(zhí)行的結(jié)果,必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài),變到另一個(gè)一致性狀態(tài)。當(dāng)數(shù)據(jù)庫只包含事務(wù)
成功提交的結(jié)果時(shí),數(shù)據(jù)庫處于一致性狀態(tài)。如果系統(tǒng)運(yùn)行發(fā)生中斷,某個(gè)事務(wù)尚未完成而被迫中
斷,而改未完成的事務(wù)對數(shù)據(jù)庫所做的修改已被寫入數(shù)據(jù)庫,此時(shí)數(shù)據(jù)庫就處于一種不正確(不一
致)的狀態(tài)。因此一致性是通過原子性來保證的。 - 其實(shí)一致性和用戶的業(yè)務(wù)邏輯強(qiáng)相關(guān),一般MySQL提供技術(shù)支持,但是一致性還是要用戶業(yè)務(wù)邏輯
做支撐,也就是,一致性,是由用戶決定的。 - 而技術(shù)上,通過AID保證C
推薦閱讀 :
如何實(shí)現(xiàn)事務(wù)的隔離性
Innodb中的事務(wù)隔離級別和鎖的關(guān)系
MySQL間隙鎖原理