PG資料庫事務隔離級別
Postgres資料庫事務隔離級別介紹
0. What is Database Transaction?
資料庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢復並使應用程式更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和永續性)屬性。事務是資料庫執行中的一個邏輯工作單位,由DBMS中的事務管理子系統負責事務的處理。 ——
[百度百科]
- A –Atomicity
事務必須是原子工作單元;要麼全都執行成功,要麼全都執行不成功。通常,與某個事務關聯的操作具有共同的目標,並且是相互依賴的。如果系統只執行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統處理操作子集的可能性。 - C –Consistency
一致性是指事務必須使資料庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
拿轉賬來說,假設使用者A和使用者B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個使用者的錢相加起來應該還得是5000,這就是事務的一致性。 - I –Isolation
隔離性是當併發訪問資料庫時,資料庫每一個事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離
即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始之前就已經結束,要麼在T1結束之後才開始,這樣每個事務都感覺不到有其他事務在併發地執行。 - D –Durability
事務完成之後,它對於系統的影響是永久性的,該修改即使出現致命的系統故障也將一直保持。
1. Why Isolation is important for DataBase?
如果不考慮事務隔離性,可能導致以下幾種嚴重的問題
(1) 髒讀
髒讀是指在一個事務處理過程裡讀取了另一個未提交的事務中的資料。
當一個事務正在多次修改某個資料,而在這個事務中這多次的修改都還未提交,這時一個併發的事務來訪問該資料,就會造成兩個事務得到的資料不一致。例如:使用者A向用戶B轉賬100元,對應SQL命令如下。
update account set money = money + 100 where name = 'B'; (此時A通知B)
update account set money = money - 100 where name = 'A';
當只執行第一條SQL時,A通知B檢視賬戶,B發現確實錢已到賬(此時即發生了髒讀),而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麼當B以後再次檢視賬戶時就會發現錢其實並沒有轉。
(2) 不可重複讀
不可重複讀是指在對於資料庫中的某個資料,一個事務範圍內多次查詢卻返回了不同的資料值,這是由於在查詢間隔,被另一個事務修改並提交了。
例如事務T1在讀取某一資料,而事務T2立馬修改了這個資料並且提交事務給資料庫,事務T1再次讀取該資料就得到了不同的結果,傳送了不可重複讀。
不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另一個事務未提交的髒資料,而不可重複讀則是讀取了前一事務提交的資料。
在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個資料當然以最後查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個資料A和B依次查詢就可能不同,A和B就可能打起來了……
(3) 幻讀
幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”並且提交給資料庫。而操作事務T1的使用者如果再檢視剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中新增的,就好像產生幻覺一樣,這就是發生了幻讀。
幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。
為了解決併發導致的不一致問題(髒讀/不可重複讀/幻讀),SQL標準提出了四種事務隔離級別
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | 可能 | 可能 | 可能 |
讀已提交 | 不可能 | 可能 | 可能 |
可重複讀 | 不可能 | 不可能 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 |
2. How Postgres handle with isolation?
在PostgreSQL中,你可以請求四種標準事務隔離級別中的任意一種。 但是在內部,實際上只有三種不同的隔離級別,分別對應級別讀已提交、可重複讀和可序列化。但你你選擇了讀未提交級別,實際上你得到的是讀已提交,並且在PostgreSQL的可重複讀實現中幻讀是不可能出現的,所以實際的隔離級別可能比你選擇的更嚴格。這是 SQL 標準允許的: 四種隔離級別只定義了哪種現像不能發生,但是沒有定義哪種現像一定發生。PostgreSQL只提供三種隔離級別的原因是, 這是把標準的隔離級別對映到多版本併發控制架構的唯一合理方法。 可用的隔離級別的行為在下面小節中詳細描述。
要設定一個事務的事務隔離級別,使用SET TRANSACTION命令。
重要: 某些PostgreSQL資料型別和函式關於事務的行為有特殊的規則。特別是,對一個序列的修改(以及用serial宣告的一列的計數器)是立刻對所有其他事務可見的,並且在作出該修改的事務中斷時也不會被回滾。
(1) 讀已提交隔離級別
讀已提交是PostgreSQL中的預設隔離級別。 當一個事務執行使用這個隔離級別時, 一個查詢(沒有FOR UPDATE/SHARE子句)只能看到查詢開始之前已經被提交的資料, 而無法看到未提交的資料或在查詢執行期間其它事務提交的資料。實際上,SELECT查詢看到的是一個在查詢開始執行的瞬間該資料庫的一個快照。不過SELECT可以看見在它自身事務中之前執行的更新的效果,即使它們還沒有被提交。還要注意的是,即使在同一個事務裡兩個相鄰的SELECT命令可能看到不同的資料, 因為其它事務可能會在第一個SELECT開始和第二個SELECT開始之間提交。
讀已提交模式提供的部分事務隔離對於許多應用而言是足夠的,並且這個模式速度快並且使用簡單。 不過,它不是對於所有情況都夠用。做複雜查詢和更新的應用可能需要比讀已提交模式提供的更嚴格一致的資料庫檢視。
(2) 可重複讀隔離級別
可重複讀隔離級別只看到在事務開始之前被提交的資料;它從來看不到未提交的資料或者並行事務在本事務執行期間提交的修改(不過,查詢能夠看見在它的事務中之前執行的更新,即使它們還沒有被提交)。這是比SQL標準對此隔離級別所要求的更強的保證,並且阻止髒讀/不可重複讀/幻讀 所有現象。如上面所提到的,這是標準特別允許的,標準只描述了每種隔離級別必須提供的最小保護。
這個級別與讀已提交不同之處在於,一個可重複讀事務中的查詢可以看見在事務開始時的一個快照,而不是事務中當前查詢開始時的快照。因此,在一個單一事務中的後續SELECT命令看到的是相同的資料,即它們看不到其他事務在本事務啟動後提交的修改。
可重複讀模式提供了一種嚴格的保證,在其中每一個事務看到資料庫的一個完全穩定的檢視。不過,這個檢視並不需要總是和同一級別上併發事務的某些序列化(一次一個)執行保持一致。例如,即使這個級別上的一個只讀事務可能看到一個控制記錄被更新,這顯示一個批處理已經被完成但是不能看見作為該批處理的邏輯組成部分的一個細節記錄,因為它讀取空值記錄的一個較早的版本。如果不小心地使用顯式鎖來阻塞衝突事務,嘗試用執行在這個隔離級別的事務來強制業務規則不太可能正確地工作。
(3) 可序列化隔離級別
可序列化隔離級別提供了最嚴格的事務隔離。這個級別為所有已提交事務模擬序列事務執行;就好像事務被按照序列一個接著另一個被執行,而不是並行地被執行。但是,和可重複讀級別相似,使用這個級別的應用必須準備好因為序列化失敗而重試事務。事實上,這個給力級別完全像可重複讀一樣地工作,除了它會監視一些條件,這些條件可能導致一個可序列化事務的併發集合的執行產生的行為與這些事務所有可能的序列化(一次一個)執行不一致。這種監控不會引入超出可重複讀之外的阻塞,但是監控會產生一些負荷,並且對那些可能導致序列化異常的條件的檢測將觸發一次序列化失敗。