什麼是一緻性
一緻性就是資料保持一緻,在分布式系統中,可以了解為多個節點中資料的值是一緻的。
- 強一緻性:這種一緻性級别是最符合使用者直覺的,它要求系統寫入什麼,讀出來的也會是什麼,使用者體驗好,但實作起來往往對系統的性能影響大。
- 弱一緻性:這種一緻性級别限制了系統在寫入成功後,不承諾立即可以讀到寫入的值,也不承諾多久之後資料能夠達到一緻,但會盡可能地保證到某個時間級别(比如秒級别)後,資料能夠達到一緻狀态
- 最終一緻性:最終一緻性是弱一緻性的一個特例,系統會保證在一定時間内,能夠達到一個資料一緻的狀态。這裡之是以将最終一緻性單獨提出來,是因為它是弱一緻性中非常推崇的一種一緻性模型,也是業界在大型分布式系統的資料一緻性上比較推崇的模型
一緻性問題
下訂單和扣庫存
電商系統中有一個經典的案例 , 即下訂單和扣庫存如何保持一緻。 如果先下訂單,扣庫存失敗,那麼将會導緻超賣;如果下訂單不成功,扣庫存成功,那麼會導緻少賣。 這兩種情況都會導緻營運成本增加,在嚴重情況下需要賠付。
同步調用逾時
系統 A 同步調用系統 B 逾時,系統 A 可以明确得到逾時回報,但是無法确定系統 B 是否已經完成了預設的功能。 于是,系統 A 不知道應該繼續做什麼,如何回報給使用方。
異步回調逾時
系統 A 同步調用系統 B 發起指令,系統 B 采用受理模式,受理後則傳回成功資訊 ,然後系統 B 處理後異步通知系統 A 處理結果。在這個過程中,如果系統 A 由于某種原因遲遲沒有收到回調結果,那麼這兩個系統間的狀态就不一緻。
解決一緻性問題的模式和思路
事務分類
- 本地事務:
- 同一資料庫和伺服器,稱為本地事務
本地事務
- 分布式事務:
分布式事務指事務的參與者、支援事務的伺服器、資源伺服器以及事務管理器分别位于不同的分布式系統的不同節點之上,且屬于不同的應用,分布式事務需要保證這些操作要麼全部成功,要麼全部失敗。本質上來說,分布式事務就是為了保證不同資料庫的資料一緻性。
分布式事務
事務的ACID
- A(Atomic):原子性,構成事務的所有操作,要麼都執行完成,要麼全部不執行,不可能出現部分成功部分失 敗的情況。
- C(Consistency):一緻性,在事務執行前後,資料庫的一緻性限制沒有被破壞。比如:張三向李四轉100元, 轉賬前和轉賬後的資料是正确狀态這叫一緻性,如果出現張三轉出100元,李四賬戶沒有增加100元這就出現了數 據錯誤,就沒有達到一緻性。
- I(Isolation):隔離性,資料庫中的事務一般都是并發的,隔離性是指并發的兩個事務的執行互不幹擾,一個事 務不能看到其他事務運作過程的中間狀态。通過配置事務隔離級别可以避髒讀、重複讀等問題。
- D(Durability):持久性,事務完成之後,該事務對資料的更改會被持久化到資料庫,且不會被復原。
具有 ACID 特性的資料庫支援強一緻性,強一緻性代表資料庫本身不會出現不一緻, 每個事務都是原子的,或者成功或者失敗,事物間是隔離的,互相完全不受影響,而且最終狀态是持久落盤的。
CAP定律
這個定理的内容是指的是在一個分布式系統中、Consistency(一緻性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。
- 一緻性(C)
在分布式系統中的所有資料備份,在同一時刻是否同樣的值。
- 可用性(A)
在叢集中一部分節點故障後,叢集整體是否還能響應用戶端的讀寫請求。
- 分區容錯性(P)
以實際效果而言,分區相當于對通信的時限要求。系統如果不能在時限内達成資料一緻性,就意味着發生了分區的情況,必須就目前操作在C和A之間做出選擇
例子:
- CP:當庫存服務減庫存以後,那麼需要将資料同步到其他的服務上,這是為了保證資料一緻性C,但是網絡是不可靠的,是以我們系統就需要保證分區容錯性P,也就是我們必須容忍網絡所帶來的的一些問題,此時如果我們想保證C那麼就需要舍棄A,也就是說我們在保證C的情況下,就必須舍棄A,也就是CP無法保證高可用。
- AP: 如果為了保證A,高可用的情況下,也就是必須在限定時間内給出響應,同樣由于網絡不可靠P,訂單服務就有可能無法拿到新的資料,但是也要給使用者作出響應,那麼也就無法保證C一緻性。是以AP是無法保證強一緻性的。
- CA: 如果我們想保證CA,也就是高可用和一緻性,也就是必須保證網絡良好才能實作,那麼也就是說我們需要将庫存、訂單、使用者放到一起,但是這種情況也就喪失了P這個保證,這個時候系統也就不是分布式系統了。
故:在分布式系統中,p是必然的存在的,是以我們隻能在C和A之間進行取舍,在這種條件下就誕生了BASE理論
BASE理論
BASE是Basically Available(基本可用)、Soft state(軟狀态)和 Eventually consistent(最終一緻性)三個短語的縮寫。BASE理論是對CAP中一緻性和可用性權衡的結果,其來源于對大規模網際網路系統分布式實踐的總結, 是基于CAP定理逐漸演化而來的。BASE理論的核心思想是:即使無法做到強一緻性,但每個應用都可以根據自身業務特點,采用适當的方式來使系統達到最終一緻性。
- 基本可用
基本可用是指分布式系統在出現不可預知故障的時候,允許損失部分可用性—-注意,這絕不等價于系統不可用。比如:
(1)響應時間上的損失。正常情況下,一個線上搜尋引擎需要在0.5秒之内傳回給使用者相應的查詢結果,但由于出現故障,查詢結果的響應時間增加了1~2秒
(2)系統功能上的損失:正常情況下,在一個電子商務網站上進行購物的時候,消費者幾乎能夠順利完成每一筆訂單,但是在一些節日大促購物高峰的時候,由于消費者的購物行為激增,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面
- 軟狀态
軟狀态指允許系統中的資料存在中間狀态,并認為該中間狀态的存在不會影響系統的整體可用性,即允許系統在不同節點的資料副本之間進行資料同步的過程存在延時
- 最終一緻性
最終一緻性強調的是所有的資料副本,在經過一段時間的同步之後,最終都能夠達到一個一緻的狀态。是以,最終一緻性的本質是需要系統保證最終資料能夠達到一緻,而不需要實時保證系統資料的強一緻性。
保證最終一緻性模式
查詢模式
任何服務操作都需要提供一個查詢接口,用來向外部輸出操作執行的狀态。 服務操作的使用方可以通過查詢接口得知服務操作執行的狀态,然後根據不同的狀态來做不同的處理操作。
補償模式
有了上面的查詢模式,在任何情況下,我們都能得知具體的操作所處的狀态,如果整個操作都處于不正常的狀态,則我們需要修正操作中有問題的子操作,這可能需要重新執行未完成的子操作,後者取消已經完成的子操作,通過修複使整個分布式系統達到一緻。 為了讓系統最終達到一緻狀态而做的努力都叫作補償。
補償操作根據發起形式分為以下幾種。
- 自動恢複:程式根據發生不一緻的環境,通過繼續進行未完成的操作,或者復原已經完成的操作,來自動達到一緻狀态。
- 通知營運:如果程式無法自動恢複,并且設計時考慮到了不一緻的場景,則可以提供營運功能,通過營運手工進行補償。
- 技術營運:如果很不巧,系統無法自動回複,又沒有營運功能,那麼必須通過技術手段來解決,技術手段包括進行資料庫變更或者代碼變更,這是最糟的一種場景,也是我們在生産中盡量避免的場景。
異步確定模式
異步確定模式是補償模式的一個典型案例,經常應用到使用方對響應時間要求不太高的場景中,通常把這類操作從主流程中摘除,通過異步的方式進行處理,處理後把結果通過通知系統通知給使用方 。這個方案的最大好處是能夠對高并發流量進行消峰,例如:電商系統中的物流、配疊,以及支付系統中的計費、入賬等。
在實踐中将要執行的異步操作封裝後持久入庫,然後通過定時撈取未完成的任務進行補償操作來實作異步確定模式,隻要定時系統足夠健壯,則任何任務最終都會被成功執行。
異步確定模式
定期校對模式
定期校對模式多應用于金融系統中。金融系統由于涉及資金安全, 需要保證準确性, 是以需要多重的一緻性保證機制,包括商戶交易對賬、系統間的一緻性對賬、現金對賬、賬務對賬、續費對賬等,這些都屬于定期校對模式。
可靠消息模式
在分布式系統中,對于主流程中優先級 比較低的操作,大多采用異步的方式執行,也就是前面提到的異步確定模型,為了讓異步操作的調用方和被調用方充分解楠,也由于專業的消息隊列本身具有可伸縮、可分片、可持久等功能,我們通常通過消息隊列實作異步化。對于消息隊列,我們需要建立特殊的設施來保證可靠的消息發送及處理機的幂等性。
- 消息的可靠發送
在發送消息之前将消息持久到資料庫,狀态标記為待發送, 然後發送消息,如果發送成功,則将消息改為發送成功。定時任務定時從資料庫撈取在一定時間内未發送的消息并将消息發送。
- 消息處理器的明幂等性
如果我們要保證可靠地發送消息,簡單來說就是要保證消息一定發送出去,那麼需要有重試機制。有了重試機制後,消息就一定會重複,那麼我們需要對重複的問題進行處理。
分布式一緻性協定
兩階段送出協定(2PC)
- 準備階段: 協調者向參與者發起指令,參與者評估自己的狀态,如果參與者評估指令可以完成,則會寫 redo 或者 undo 日在、( Write- Ahead Log 的一種),然後鎖定資源,執行操作,但是并不送出。
- 送出階段: 如果每個參與者明确傳回準備成功,也就是預留資源和執行操作成功,則協調者向參與者發起送出指令,參與者送出資源變更的事務,釋放鎖定的資源;如果任何一個參與者明确傳回準備失敗, 也就是預留資源或者執行操作失敗,則協調者向參與者發起中止指令,參與者取消己經變更的事務,執行 undo 日志,釋放鎖定的資源。
兩階段送出協定(2PC)
我們看到兩階段送出協定在準備階段鎖定資源,這是一個重量級的操作 , 能保證強一緻性,但是實作起來複雜、成本較高、不夠靈活,更重要的是它有如下緻命的問題。
- 阻塞:從上面的描述來看,對于任何一次指令都必須收到明确的響應,才會繼續進行下一步,否則處于阻塞狀态,占用的資源被一直鎖定,不會被釋放。
- 單點故障:如果協調者當機,參與者沒有協調者指揮,則會一直阻塞,盡管可以通過選舉新的協調者替代原有協調者,但是如果協調者在發送一個送出指令後當機,而送出指令僅僅被一個參與者接收,并且參與者接收後也當機,則新上任的協調者無法處理這種情況。
- 資料不一緻:協調者發送送出指令,有的參與者接收到并執行了事務,有的參與者沒有接收到事務就沒有執行事務,多個參與者之間是不一緻的。
三階段送出協定(3PC)
三階段送出協定是兩階段送出協定的改進版本。它通過逾時機制解決了阻塞的問題, 井且把兩個階段增加為以下三個階段。
- 詢問階段:協調者詢問參與者是否可以完成指令,協調者隻需要回答是或不是,而不需要做真正的操作 ,這個階段逾時會導緻中止。
- 準備階段: 如果在詢問階段所有參與者都傳回可以執行操作,則協調者向參與者發送預執行請求,然後參與者寫 redo 和 undo 日志,執行操作但是不送出操作:如果在詢問階段任意參與者傳回不能執行操作的結果,則協調者向參與者發送中止請求,這裡的邏輯與兩階段送出協定的準備階段是相似的。
- 送出階段:如果每個參與者在準備階段傳回準備成功,也就是說預留資源和執行操作成功,則協調者向參與者發起送出指令,參與者送出資源變更的事務,釋放鎖定的資源:如果任何參與者傳回準備失敗,也就是說預留資源或者執行操作失敗,則協調者向參與者發起中止指令,參與者取消已經變更的事務,執行 undo 日志,釋放鎖定的資源,這裡的邏輯與兩階段送出協定的送出階段一緻。
三階段送出協定(3PC)
三階段送出協定與兩階段送出協定主要有以下兩個不同點。
- 增加了一個詢問階段,詢問階段可以確定盡可能早地發現無法執行操作而需要中止的行為,但是它并不能發現所有這種行為,隻會減少這種情況的發生。
- 在準備階段以後,協調者和參與者執行的任務中都增加了逾時,一旦逾時,則協調者和參與者都會繼續送出事務 ,預設為成功,這也是根據機率統計逾時後預設為成功的正确性最大。
三階段送出協定與兩階段送出協定相比,具有如上優點,但是一旦發生逾時,系統仍然會發生不一緻,隻不過這種情況很少見,好處是至少不會阻塞和永遠鎖定資源。
前兩節講解了兩階段送出協定和三階段送出協定,實際上它們能解決前邊的分布式事務的問題,但是遇到極端情況時,系統會産生阻塞或者不一緻的問題,需要營運或者技術人員解決。兩階段及三階段方案中都包含多個參與者、多個階段實作一個事務,實作複雜,性能也是一個很大的問題,是以,在網際網路的高并發系統中,鮮有使用兩階段送出和三階段送出協定的場景。
TCC
TCC 協定将一個任務拆分成 Try、 Confirm 、 Cancel 三個步驟,正常的流程會先執行Try可,如果執行沒有問題,則再執行 Confirm ,如果執行過程中出了問題,則執行操作的逆操作Cancel 。
TCC
從正常的流程上講,這仍然是一個兩階段送出協定,但是在執行出現問題時有一定的自我修複能力,如果任何參與者出現了問題,則協調者通過執行操作的逆操作來 Cancel 之前的操作,達到最終的一緻狀态。
如果遇到極端情況,則 TCC 會有很多問題,例如,如果在取消時一些參與者收到指令,而另一些參與者沒有收到指令,則整個系統仍然是不一緻的 。對于這種複雜的情況,系統首先會通過補償的方式嘗試自動修複,如果系統無法修複,則必須由人工參與解決。
以上的思路都是能很好解決分布式系統一緻性問題,市面也有基于這些思路實作的優秀架構,比如Seata,後續也會介紹此架構的運用。