newsql的分散式事務實現機制
前言:本文僅從事務角度論述,難免一些結論性的東西會有些偏頗,畢竟資料庫不僅僅只有事務,但事務卻是資料庫中最複雜,最難實現,最重要的技術之一。
背景:網際網路公司從成立初業務往往會飛速發展,系統高併發、資料量急速膨脹對系統擴充套件性提出了更高的需求。對於系統應用服務來說很容易進行擴充套件,但是資料庫卻並非那麼容易,因此資料庫層難於擴充套件往往成為拖累業務發展的瓶頸。
目前主流的關係型資料庫如:Oracle 、MySQL、SQL Server、 PostgreSQL 等面對資料量爆發增長,無法線性擴充套件問題日益凸顯。主流的方案還是採用主從讀寫分離的架構,只能擴充套件一部分的讀計算,對於資料寫、資料儲存擴容目前主要通過微服務的架構業務上對資料庫做垂直拆分再水平拆分來實現。這種架構的優點在於規避了分散式事務,事務處理還在單機資料庫上進行。不過換個角度來說同時也是缺點,業務上需要做很多妥協,例如將一個大應用拆分成使用者模組,訂單模組,商品模組,每個模組都有自己的資料庫,在使用者購買商品的時候需要扣減商品模組庫存,在訂單模組新增訂單資料,這時候需要保證這兩個資料庫操作在同一個事務中完成變得異常困難。兩種方式來解決此類問題:1、應用層實現分散式事務,這個就有點對微服務架構有點背道而馳了,而且實現難度很大、系統整體效能也沒有保障。2、訊息事務+最終一致性,犧牲了一致性,換來了效能的大幅度提升,不過業務要承擔資料不一致帶來的風險,相信大多數網際網路場景下的微服務架構還是會採用這種方式。
回到資料庫擴充套件性上,通過資料庫做分庫分表的方式,擴容實現起來也沒那麼容易往往需要人工干預,即使說通過外部工具能夠做到自動化擴容(如:阿里雲的DRDS)也不能避免對業務產生影響,更不要提有些業務場景下還需要做縮容。比如:雙11場景。
對於擴充套件性上有沒有比較好的資料庫產品呢?
有三類:
1、分散式NoSQL
NoSQL大多數是分散式的資料庫,原生支援資料擴縮容,又有不錯的效能表現。但是大多數NoSQL產品規避了對事務的實現,只能應用於少部分對事務沒有需要的業務場景如:Web應用伺服器、內容管理器、結構化的事件日誌、移動應用程式的伺服器端和檔案儲存的後備儲存。
注:(MongoDB在4.0裡面支援了多行事務,並打算在下個版本支援分散式事務)
由此可見在分散式場景下實現事務是多麼困難。
所以這麼多年過去了,即使傳統關係型資料庫在擴充套件性上暴露明顯不足,NoSQL貌似也沒能取代傳統關係型資料庫的地位。大家發現很多的業務並沒有辦法直接使用 NoSQL 的模型,應用需要很複雜的開發才能實現SQL 和事務能很簡單實現的功能。
2、雲關係型資料庫Aurora、PolarDB
此類資料庫產品多采用儲存和計算分離,所有計算節點共享一份資料屬於share-storage架構。在計算層讀和寫被分離到不同的節點上,只有一個寫節點,寫節點可以處理讀寫請求,其他的節點都是隻讀節點,只讀節點可以很方便的去做Scale out,寫節點也可以很方便的做Scale up。在儲存層採用分散式的檔案系統,儲存叢集可以做橫向擴充套件 (但是有上限官方的資料 Aurora 最大支援64TB、PolarDB 100TB)。Aurora和PolarDB基於MySQL和InnoDB,實現的是單點寫的一主多從架構,所以在事務處理方面,沒有大的變動,事務處理技術得到繼承。整體上是依據SS2PL和MVCC技術實現了事務模型。本質上用的單機事務,規避了分散式事務,但MySQL事務處理機制還是有差別的,比如事務提交不用寫binlog,這裡不在過多介紹。
3、分散式關係型資料庫NewSQL
NewSQL多采用多副本的share-nothing架構,這裡就要引入分散式系統場景中的CAP理論。有人說NewSQL=NoSQL(AP)+SQL(CA),來挑戰CAP理論的正確性。其實魚與熊掌最難兼得的部分是C和P。 本文論述分散式場景下的OLTP資料庫 因此需要有AP這個大前提 ,那麼NewSQL怎麼保障C呢?分兩種情況:1、 分割槽主節點和副本節點的C主要通過Paxos或者Raft協議做到強一致;2、分割槽之間資料操作的事務原子性 需要分散式事務來保證。實際上CAP的正確性還是存在的:1、對於分割槽主副節點強一致共識演算法只能做到日誌級別,日誌Apply到狀態機有可能存在不一致,一些NewSQL提供通過直接讀日誌來構建一致性讀,這個實現說實話有點重,對於客戶端的響應時延會比較大,其實是在一定程度上犧牲了A(可用性),為了規避這個問題有些乾脆副本節點不提供讀操作。2、通常分割槽之間實現的分散式事務多采用2PC協議,經典2PC協議實際上是一個CP系統,同樣犧牲了A。
論述NewSQL產品的話需要詳細介紹一下Google Spanner,很多NewSQL的後起之秀如開源CockroachDB以及國內的TiDB,包括螞蟻金服的Oceanbase、阿里集團的XDB都在一定程度上借鑑了Spanner。
Google Spanner是谷歌研發的可橫向擴充套件的、支援多版本的、可在全球範圍進行分散式部署的、同步進行資料複製的分散式資料庫。Spanner是世界上第一個可以在全球範圍內進行資料分散式管理並且支援外部一致性(Linearizability_Consistency+Transaction_Serializable)分散式事務的資料庫產品。實現這麼多功能有很多技術點,有興趣可以閱讀下Spanner的paper,我們這裡只探討事務相關。
Spanner 對外提供了read-only transaction和read-write transaction兩種事務。這些都需要 TrueTime這個大殺器作為前提,TrueTime 是一種 API,它允許 Google 資料中心內的任何機器以高精度(誤差在幾毫秒內)獲悉準確的全球時間。這使得各種 Spanner 機器能夠推斷事務操作的順序(並且該順序與客戶端觀察到的順序相匹配),且通常無需任何通訊。Google 不得不為其資料中心配備特殊硬體(原子鐘!)以使 TrueTime 發揮作用。由此產生的時間精度和準確度遠高於其他協議(如 NTP)所能達到的時間精度和準確度。具體來說,Spanner 會為所有讀寫操作分配一個時間戳。時間戳T1處的事務可確保反映在T1之前發生的所有寫入的結果。如果一臺機器想要在T2處滿足讀取請求,它必須確保其資料檢視至少在T2處保持最新狀態。得益於 TrueTime 技術,這個解決方法通常成本低廉,其實蠻貴的。這裡可能有人會有疑問,用一個QQ號購買平臺地圖全域性序列號生成器如:Google 很早的另一篇paper 《Large-scale Incremental Processing Using Distributed Transactions and Notifications》中提到的Percolator 依賴的 全域性授時服務TSO(Timestamp Oracle )不是更容易實現嗎?實際上國內TiDB 、Oceanbase的 GTS都採用類似的實現。為啥還要這麼麻煩的搞一個 TrueTime 出來?全域性授時服務TSO雖然實現起來比較簡單,但很容易看出來它是一個典型的單點服務,即使通過共識演算法構建高可用會做一些 failover 的處理,仍然可能是整個系統的一個瓶頸,同時在多機房部署場景下也不可避免產生網路開銷,這對於號稱全球化部署的Spanner顯然是不能接受的。
這裡不再描述實現事務的具體細節,只來說明對比單機資料庫會多出哪些時延開銷!
單節點寫入
這是由 Spanner 完成的寫入中成本最低的一種
1、事務提交前需要將寫入同步到副本多數派的時延(Paxos延遲)
2、主副本會等到能確定事務的時間戳已實時傳遞為止;這通常需要幾毫秒,以避開 TrueTime 時間戳可能存在的不確定性。這樣可以確保高度一致性:一旦客戶端獲知事務的結果,就可以保證所有其他讀取者也能看到事務的效果。這個“提交等待”通常與上述步驟中的副本通訊同時發生,因此它的實際延遲成本很低
多分片寫入
除了單節點寫入的開銷外,還需要額外的跨分片協調即分散式事務處理(Paxos延遲+commit延遲)。
Spanner使用的是標準的2PC,雖然在 Paxos 上執行兩階段提交弱化了可用性問題,但依然存在效能問題。
標準2PC中全程至少2次RPC延遲(prepare+commit),和4次持久化資料延遲(prepare寫日誌+協調者狀態持久化2次+commit寫日誌)
這裡額外提一下TiDB 和Oceanbase!
TiDB使用的是 Percolator系統的2PC
Percolator 通過把事務的狀態都下沉到儲存中,簡化了2PC中協調者的角色,只要primary record commit成功就返回客戶端事務提交成功。這種實現優點就是commit延遲低,對寫入事務友好。
但是也存在著諸多侷限:
1、底層KV遮蔽了sharding細節,且不提供交戶型的事務上下文機制,對儲存引擎的讀寫只能在一次RPC提交,使得加鎖、修改、提交都必須是一次bigtable的提交操作,延遲代價是巨大的。
2、儘管primary record的設計簡化了2PC的協調者狀態維護,但是commit階段secondaries仍然要等待primary record持久化事務狀態成功後,才能進行commit,這一次延遲不可避免。這就導致secondaries持有鎖的時間變長,SI隔離級別下,單機讀分散式事務參與者會等待更長的時間(不能讀舊版本資料,不然就會出現幻讀),單機寫的鎖衝突也會加劇。
3、commit延遲低帶來的代價就是需要一種方案來處理執行中事務發生故障的時候遺留下來的鎖,Percolator採用延遲處理來釋放鎖,這種方式雖然易於實現不過可能會將事務提交延遲數10s。
Oceanbase 在標準的2PC演算法上做了優化,引入無狀態協調者,參與者預提交,最終只需要1次日誌延遲 + 2次 RPC 延遲。詳見螞蟻金服資深技術專家 顏然的演講《OceanBase事務引擎介紹》。Oceanbase 還在最新的2.0版本中提出了一個分割槽組的概念,用來規避多分片寫入所引入的分散式事務。詳見:螞蟻金服技術專家梅慶的演講《OceanBase支撐2135億成交額背後的技術原理》。有點像把傳統的分庫分表方案放在資料庫核心中實現,但也會存在分庫分表中熱點資料傾斜的問題,有點偽分散式的感覺。
強讀取(多節點)
由於讀取操作可能會由給定分片的任意最新副本執行,對最新資料的“強”讀取可能會因驗證副本是否足夠新而產生一些延遲時間(向主副本發出一次RPC);但只需過時資料即可的讀取則無需付出這種延遲代價。
這裡沒有具體的資料做支撐,有興趣的可以閱讀下Spanner paper中的基準測試部分
以上可以看到:
第二種資料庫產品有著不錯的擴充套件性的同時也有號稱比傳統關係型資料庫更佳的OLTP事務效能,目前看來對大部分讀多寫少的應用是個不錯的方案。
第三種資料庫產品NewSQL保障ACID事務相比傳統的關係型資料庫存在著非常多的效能開銷且無法避免。事務中持有的鎖會從事務開始直到多個節點寫完日誌,經歷多次網路延遲、IO延遲,導致整體事務吞吐量降低,而NewSQL在一定程度上是通過擴充套件性來緩解。
以前都說資料庫的效能瓶頸在磁碟IO,那麼未來資料庫效能瓶頸將是網路IO。
在2017年DTCC大會上阿里巴巴研究員張瑞的演講《面向未來的資料庫體系架構的思考》中提到以前對於OLTP系統資料庫來說不允許有很高的時延,未來在資料庫架構上很多物理時延不可避免,應用程式需要對相對高的資料庫時延來做適配。正所謂英雄所見略同,Pingcap的CTO黃東旭在一篇博文 《十問 TiDB :關於架構設計的一些思考》中也強調了類似的看法“衡量一個分散式系統更有意義的指標是吞吐,這個觀點我在很多文章裡已經提到過,提高併發度,如果系統的吞吐能夠隨著叢集機器數量線性提升,而且延遲是穩定的才有意義,而且這樣才能有無限的提升空間。”
雖然兩位大牛都這麼說,不過目前應該很少有企業在自己的業務系統中使用分散式資料庫處理事務。未來在分散式資料庫中使用事務,你是否已經做好準備?
附生產級NewSQL所用的資料庫技術