沈洵:分散式事務原理與實踐之單機事務
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)
永續性(永續性保證策略)
單機事務的典型異常應對策略
事務的調優原則
。。。未完