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

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

COW(Copy On Write):sql

COW技術背後的思想是拖延技術,基本方法是假若有多個調用者須要訪問的資源,在其初始化的時候是不能區分的,即對於多個調用者來講,這資源就是同樣的。這樣就能夠給每一個數據庫

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

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

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

MVCC(Multiversion concurrency control):工具

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

所以能夠提供最高的併發性。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

相關文章
相關標籤/搜索