1. 程式人生 > >沈洵:分散式事務原理與實踐之單機事務

沈洵:分散式事務原理與實踐之單機事務

tips:容易理解的模型效能往往效能不好,效能好的模型往往不容易理解

什麼事事務?

事務看起來很簡單,就三個命令begin transaction,commit,rollback,但是如果沒有對這個的原理了解的話,就不會取捨。

事務的簡介

事務的核心是鎖與併發,同步控制。

優勢:方便理解

劣勢:效能較低

計算機就像是一個標準的打字機,cpu單位時間內只能做一件事,要麼讀,要麼算,要麼寫,每個cpu只在單位時間做一件事。

version=version+1;一共為三步,先取出資料,加1,再寫回去。而事務就是在讀寫算外再額外提供了一個同步塊。

資料庫的事務本質來說跟應用做的synchronize 和lock等同步是一樣的,沒有本質的差異,

多個人操作同一塊資料的時候,得保證只有一個執行緒進入事務。

事務單元

一段業務程式碼的邏輯,希望它就像我寫出來的那樣去執行,執行這段邏輯的過程中的時候,外邊的人不參與這段邏輯執行,我有幾個操作看起來是原子操作,不論有多少步,對於應用來說都是一步,通過一個bebgin transaction  comit rollcbck 之間的操作都應該加同步訪問。


多個事務單元的情況,都有史密斯,其他事務都得排隊,這是一種事務單元一種非常重要的訪問控制的方式:鎖的方式

一組事務單元

Bob給Joe100塊

Joe給Simith100塊

Simith給Bob100塊


事務單元之間有很多不同的關係,事務和事務之間有哪些關係,如果需要有一種方式來表述,那就是happen before的四種可能性。

事務單元之間的happen-before關係四種可能性,也就是事務衝突的可能性分別為

讀寫

寫讀

讀讀

寫寫

所有事務衝突的可能性可以抽象為這四種關係

在這之上還有個問題:

如何能夠以最快的速度完成這些事務單元之間的關係?又能保證上面四種操作的邏輯順序?有沒有可能儘可能的讓事務單元不出現衝突?

假設一個事務耗時1秒,60個事務序列要60秒,但如果並行的化只要1秒。

事務之間沒有關聯是最快的。

阿木大二定律,如果事務之間沒有相關性,所有的事務都可以並行,系統的效能理論上是最優的。

要保證系統之間的這四種happen before的關係在邏輯上是有序的又想辦法保證效能又要保證看起來一樣,也就是提高系統的易用性的同時,又儘可能不損失系統的效能,尤其是不損失系統的吞吐。

除了轉賬這樣的操作,有一些操作也是一個事務單元這也是一個事務單元

商品要建立一個基於GMT_modified的索引,alter table add index ...,原來的資料讀一遍,在新的表寫一遍,在讀使用一張表的時候,要讓原來的表不變,這樣才能資料一致

從資料庫中讀取一行記錄

向資料庫中寫入一行記錄,同時更新這行記錄的所有索引。

刪除整張表

所以說資料庫有很多的事務單元。

如何實現事務單元?

Two Phase lock,兩段提交協議。

所有的資料庫操作CRUD都是先讀再寫的操作,比如insert和delete,update都是先查再寫的操作,每次查出資料時候資料庫會再上面加鎖。

對於insert讀取資料,判斷資料又沒有,沒有寫入,又的話返回錯誤,如果並沒有讀,只有寫那就沒有事務操作。

比如最高級別的排它鎖,都不會被其他人讀到,無論有多少次更新,中間有多少箇中間狀態其他人都看不到。

當做完中間狀態提交後,鎖就解開了,所有系統都恢復了,就都可以看到。對於轉賬,要麼開始我有錢你沒錢,要麼結束是你有錢我沒錢。而中間我減錢和你加錢的過程中被鎖隱藏掉了,這就是資料庫中做事務最標準的方法,而X鎖U鎖讀寫鎖很多鎖都是對標準的排他鎖的優化。

