揭露資料庫隔離級別的骯髒祕密:可序列性與“嚴格”可序列化區別! - Matt Freels
多年來,“可序列化/序列化”(serializability)被稱為資料庫隔離級別的“黃金標準 ”。它是絕大多數商業資料庫系統中提供的最高隔離級別,一些高度廣泛部署的系統甚至無法提供 隔離級別與可序列化一樣高。
在這篇文章中,我們展示了可序列化的可行性,並且說明它從未成為資料庫系統的“黃金標準”。事實上,嚴格的可序列化才一直是黃金標準。正如我們將看到的,因為在遺留資料庫系統中實現可序列化提供了關鍵的嚴格保證,所以“可序列化”和“嚴格可序列化”之間的區別大多被忽略了。然而,在以云為中心的分散式系統的現代世界中,這種差異是顯著的。
這篇文章首先解釋了資料庫系統中隔離的概念,然後說明了這兩個隔離級別之間的重要差異,以及應用程式設計師需要注意的“僅僅”引入可序列化系統導致的正確性bug。
背景:什麼是“隔離”
假設我們有一個售票應用程式,允許人們購買限量供應活動的門票。應用程式將以下形式的事務傳送到資料儲存(當客戶嘗試買票時):
如果庫存> 0那麼 庫存=庫存-1; 過程(順序) 返回成功 否則 返回失敗FAIL
如果資料儲存支援ACID事務,則將以原子方式處理上述程式碼: 將處理整個訂單並減少庫存,或者訂單與庫存兩者都不處理。要麼全部處理要麼全部失敗,除此以外其他任何事情都不可能發生。
如果系統中只執行一個事務,則顯然執行上述程式碼不會出現任何問題。如果庫存足夠,則會處理訂單。否則,它不會。
問題通常僅在併發下發生:多個客戶嘗試同時購買一張票。如果有更多客戶試圖購買門票而不是庫存,理想的最終結果是所有庫存都將被出售,而不是更多。
如果允許上述程式碼在沒有系統級保證的情況下執行,則程式碼中存在明顯的“競爭條件”。兩個並行執行緒可以同時執行第一行,並且都看到庫存為“1”。因此,兩個執行緒都通過IF條件並處理訂單,這導致庫存超賣的負面結果。
系統級保證在面對併發請求時防止這些負面結果屬於ACID的I - “隔離”。
隔離的黃金標準是“可序列化serializability”。保證可序列化的系統能夠同時處理事務,但保證最終結果等同於單獨處理每個事務時發生的事情,一個接一個地處理(序列就好像沒有併發)。這是一個非常強大的保證,它能夠經受住時間的考驗(50年),從而能夠在其上構建健壯且無錯誤的應用程式。
可序列化隔離如此強大的原因是應用程式開發人員根本不需要推理併發性。開發人員只需單獨地關注單個事務的邏輯正確性即可。只要每個單獨的事務不能違反應用程式的語義,開發人員就可以確保同時執行其中許多事務也不會違反應用程式的語義。在我們的示例中,我們只需要確保上面顯示的6行程式碼的正確性。只要在資料庫的任何啟動狀態下處理它們時它們都是正確的,那麼應用程式在併發時將保持正確。
可序列化的髒資料祕密:它看起來並不像“安全”
儘管具有令人難以置信的可序列化能力,但確實存在更強的保證,並且保證“僅僅”依靠可序列化的資料庫實際上容易出現與併發性相關的其他型別的錯誤。
可序列化的第一個限制是:它不限制如何選擇等效的事務序列順序。指定一組併發執行的事務,系統保證它們將被等效地處理為序列順序,但它不保證任何特定的序列順序。
結果,指定一組相同的併發事務處理的兩個副本可能最終處於非常不同的最終狀態,因為他們選擇以不同的等效序列順序處理事務。因此,“僅僅”依靠可序列化的資料庫的複製不能只複製輸入並讓每個複製事務程序同時處理輸入就可以了,這不能保證複製。
相反,一個複製的程序必須首先處理工作負載,然後處理來自原初始程序發生的一系列狀態改變的複製,這個狀態改變的複製是原初始程序通過網路釋出的,因此,資料經過網路延遲到達時間和原始程序中原始資料發生的時間有滯後性。
可序列化的第二個限制是:可序列化系統選擇的等同序列順序不必與提交給系統的事務的順序相關,。在“事務X之後提交的事務Y“與“Y在在X之前處理”是符合等效序列順序的。
即使在X完成後的幾周內提交了Y,這也是正確的 , 理論上可以為可序列化系統帶來Y及時返回,並在X存在之前處理系統狀態(只要沒有提交的事務讀取Y寫入的相同資料),從技術上講每個只讀事務都可以返回空集(即資料庫的初始狀態),並且不會違反序列化保證,這是合法的,因為系統可以使只讀事務“及時”返回,並在寫入資料B的任何事務之前以序列順序獲得一個卡位。
任何使用者都不太可能使用始終為每個只讀查詢返回空集的資料庫。那麼可序列化如何成為“黃金標準”?
在過去,資料庫只能在一臺機器上執行。因此,資料庫系統保證隔離級別保證更強一致性要超過可序列化serializability。事實上,以至於多年來沒有人為這個強大的隔離保證保證(即使幾乎每個資料庫系統都保證它)。隔離級別最終被Herlihy和Wing 命名為: 嚴格可序列化。
嚴格的可序列性更安全
嚴格的可序列化在普通可序列化之上添加了一個簡單的額外約束。如果事務Y在事務X完成後啟動(請注意,這意味著X和Y根據定義不是併發的),那麼保證嚴格可序列化的系統保證:
(1)最終狀態等同於按順序處理事務和
(2)X必須在該序列順序中的Y之前。
因此,如果我要向保證嚴格可序列化的系統提交事務,我知道該事務的任何讀取都將反映由於已提交的事務(至少)到達我的事務處理時的資料庫的狀態。提交。事務可能看不到同時提交的事務的寫入,但至少它會看到在它開始之前完成的所有寫入。
當資料庫位於一臺機器上時,保證嚴格的可序列化通常不會增加相對於普通可序列化的工作量。在進行交易之前,每個現有系統(一個例外是Daniel Abadi博士的2014年“懶惰交易”論文 )必須執行該交易事務中涉及的寫入。對於稍後出現的事務,忽略這些寫入(而不是看到它們)通常比看到它們更多的工作。因此,幾乎每個在一臺機器上執行的可序列化系統也保證了嚴格的可序列化。這導致了行業現狀,資料庫系統的文件表明它們保證了“可序列性”,但實際上,它們保證了“嚴格的可序列性”。
“嚴格的可序列化”保證消除了系統返回我們在上一節中討論過的陳舊/空資料的可能性。[但是,它沒有消除我們在該部分討論過的副本分歧問題。我們將在以後的帖子中回到這個問題。]
總結到目前為止:實際上,“嚴格可序列化”是資料庫系統中的黃金標準隔離級別。但是,只要大多數資料庫在一臺機器上執行,在保證“可序列化”的系統和保證“嚴格可序列化”的系統中沒有可察覺的差異。因此,通俗地說,“可序列化”被稱為黃金標準,這種口語化的語義不準確性從未如此重要。
分散式系統使可序列性變得危險
在分散式系統中,從可序列化到嚴格可序列化的跳躍不再是微不足道的。
以我們的售票應用程式的簡單示例為例:
假設票證庫存在兩臺機器上覆制 --A和B ----兩者都允許處理交易。客戶向機器A發出減少庫存的事務。此事務完成後,將向計算機B發出另一個事務,以讀取當前庫存。在一臺機器上,第二個事務肯定會讀取第一個事務的寫入。但是現在他們在不同的機器上執行,如果系統不能保證一致的讀取跨機器,第二個事務很可能會返回一個陳舊的髒值(例如,如果複製是非同步的,並且第一個事務的寫入尚未從A複製到B,則可能會發生這種情況)。這種陳舊的讀取不會違反可序列化。它相當於第一個事務之前的第二個事務的序列順序。但是,由於第二次交易事務是在第一次交易完成後提交的,因此肯定違反了嚴格的可序列性。
因此, 當跨機器複製資料時,可序列化和嚴格的可序列化之間存在巨大而實際的差異。在選擇要使用的資料庫系統之前,終端使用者必須意識到這種差異。無法保證嚴格可序列化的分散式系統容易出現許多不同型別的錯誤。
可序列化和嚴格可序列化之間的區別遠遠超過我之前示例中的“陳舊讀取”錯誤。讓我們看看可能出現的另一個錯誤,稱為“因果反轉”。
如果您存入的金額(在您的所有賬戶中)超過5000美元,銀行會“free checking免費檢查”。愛麗絲正好5000美元,需要給孩子的保姆簽發支票,所以她將一些額外的錢轉入她的儲蓄賬戶。在確認轉移成功並且錢存入她的儲蓄賬戶後,她從她的支票賬戶中提取支票。當她看大結果時,她發現因為沒有在5000美元以上的賬戶中保持平衡而受到的處罰。
所以發生了什麼事?違反嚴格的可序列性。兩個交易---她的儲蓄賬戶的增加和她的支票賬戶的扣除被重新排序,以便在儲蓄賬戶新增之前發生支票賬戶扣除,即在加法完成之後提交減法。在僅保證普通可序列化的系統中,這種重新排序是完全可能的,但在保證嚴格可序列化的系統中是不可能的。在這種型別的示例中,這種事務重新排序是非常常見的,其中如果不相交的資料位於分散式系統中的不同機器上,則事務訪問不相交的資料(檢查餘額與儲蓄餘額)。交易事務重新排序後,Alice在兩個賬戶的餘額暫時低於5000美元,這導致了處罰。如果銀行在一個保證嚴格可序列化的系統之上構建他們的應用程式,他們將不必處理來自Alice的憤怒投訴電話。
FaunaDB:所有交易的嚴格可序列化
很少有分散式資料庫系統聲稱可以保證嚴格的可序列化的隔離級別。實際上,只需更少的產品即可實現這一保證。FaunaDB就是其中之一。
FaunaDB現在支援配置,使所有事務 - 包括讀寫和只讀事務 - 以嚴格可序列化的隔離執行。
FaunaDB對嚴格可序列化的保證也是市場上最強大的。由於嚴格可序列化的定義基於實時(如上所述:在實時完成其他事務後提交的事務必須遵守其各自的順序),其他供應商在分散式系統中的不同機器上使用本地時鐘為了執行保證。遺憾的是,基於此方法的方法僅與跨機器的時間同步協議一樣好,並且當同步演算法暫時超過對最大時鐘偏差的假設時,容易發生拐角情況違規。FaunaDB實現嚴格可序列化的方法也適用於多區域環境,並且類似於通過其可擴充套件的分散式統一日誌實現一致性的方式。我們將在以後的文章中深入探討技術細節。
最重要的是,如果將FaunaDB配置為對所有事務嚴格可序列化,那麼過時的讀取將永遠不會發生。應用程式發出的每個讀取都保證反映在發出讀取之前已完成的事務的寫入。無論FaunaDB部署中機器的本地時鐘存在多少偏差,都是如此。即使一臺機器認為當年是1492年,該機器提供的任何讀取都將反映所有已提交的寫入。此外,FaunaDB中的事務永遠不會在完成的事務之前重新排序 - 即使它們觸及位於不同機器上的完全不相交的資料集。因此,我們之前看到的銀行交易重新排序問題保證永遠不會出現在FaunaDB中。
因此,FaunaDB對嚴格可序列化的支援使其成為該市場上最安全的分散式(可擴充套件)資料庫系統。