閒話MVCC

爲什麼需要事務

其實和我們需要是一個道理。因爲我們業務上的一個邏輯操作單元Tx_A,對應的是多個計算機指令。我們需要一種機制來確保在Tx_A操作執行期間,其內部狀態不可以被外界觀測到。如果外界訪問,要麼阻塞等待直到該操作結束。要麼直接返回告訴它,“Sorry ,I’m busy now”。事務就是這樣一種機制。做數據庫的大神說,“得了,您也別自己去實現這樣一種機制了,就我實現得了。你們會用就行,免得一大堆bug”,所以事務的出現使得我們能更好focus在我們的業務上。而不用自己去實現事務等價一套APIs。
事務是一個說明性的概念,這個概念用來表述你想要達到什麼樣的效果。

Mysql對外暴露的事務APIs如下:

  • 1.事務的開始
    • START TRANSACTION
    • BEGIN
  • 2.事務提交
    • COMMIT
  • 3.事務回滾
    • ROLLBACK

ANSI/ISO isolation(隔離級別)

一言以蔽之,隔離級別是事務之間可見性的定義。

1. 可序列化(Serializable)

可序列化涉及到範圍鎖(主要是用於解決聚集查詢),所以沒有幻讀問題。

2.可重複讀(READ-REPEATABLE)

在可重複讀取(REPEATABLE READS)級別下,數據庫系統會在整個事務期間保持讀取鎖寫入鎖。但相較於可序列化,範圍鎖不會持有,所以幻讀可能會出現。

Ps:MySQL的MVCC兼容此隔離級別,並且避免了幻讀問題。

3.不可重複讀(READ-COMMITTED)

在READ COMMITTED級別下,整個事務期間保持寫入鎖,但讀取鎖會在SELECT執行之後立即釋放。所以也稱爲NON-REPEATABLE READ。

4.讀未提交(READ-UNCOMMITED)

在此隔離級別下,髒讀可能會出現。
Ps:此處需要注意髒讀含義的理解。
事務A如果可以看見其它事務的中間狀態,那麼就是髒讀。換句話說,一個事務能看見的數據,要麼在其它事務開始之前,要麼是之後。事務是將數據庫狀態從某個一致的狀態,變成另一種一致的狀態。

小結

以MySql爲例,大多數應用採用的隔離級別爲REPEATABLE READ,所以SELECT讀到的數據可能不是目前最新的(可見性?)。其實對大多數業務來說都OK,因爲大多數業務並不要求全局的happen-before關係,而只要求某幾個操作之間有。

MVCC

以隔離級別「READ-REPEATABLE」爲例,按照上面「讀寫鎖」的實現,那麼讀寫依然是衝突的,能不能達到讀寫也不衝突呢?畢竟人類對性能的追求是無止境的。答案是肯定的。其思想上和Java的CopyOnWriteArrayList極爲類似。簡單來說
就是通過”volatile+寫鎖”實現。這樣就可以實現
讀寫不互斥(通過volatile);
還是以經典的Bob給Simith轉賬100刀爲例.

事務A
start
update account set balance=balance where id=’bob’ and balance >=100
commit;
事務B
start;
select balance where id=’bob’
commit;
現在假設事務A和事務B並行執行,照例說事務B,應該會等事務A提交時候( 提交會釋放鎖),之後纔會讀到bob賬戶餘額,事實上由於讀餘額時,並不會嘗試獲取寫鎖,所以無需等待。所以讀寫不互斥。
Ps:肯定有同學會疑惑,這樣不就是讀得不是最新的了嘛?的確,這樣讀到的數據不是最新的,但是爲什麼需要讀到最新的呢,對大多讀事務場景,並不需要讀到最新值啊,如果是寫事務的話,那麼對業務屬性是否滿足更新條件,必須放在update的where子句中,而不能先select後update,當然,對大多數應用,先selelct 如果(不)滿足,然後(不)更新,如果大多數條件都不用更新,那麼這麼寫,可能會有性能優化,畢竟select 無鎖,update可是必須要鎖的。

顯示鎖

隱式鎖

  • select 隱式獲取與釋放讀鎖

寫時複製(COW(Copy-On-Write))的

參考資料

發佈了59 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章