lightning mdb 源代碼分析(4)—MVCC/COW

本博文將描述MVCC和cow技術以及LMDB中如何使用以及實現這兩種技術。

COW(Copy On Write):

COW技術背後的思想是拖延技術,基本方法是假如有多個調用者需要訪問的資源,在其初始化的時候是不能區分的,即對於多個調用者來說,這資源就是一樣的。這樣就可以給每個

調用者一個指向資源的指針即可。這種方法一直持續到調用者需要進行修改所需要訪問的資源時,在這個時候,調用者將被分配到一份真正私有的資源拷貝,這樣調用者對資源的任意

改動對於其它調用者來說都是不可見的。所有的以上操作對於調用者來說是透明的,這種方法最大的好處就是假如調用者不修改資源,私有拷貝就不會被創建。因此這種技術對於讀大於

寫的應用場景來說特別合適,比如說虛擬內存與分頁、數據庫、string對象等等,都使用此技術以提升系統性能。

MVCC(Multiversion concurrency control):

MVCC是數據庫系統實現併發控制和一般系統中實現事務性內存控制的一種技術,主要用於併發控制,使用MVCC將讀不會阻塞寫,寫不會阻塞讀,只有兩個線程寫同一行數據可能導致衝突,

因此可以提供最高的併發性。MVCC對於併發寫同一區域的數據可能導致衝突要求事務回滾或者互相等待資源導致死鎖。

對於數據庫系統來說,若一個用戶正在讀數據的同時,另一個用戶正在寫同一個數據,則讀用戶可能讀到寫到一半的數據或者不一致的數據,因此數據庫系統都會使用併發控制。最簡單的方式就是

寫時阻塞所有讀,這就是封鎖技術。MVCC的方式是每個用戶看到的數據是其連接上數據庫時的快照,所有寫操作對數據的改變其他數據庫用戶都不能看到,除非改變已經完成(即事務已提交)。

在MVCC中數據的更新使用delete(標記)+insert實現,因此對於同一行數據可能在數據庫多個地方存在不同版本,舊數據和新數據不在同一個地方(不是就地更新),但只有最後一個版本是最新的,

之前的版本對於之前的事務有效。MVCC的好處是對於讀數據來說,哪怕數據在整個事務過程中被別的用戶修改刪除,其讀到的數據就是剛開始使用數據庫時的那份數據。另外就是其不需要及時刪除

舊數據,這樣避免了系統來回換頁導致性能下降。對於文檔型數據庫來說,還可以優化爲數據存儲在連續區塊,delete+insert可以不更新裏面部分數據,這樣對於後續組裝數據提供最大便利。MVCC

提供了即時的一致性視圖,讀寫隔離,不需要進行封鎖,因此可以提高併發性。

MVCC的圖示:

圖1:事務T1改變數據V1,將其改爲數據V2,在堆中,數據如下圖

圖2:事務T3改變了V2,將其改爲V3,在堆中,數據如下圖:目前事務T2還在活動中,所以V1和V2屬於recently dead狀態,而不是真的dead狀態。

圖3:從可視性而言,事務T0只能看到數據V1。因爲它早於事務T1啓動。

圖4:事務T1提交後,事務T2啓動,此時事務T3尚未啓動,故T2可以看到T1提交後的數據V2。

圖5:事務T3提交後,事務T4啓動,故T4只能看到數據V3。

圖6: 前面說過,當還有事務活動中訪問數據V1和V2,V1和V2的狀態是recently dead。

當T0和T2都結束,已經沒有事務在訪問數據V1和V2了,此時V1和V2爲dead狀態,所以V1和V2都成爲VACUUM的處理對象了。

 

以上幾圖以postgresql的MVCC實現爲例描述了不同事務間讀寫操作過程以及其訪問的數據。對於同時更新來說,主要多了

更新衝突檢測,若更新存在讀寫依賴衝突則,更新失敗,事務必須回滾,若存在互相依賴,則會解鎖某一個事務,以避免死鎖。

 

MVCC/COW在LMDB中的實現

LMDB對MVCC加了一個限制,即只允許一個寫線程存在,從根源上避免了寫寫衝突,當然代價就是寫入的併發性能下降。因爲只有

一個寫線程,所以不會不需要wal日誌、讀寫依賴隊列、鎖隊列等一系列控制併發、事務回滾、數據恢復的基礎工具。MVCC的基礎

就是COW,對於不同的用戶來說,若其在整個操作過程中不進行任何的數據改變,其就使用同一份數據即可,若需要進行改變,比如

增加、刪除、修改等,就需要在私有數據版本上進行,修改完成提交之後纔給其他事務可見。

LMDB中,數據操作的基本單元是頁,因此cow也是以頁爲單位,對應函數是mdb_page_touch,mdb_page_copy,copy真正實現頁面複製,

touch調用copy完成複製,然後修改pgno後插入到B+Tree當中,這樣對於此次事務,後續的操作訪問的數據頁就是最新的數據頁面,而非

事務啓動時對應的數據頁面,且此頁面與其他頁面的關聯關係僅在本事務頁面列表中可見,對其他事務不可見。

實際上通過以上兩個函數也實現了MVCC的核心,對於讀寫的控制,通過mdb_txn_begin控制,在其中,事務啓動時會檢查讀寫鎖的情況,

若事務需要更新數據,則會被阻止,若只是讀數據,則不管是否有寫事務存在,讀鎖都可以獲得。

MVCC的一個副作用就是對於存在大量寫的應用,其數據版本很多,因此舊數據會佔用大量空間,postgresql解決此問題通過vacuum命令,

LMDB中通過freedb解決,即將不再使用的舊的數據頁面空間插入到一顆b-Tree當中,這樣舊空間在所有事務不再訪問之後就可以被LMDB

使用,從而避免了需要定期執行清理操作。當然其副作用是數據只能保持最新不能恢復到任意時刻,未執行vacuum之前,保存所有版本的數據庫

可以恢復到任意時刻。

 

本文參考瞭如下資料,在此一併表示感謝。

https://en.wikipedia.org/wiki/Multiversion_concurrency_control

http://www.kuqin.com/system-analysis/20120319/319108.html

http://blog.chinaunix.net/uid-20726500-id-4040024.html

http://www.cnblogs.com/gaojian/p/3295951.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章