具體的事務實現思路

事務的實現方式-排隊法

一大堆資料單執行緒處理是事務最簡單最重要的解決方案

單執行緒來處理事務,例如nosql的REDIS。Redis的核心思路,所有資料都在記憶體裡,單執行緒處理所有的put、get是效率最高的。

因為多執行緒是CPU模擬多執行緒的情況,多執行緒有上下文切換,在記憶體的情況下,而沒有上下文切換效率最高,這個方案是最好的方案。

第二種簡單的方案:排它鎖

第三種方案:讀寫鎖

對於讀讀場景的優化,一個只讀的事務,比如只做查詢。readwriteLock在讀多寫少的情況下可以搞定。

第三種方式:MVCC

MVCC是後來orcale提出來的

讀是並行,寫是序列

這是最優的解法,本質是copy on write。之前所有的版本都在裡面,讀的時候申請一個版本號,如果版本號比上面的小就可以獲得,讀讀不阻塞的前提下,讀寫和讀讀也不阻塞。

什麼時候事務需要多執行緒?

這在於下層使用的儲存

記憶體操作的時候,IOPS非常高的系統,記憶體的申請和銷燬非常快,記憶體可以動態申請大小,記憶體還可以隨時動態開闢銷燬一段空間。

磁碟IOPS很低,吞吐量高。SAS是幾百M 傳統的磁碟100~150M,意味著必須將大量的讀和寫攢成一個batch一起提交效能最高。

一個事務讀算寫在記憶體裡很快完成,記憶體IOPS是磁碟的1000到1萬倍,如果用記憶體的方式會來操作磁碟會系統性能上不去。所以要將大量的請求攢到一起提交。

方式:

非同步,我的請求和執行緒不繫結,執行緒不block,處理你完了可以處理別人,也是一種多執行緒的場景,將大量不同人的請求提交到一個buffer裡,再由buffer統一再讀或者寫。磁碟網路慢速裝置,多執行緒或者=非同步場景特別多,面對磁碟網路要面對使用多執行緒來處理這些事情。

事務基本的調優原則

不影響業務應用的情況下,儘可能減少鎖的覆蓋範圍

Myisam 表鎖->InnoDb 行鎖

原位鎖 -> MVCC多版本,也是某種意義上的減少鎖的範圍

增加鎖上可並行的執行緒數

讀鎖寫鎖分離,允許並行讀取資料。

選擇正確鎖型別

悲觀鎖 適合併發爭搶中比較嚴重的場景

樂觀鎖 適合併發爭搶中不嚴重的場景

樂觀鎖悲觀鎖定義:

悲關鎖:系統一個請求進來後,請求的執行緒切換出去,正在處理資料的執行緒處理完後,notify被等待的執行緒,然後讓執行緒回到這裡重新競爭這把鎖

樂觀鎖:進來後發現有人再做操作,不切換出去,不切換上下文,迴圈,對方把鎖打開了,你進來,你持有這把鎖,其他的人再做自旋

很多時候業務也會允許要悲觀鎖和樂觀鎖。

最重要的差異

悲觀鎖,請求讀的時候發現有鎖,請求去等待,鎖處理完了,然後notify所有的等待,等待通知。
樂觀鎖,發現對方有鎖,自旋,拿到後讓其他請求自旋,等一段時間問你一次。

為什麼悲觀鎖 適合併發爭搶中比較嚴重的場景而樂觀鎖 適合併發爭搶中不嚴重的場景?

因為每次切換出去,需要CPU開銷,要清除記憶體頁和暫存器,然後才能在外面等。

小結:

事務的ACID

原子性

一致性

隔離性(MVCC/SNAPSHO IOSLATION)

永續性(永續性保證策略)

單機事務的典型異常應對策略

事務的調優原則

。。。未完