「分散式技術專題」併發系列二:基於時間的併發控制
與基於加鎖的併發控制不同,基於時間戳的併發控制並不試圖透過互斥關係來維護可序列化,而是先提前選擇一個序列化順序,再根據這個順序來執行事務,為了建立這個順序,事務管理器需要對每個事務 T進行初始化時分配一個時間戳。
時間戳時一種專門用來唯一標識每個事務並將事務排序的識別符號。唯一性、單調性是時間戳的特性,即每個時間戳都是唯一的,事務管理器生成的事務必須單調遞增。
一種方式是維護一個全域性的計數器,但這在分散式系統中是一個比較困難的問題。還有一種方式是讓每個節點根據本地的區域性計數器獨立分配時間戳。為了維護唯一性,每個節點會將直接的標識號附加到計數器值的後面,
即時間戳是一個二元組:
<區域性計數器,節點ID>。節點ID作為附加在較低位, 只有在兩個事務被分配了相同的區域性計數值時,節點ID才能派上用場。而每個節點訪問自己的時鐘,這裡的時鐘可以是物理時鐘、邏輯時鐘、混合時鐘、全域性時鐘等等方式, 主要看分散式資料庫時鐘選取問題,各種時鐘都有自己的優劣,需要謹慎選取。
綜合上面的資訊可知,可以按照時間戳來對事務的操作進行排序了。時間戳排序(
timestamp ordering,TO)的規則如下:TO規則給定事務Ti和Tj中的兩個衝突操作Oik和Ojl,當且僅當ts(Ti)<ts(Tj)時,Oik會在Ojl之前執行。ts即是對於事務T分配的唯一時間戳。
使用 TO規則的排程會將每一個操作與已經排程過的衝突操作進行比較,如果新的操作屬於一個較新的事務, 那麼就接收它;否則它就會被拒絕,然後將相應的事務賦予新的時間戳並重新啟動。
這個思路提供了基於時間戳的排程,可以保證生成可序列化的排序,不過,只有當排程程式接收到所有 需要排程的操作,時間戳之間才能比較。如果操作一個接一個傳遞給排程器,那麼就需要以高效的方式檢測 每個操作是否按照合法順序進入。這樣,每個資料項 x需要被賦予兩個時間戳:讀時間戳rts(x)、寫時間戳wts(x)。讀時間戳表示讀取x的所有事務的時間戳的最大值,寫時間戳表示寫入x的所有事務的時間戳的最大值。這時,當一個操作需要訪問某個資料項時,排程程式就可以根據讀時間戳和寫時間戳來判斷是否有時間戳更大的事務 訪問了該資料項。
從構架上來看,事務管理器負責為每個新的事務分配時間戳,並將這個時間戳附加到事務的資料庫操作上。排程器的職責是記錄讀和寫的時間戳,並進行可序列化排序檢查。
基於時間戳的併發控制也是由傳統關係型資料庫最早提出的,分散式的 TO演算法也是由單機TO演算法延伸而來。在《資料庫系統概念》一書的15.4章節詳細介紹了基於時間戳的協議。
基本 TO
基本 TO是TO規則的直接實現,協調事務管理器為每個事務分配時間戳,為每個資料項選定儲存節點, 並且將相關資料庫操作傳送到這些節點上。
基本 TO的規則是:
• 事務管理器讀或者寫資料項均不需要鎖
• 每個資料項x都帶有成功讀/寫最後一個事務的時間戳:rts和wts。
• 為每個操作檢查時間戳:如果事務試圖訪問“未來”的物件,將重啟或取消。
讀取規則:
• 如果ts(t)<wts(x),則t需要讀取的x值已經被覆蓋。讀取被拒絕,並回滾。換句話說,這違反了事務t對寫x的時間戳排序,需要取消t並重啟分配新的ts
• 否則允許t對x進行讀,更新rts(x)=max(rts(x),ts(t)),製作一個x的本地副本,以確保t的可重複讀
寫規則:
• 如果ts(t)<rts(x)或者ts(t)<wts(x),取消或者重啟t
– ts(t)<rts(x)時,事務t產生的x值是先前所需要的值,所以write操作被拒絕,t取消或重啟。
– ts(t)<wts(x)時,t試圖寫入的x已過時,因此寫操作被拒絕,t取消或重啟
• 否則允許t對x進行寫並更新wts(x),同時製作x一個本地副本,以確保t的可重複讀
一種更快速的方式是使用 thomas寫規則(資料庫系統概念15.4.3),這裡不再展開。
擴充套件到分散式,採用分散式中的時間戳方案透過事務管理器將時間戳分配給所有的讀和寫操作, 每個資料管理器( DM)的排程程式都會跟蹤迄今為止為每個資料物件處理的所有讀寫操作中的最大時間戳。
read(x,ts)=rts(x),對資料物件x帶有時間戳ts的讀取請求, write(x,v,ts)=wts(x),向資料物件x寫入帶有時間戳ts的寫入請求,寫入值是v。基本演算法同上邊的讀取和寫規則,只是增加了分散式節點間的訊息處理,所以在設計上會稍有不同。首先,某一個操作如果被拒絕,那麼整個事務就需要重新分配新的時間戳,然後重啟, 這保證了事務的重試的機會,這意味著基本TO不會死鎖;當一個已接收的操作被傳遞到資料處理器時, 排程器必須在該操作完成之前避免向資料處理器再傳送另一個雖然衝突,但也被接受的操作。同時,每個節點的資料處理器上的資料操作如何保證故障轉移和一致性也是設計時需要考慮的事情。
基本 TO的演算法描述可以查詢相關論文。
保守 TO
基本 TO不會讓操作進行等待,而是將其重啟。這種方法可以有效避免死鎖,但也有劣勢, 即過多的重啟會對效能造成不利的影響。保守TO試圖透過減少失誤重啟的次數來降低系統的負載。保守TO採用了Lamport的邏輯時鐘的做法來進行時鐘同步,避免出現單個節點的時鐘落後其他節點太多的情況。
相對於基礎
TO,保守TO的思路是每個排程器建立一個緩衝區,每個事務的操作都放在緩衝區內, 然後再排序之後執行操作,而不是和基本TO一樣即時執行。緩衝區中的操作時不會被拒絕的。保守特性來源於其執行每個操作的方式,對於基礎TO來說,每當接收一個操作就立即執行, 與之相對的是,保守TO會將操作進行延遲,直到可以確認今後排程器不會再接收到時間戳更小的操作。如果這個條件可以保證,那麼排程器就不會拒絕任何一個操作,不過這種方式有可能引入死鎖。
保守
TO會減少重啟次數,但不能完全避免事務重啟。除非採用更保守的TO:僅當佇列裡至少有一個操作時, 排程器才可以選擇一個操作傳送給資料處理器。這樣做可以保證排程器在接收到每個操作時, 時間戳都能大於或等於當前佇列中的操作的時間戳。如果沒有事務需要處理,事務管理器以 一定的間隔向其他排程器傳送偽資訊,以便以後傳送的操作都比偽資訊時間戳大。這種極端保守的TO排程程式會讓每個節點都順序執行事務。
多版本 TO
多版本 TO是另一種試圖避免重啟帶來開銷的方法。在多版本TO中,更新並不改變資料庫;每個寫操作都建立一個資料的新版本。每個版本都標記一個建立它事務的時間戳, 這樣多版本TO就可以在儲存空間和時間上做一個平衡。這樣,資料庫處理事務可以按照時間戳 順序執行。
多版本 TO的處理:
Ri(x)會轉化為x的某一版本的讀。首先找到x的一個版本Xv,這個版本時間戳比ts(Ti)小, 但比其他早於Ti事務的其他版本的時間戳都大。讀取到的值就是這個時間戳的點的值。如圖, 這時,Ri可以讀取到的值是Xv。
Wi(x)轉化成帶有Wi(xw),滿足ts(xw)=ts(Ti),並且它被髮送給資料處理程式時, 當且僅當,沒有其他時間戳大於ts(Ti)事務讀取了x的一個版本Xr,滿足ts(xr)>ts(xw), 換句話說,如果排程程式已經處理了圖中的Rj(xr):ts(Ti)<ts(Xr)<ts(Tj)時,Wi(x)被拒絕。
Ri
| | ↓ |
---------±-------±---------±--------> 時間戳
| | |
Xk Xv Xw
Wi Rj
| | ↓ ↓ |
1
2
---------±-------±-----------±--------> 時間戳
| | xw |
Xl Xk Xr
如圖,如果
Wi被接受的話,事務管理器會建立x的版本xw,這個版本本該被Rj讀取,但由於 Rj執行時xw並不存在,如果只能讀取到Xk,會出現歷史錯誤。
總結
無論是前邊介紹的加鎖還是基於時間戳的併發控制,都是悲觀的,也就是先假設事務之間的衝突比較頻繁, 它們不允許兩個事務同時對同一個資料項進行衝突的訪問,因此,任意一個操作都可以按照這個步驟來執行:有效性驗證,讀,計算,寫。一般來說,這個順序對於更新事務以及它的操作來說都是有效的。而目前來說,一般使用的分散式併發控制,都是樂觀併發控制( OPTIMISTIC CONCURRENCY CONTROL OCC)。雖然本文和加鎖的併發控制介紹的並非是時下流行的樂觀併發控制,但可以增加對傳統關係型資料庫的併發控制的瞭解, 並且這些理論都是後續的基於MVCC的樂觀併發控制的基礎。
以上為基於時間的併發控制。 「分散式技術專題」是國產資料庫 hubble 團隊精心整編,專題會持續更新,歡迎大家保持關注。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70026685/viewspace-2935019/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 「分散式技術專題」併發系列一:基於加鎖的併發控制分散式
- 「分散式技術專題」併發系列三:樂觀併發控制之理論研究分散式
- 「分散式技術專題」併發系列三:樂觀併發控制之原型系統分散式原型
- 「分散式技術專題」併發系列三:樂觀併發控制之原型系統(分散式驗證)分散式原型
- 「分散式技術專題」併發系列三:樂觀併發控制之生產系統分散式
- 「分散式技術專題」併發系列三:樂觀併發控制之原型系統(動態調整提交時間戳)分散式原型時間戳
- PostgreSQL 併發控制機制(3):基於時間戳的併發控制SQL時間戳
- 「分散式技術專題」資料切分與合併分散式
- [分散式][高併發]高併發架構分散式架構
- 分散式鎖不是控制併發冪等的方式分散式
- 高併發核心技術 - 冪等性 與 分散式鎖分散式
- 併發技術3:定時器定時器
- 併發技術1:CSP併發理論
- 探索併發程式設計(七)——分散式環境中併發問題程式設計分散式
- 高併發技術
- JDK併發AQS系列(二)JDKAQS
- 併發包系列二—— CopyOnWriteArrayList
- [分散式]高併發案例---庫存超發問題分散式
- 基於 Python 自建分散式高併發 RPC 服務Python分散式RPC
- 用分散式鎖解決併發問題分散式
- [分散式]對高併發流量控制的一點思考分散式
- 併發技術5:死鎖問題
- 基於Mongodb分散式鎖簡單實現,解決定時任務併發執行問題MongoDB分散式
- Guava併發:使用Monitor控制併發Guava
- 併發控制
- GO-併發技術Go
- 併發技術中同步
- [技術討論]多人併發開發中的問題
- Java併發專題(二)執行緒安全Java執行緒
- 「分散式技術專題」時鐘系列二:資料庫世界中為什麼要有時鐘?分散式資料庫
- 「分散式技術專題」時鐘系列一:事件的因果和邏輯時鐘分散式事件
- 基於redis實現的鎖(用於控制nodejs的併發)RedisNodeJS
- Java併發基礎02:傳統執行緒技術中的定時器技術Java執行緒定時器
- goroutine併發控制Go
- mysql併發控制MySql
- PGSQL併發控制SQL
- oracle併發控制Oracle
- [分散式][高併發]限流的四種策略分散式