lightning mdb 源代碼分析(5)-事務控制

本博文系列前面已經探討了LMDB的系統架構、MMAP映射、B-Tree操做等部分,本文將嘗試描述LMDB中的事務控制的實現。數據庫

事務的基本特徵:數據結構

事務是恢復和併發控制的基本單位。它是一個操做序列,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單位。多線程

事務是數據庫維護數據一致性的單位,在每一個事務結束時,都能保持數據一致性。架構

事務應該具備4個屬性:原子性、一致性、隔離性、持久性。這四個屬性一般稱爲ACID特性併發

原子性(atomicity)。一個事務是一個不可分割的工做單位,事務中包括的諸操做要麼都作,要麼都不作。atom

一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另外一個一致性狀態。一致性與原子性是密切相關的。線程

隔離性(isolation)。一個事務的執行不能被其餘事務干擾。即一個事務內部的操做及使用的數據對併發的其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。對象

持久性(durability)。持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其餘操做或故障不該該對其有任何影響。接口

LMDB中的實現基本思路:隊列

Atom(A):LMDB中經過txn數據結構和cursor數據結構的控制,經過將髒頁列表放入dirtylist中,當txn進行提交時再一次性統一刷新到磁盤

中或者abort時都不提交保證事務要不全成功、要不全失敗。對於長事務,若頁面spill到磁盤,由於COW技術,這些頁面未與整棵B-Tree的root

page產生關聯,所以後續的事務仍是不能訪問到這些頁面,一樣保證了事務的原子性。

 

其數據就是一致的,不存在由於多線程同時寫數據致使數據產生錯誤的狀況。

Isolation(I):事務隔離經過鎖控制(MUTEX),LMDB支持的鎖互斥是進程級別/線程級別,支持的隔離方式爲鎖表支持,讀讀之間不鎖,寫等待讀完成以後開始,

讀等待寫完成後開始

Duration(D):LMDB中,沒有使用WAL、undo/redo log等技術來保證系統崩潰時數據庫的可用性,其保證數據持續可用的技術是COW技術和只有一線程寫技術。

假如LMDB或者系統崩潰時,只有讀操做,那麼數據原本就沒有發生變化,所以數據將不可能遭到破壞。假如崩潰時,有一個線程在進行寫操做,則只須要判斷最後的

頁面號與成功提交到數據庫中的頁面號是否一致,若不一致則說明寫操做沒有完成,則最後一個事務寫失敗,數據在最後一個成功的頁面前的是正確的,後續的屬於

崩潰事務的,不能用,這樣就保證了數據只要序列化到磁盤則必定可用,要不其就是尚未遵循ACI原則序列化到磁盤

順便說一句,由於MMAP技術、只一個寫線程的實現方案,因此數據庫進行備份時特別簡單,只要按期在線熱備整個數據庫便可完成。同時恢復也將比較快。固然因爲

其使用了重用舊頁技術,LMDB在恢復時只能恢復到最新狀態,不能恢復到任意時刻。

實現方法:

LMDB支持嵌套事務,不指望在子事務完成以前父事務有任何讀寫操做,這樣的話能夠避免父子事務之間的數據不一致。

LMDB不支持跨線程事務,一個事務只能屬於一個線程,一個線程在任一時刻只能持有一個事務。

mdb_txn_begin:

     開啓一個事務,根據是否傳入父事務判斷是否爲子事務,根據傳入參數判斷是否爲只讀事務。嵌套事務支持只支持一個子事務,且子事務爲寫事務父事務也必須爲寫事務,

並且數據庫不能爲mmap可寫方式。事務開啓流程:分配內存,設置變量,若爲子事務,設置父子相關關聯變量並shadow父親全部cursor以減小IO讀取。不然調用renew0完成

最終的事務開啓工做。

mdb_txn_abort:

    放棄一個事務,有子事務則先放棄子事務,而後調用reset0真正執行結束操做。
mdb_txn_commit:

    提交一個事務,有子事務則先提交子事務,若爲只讀事務,則關閉全部打開的數據庫句柄並保持打開狀態,而後放棄事務便可,若爲可寫事務,肯定事務狀態是否正確,若爲error

狀態,不能夠提交,若不是則根據是否存在父事務進行處理,沒有父事務則首先更新數據庫的root節點,而後保存可重用空間到freedb以便空間重用,並釋放midl空間以後,進行

頁面刷新,同步相關環境變量以後釋放內存,最後釋放寫鎖,至此沒有父事務狀況提交完成。如有父事務,則其進行將midl列表與父事務的midl合併,cursor一樣合併到父事務中進行

最終關閉,將dirtylist合併到父事務中,相關合並和本事務的變量內存釋放完畢以後,子事務提交成功,即子事務主要完成內存釋放,其餘動做如磁盤刷新等都合併至父事務中一次性完成。

mdb_txn_reset:

    放棄一個事務,可是保留句柄,僅對只讀事務有效,一樣調用reset0進行真正事務結束操做。
mdb_txn_reset0 :

    放棄事務的公共代碼.首先關閉事務中打開的數據庫句柄。如果只讀事務,設置事務相關變量便可,若爲可寫事務,須要關閉全部遊標,而後釋放midl空間,最後釋放寫鎖。至此事務

關閉完畢。
mdb_txn_renew:

    重用一個只讀事務句柄,避免一次內存分配,檢查是否有嚴重錯誤,如有失敗,沒有的話調用renew0完成。
mdb_txn_renew0:

    renew0是renew和begin的公共代碼。如果寫事務,申請進程間互斥鎖,如果讀事務,首先檢查本線程是否已經有讀事務,有不支持返回錯誤,沒有的話,開始申請讀表互斥鎖,

成功後將線程id記錄到讀表裏面,而後馬上釋放讀表鎖。而後再次確認線程中確有事務。事務(讀寫)申請成功後,將env的meta頁面根據txnid進行切換,輪流使用。

最後再次設定些變量後通知調用者申請成功。
mdb_txn_env:

   返回事務關聯的env對象

    上文解釋了LMDB實現事務控制的方式和主要接口方法的基本流程,若實現相似關係型數據庫的細粒度事務,則須要更細粒度的鎖以及複雜的頁面等待隊列機制等以保證行鎖或表鎖

的正確性並最終實現事務控制機制,且在數據庫應用時有可能陷入死鎖狀態,而在LMDB當中,讀寫鎖分開,且進程崩潰時,系統會釋放相關內核變量,從而保證要不進程正常,

鎖成功釋放,要不進程崩潰,系統釋放鎖,所以數據庫永遠不會陷入死鎖狀態,不過若事務在等待寫鎖,有可能等待較長時間。

    但願各位能積極批評指正以及轉載。

相關文章
相關標籤/搜索