擼明白分佈式事務(一)

傳統的ACID

什麼是事務?回答這個問題之前,我們先來看一個經典的場景:支付寶等交易平臺的轉賬。假設小明需要用支付寶給小紅轉賬 100000 元,此時,小明帳號會少 100000 元,而小紅帳號會多 100000 元。如果在轉賬過程中系統崩潰了,小明帳號少 100000 元,而小紅帳號金額不變,就會出大問題,因此這個時候我們就需要使用事務了。大致流程如下圖

這裏,體現了事務一個很重要的特性:原子性。事實上,事務有四個基本特性:原子性、一致性、隔離性、持久性。其中,原子性,即事務內的操作要麼全部成功,要麼全部失敗,不會在中間的某個環節結束。一致性,即使數據庫在一個事務執行之前和執行之後,數據庫都必須處於一致性狀態。如果事務執行失敗,那麼需要自動回滾到原始狀態,換句話說,事務一旦提交,其他事務查看到的結果一致,事務一旦回滾,其他事務也只能看到回滾前的狀態。隔離性,即在併發環境中,不同的事務同時修改相同的數據時,一個未完成事務不會影響另外一個未完成事務。持久性,即事務一旦提交,其修改的數據將永久保存到數據庫中,其改變是永久性的。

本地事務通過 ACID 保證數據的強一致性。ACID是 Atomic(原子性)、Consistency(一致性)、 Isolation(隔離性)和 Durability(持久性)的縮寫 。在實際開發過程中,我們或多或少都有使用到本地事務。例如,MySQL 事務處理使用到 begin 開始一個事務,rollback 事務回滾,commit 事務確認。這裏,事務提交後,通過 redo log 記錄變更,通過 undo log 在失敗時進行回滾,保證事務的原子性。筆者補充下,使用 Java 語言的開發者都接觸過 Spring。Spring 使用 @Transactional 註解就可以搞定事務功能。事實上,Spring 封裝了這些細節,在生成相關的 Bean 的時候,在需要注入相關的帶有 @Transactional 註解的 bean 時候用代理去注入,在代理中爲我們開啓提交/回滾事務。單庫單表的應用與數據庫的交互如圖所示

後來,ACID開始力不從心了

隨着業務的高速發展,面對海量數據,例如,上千萬甚至上億的數據,查詢一次所花費的時間會變長,甚至會造成數據庫的單點壓力。因此,我們就要考慮分庫與分表方案了。分庫與分表的目的在於,減小數據庫的單庫單表負擔,提高查詢性能,縮短查詢時間。這裏,我們先來看下單庫拆分的場景。事實上,分表策略可以歸納爲垂直拆分和水平拆分。垂直拆分,把表的字段進行拆分,即一張字段比較多的表拆分爲多張表,這樣使得行數據變小。一方面,可以減少客戶端程序和數據庫之間的網絡傳輸的字節數,因爲生產環境共享同一個網絡帶寬,隨着併發查詢的增多,有可能造成帶寬瓶頸從而造成阻塞。另一方面,一個數據塊能存放更多的數據,在查詢時就會減少 I/O 次數。水平拆分,把表的行進行拆分。因爲表的行數超過幾百萬行時,就會變慢,這時可以把一張的表的數據拆成多張表來存放。水平拆分,有許多策略,例如,取模分表,時間維度分表等。這種場景下,雖然我們根據特定規則分表了,我們仍然可以使用本地事務。但是,庫內分表,僅僅是解決了單表數據過大的問題,但並沒有把單表的數據分散到不同的物理機上,因此並不能減輕 MySQL 服務器的壓力,仍然存在同一個物理機上的資源競爭和瓶頸,包括 CPU、內存、磁盤 IO、網絡帶寬等。對於分庫拆分的場景,它把一張表的數據劃分到不同的數據庫,多個數據庫的表結構一樣。此時,如果我們根據一定規則將我們需要使用事務的數據路由到相同的庫中,可以通過本地事務保證其強一致性。但是,對於按照業務和功能劃分的垂直拆分,它將把業務數據分別放到不同的數據庫中。這裏,拆分後的系統就會遇到數據的一致性問題,因爲我們需要通過事務保證的數據分散在不同的數據庫中,而每個數據庫只能保證自己的數據可以滿足 ACID 保證強一致性,但是在分佈式系統中,它們可能部署在不同的服務器上,只能通過網絡進行通信,因此無法準確的知道其他數據庫中的事務執行情況。單應用多庫的場景大致如下圖

此外,不僅僅在跨庫調用存在本地事務無法解決的問題,隨着微服務的落地中,每個服務都有自己的數據庫,並且數據庫是相互獨立且透明的。那如果服務 A 需要獲取服務 B 的數據,就存在跨服務調用,如果遇到服務宕機,或者網絡連接異常、同步調用超時等場景就會導致數據的不一致,這個也是一種分佈式場景下需要考慮數據一致性問題 。多應用多庫場景如下

總結一下,當業務量級擴大之後的分庫,以及微服務落地之後的業務服務化,都會產生分佈式數據不一致的問題。既然本地事務無法滿足需求,因此分佈式事務就要登上舞臺。什麼是分佈式事務?我們可以簡單地理解,它就是爲了保證不同數據庫的數據一致性的事務解決方案。這裏,我們有必要先來了解下 CAP 原則和 BASE 理論。CAP 原則是 Consistency(一致性)、Availablity(可用性)和 Partition-tolerance(分區容錯性)的縮寫,它是分佈式系統中的平衡理論。在分佈式系統中,一致性要求所有節點每次讀操作都能保證獲取到最新數據;可用性要求無論任何故障產生後都能保證服務仍然可用;分區容錯性要求被分區的節點可以正常對外提供服務。事實上,任何系統只可同時滿足其中二個,無法三者兼顧。對於分佈式系統而言,分區容錯性是一個最基本的要求。那麼,如果選擇了一致性和分區容錯性,放棄可用性,那麼網絡問題會導致系統不可用。如果選擇可用性和分區容錯性,放棄一致性,不同的節點之間的數據不能及時同步數據而導致數據的不一致。對於CAP原理,可以看看CAP原理深入瞭解。

此時,BASE 理論針對一致性和可用性提出了一個方案,BASE 是 Basically Available(基本可用)、Soft-state(軟狀態)和 Eventually Consistent(最終一致性)的縮寫,它是最終一致性的理論支撐。簡單地理解,在分佈式系統中,允許損失部分可用性,並且不同節點進行數據同步的過程存在延時,但是在經過一段時間的修復後,最終能夠達到數據的最終一致性。BASE 強調的是數據的最終一致性。相比於 ACID 而言,BASE 通過允許損失部分一致性來獲得可用性。

現在,業內比較常用的分佈式事務解決方案,包括強一致性的兩階段提交協議,三階段提交協議,以及最終一致性的可靠事件模式、補償模式,阿里的 TCC 模式。我們會在後面的章節中詳細介紹與實戰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章