導航:首頁 > 數據處理 > 並發扣款如何保證數據一致性

並發扣款如何保證數據一致性

發布時間:2023-02-05 05:37:18

❶ 如何保證資料庫緩存的最終一致性

對於互聯網業務來說,傳統的直接訪問資料庫方式,主要通過數據分片、一主多從等方式來扛住讀寫流量,但隨著數據量的積累和流量的激增,僅依賴資料庫來承接所有流量,不僅成本高、效率低、而且還伴隨著穩定性降低的風險。

鑒於大部分業務通常是讀多寫少(讀取頻率遠遠高於更新頻率),甚至存在讀操作數量高出寫操作多個數量級的情況。因此, 在架構設計中,常採用增加緩存層來提高系統的響應能力 ,提升數據讀寫性能、減少資料庫訪問壓力,從而提升業務的穩定性和訪問體驗。

根據 CAP 原理,分布式系統在可用性、一致性和分區容錯性上無法兼得,通常由於分區容錯無法避免,所以一致性和可用性難以同時成立。對於緩存系統來說, 如何保證其數據一致性是一個在應用緩存的同時不得不解決的問題 。

需要明確的是,緩存系統的數據一致性通常包括持久化層和緩存層的一致性、以及多級緩存之間的一致性,這里我們僅討論前者。持久化層和緩存層的一致性問題也通常被稱為雙寫一致性問題,「雙寫」意為數據既在資料庫中保存一份,也在緩存中保存一份。

對於一致性來說,包含強一致性和弱一致性 ,強一致性保證寫入後立即可以讀取,弱一致性則不保證立即可以讀取寫入後的值,而是盡可能的保證在經過一定時間後可以讀取到,在弱一致性中應用最為廣泛的模型則是最終一致性模型,即保證在一定時間之後寫入和讀取達到一致的狀態。對於應用緩存的大部分場景來說,追求的則是最終一致性,少部分對數據一致性要求極高的場景則會追求強一致性。

為了達到最終一致性,針對不同的場景,業界逐步形成了下面這幾種應用緩存的策略。


1

Cache-Aside


Cache-Aside 意為旁路緩存模式,是應用最為廣泛的一種緩存策略。下面的圖示展示了它的讀寫流程,來看看它是如何保證最終一致性的。在讀請求中,首先請求緩存,若緩存命中(cache hit),則直接返回緩存中的數據;若緩存未命中(cache miss),則查詢資料庫並將查詢結果更新至緩存,然後返回查詢出的數據(demand-filled look-aside )。在寫請求中,先更新資料庫,再刪除緩存(write-invalidate)。


1、為什麼刪除緩存,而不是更新緩存?

在 Cache-Aside 中,對於讀請求的處理比較容易理解,但在寫請求中,可能會有讀者提出疑問,為什麼要刪除緩存,而不是更新緩存?站在符合直覺的角度來看,更新緩存是一個容易被理解的方案,但站在性能和安全的角度,更新緩存則可能會導致一些不好的後果。

首先是性能 ,當該緩存對應的結果需要消耗大量的計算過程才能得到時,比如需要訪問多張資料庫表並聯合計算,那麼在寫操作中更新緩存的動作將會是一筆不小的開銷。同時,當寫操作較多時,可能也會存在剛更新的緩存還沒有被讀取到,又再次被更新的情況(這常被稱為緩存擾動),顯然,這樣的更新是白白消耗機器性能的,會導致緩存利用率不高。

而等到讀請求未命中緩存時再去更新,也符合懶載入的思路,需要時再進行計算。刪除緩存的操作不僅是冪等的,可以在發生異常時重試,而且寫-刪除和讀-更新在語義上更加對稱。

其次是安全 ,在並發場景下,在寫請求中更新緩存可能會引發數據的不一致問題。參考下面的圖示,若存在兩個來自不同線程的寫請求,首先來自線程 1 的寫請求更新了資料庫(step 1),接著來自線程 2 的寫請求再次更新了資料庫(step 3),但由於網路延遲等原因,線程 1 可能會晚於線程 2 更新緩存(step 4 晚於 step 3),那麼這樣便會導致最終寫入資料庫的結果是來自線程 2 的新值,寫入緩存的結果是來自線程 1 的舊值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。


2、為什麼先更新資料庫,而不是先刪除緩存?

另外,有讀者也會對更新資料庫和刪除緩存的時序產生疑問,那麼為什麼不先刪除緩存,再更新資料庫呢?在單線程下,這種方案看似具有一定合理性,這種合理性體現在刪除緩存成功。

但更新資料庫失敗的場景下,盡管緩存被刪除了,下次讀操作時,仍能將正確的數據寫回緩存,相對於 Cache-Aside 中更新資料庫成功,刪除緩存失敗的場景來說,先刪除緩存的方案似乎更合理一些。那麼,先刪除緩存有什麼問題呢?

問題仍然出現在並發場景下,首先來自線程 1 的寫請求刪除了緩存(step 1),接著來自線程 2 的讀請求由於緩存的刪除導致緩存未命中,根據 Cache-Aside 模式,線程 2 繼而查詢資料庫(step 2),但由於寫請求通常慢於讀請求,線程 1 更新資料庫的操作可能會晚於線程 2 查詢資料庫後更新緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存的結果是來自線程 2 中查詢到的舊值,而寫入資料庫的結果是來自線程 1 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存( step 5 ),讀取到的便是舊值。


另外,先刪除緩存,由於緩存中數據缺失,加劇資料庫的請求壓力,可能會增大緩存穿透出現的概率。

3、如果選擇先刪除緩存,再更新資料庫,那如何解決一致性問題呢?

為了避免「先刪除緩存,再更新資料庫」這一方案在讀寫並發時可能帶來的緩存臟數據,業界又提出了延時雙刪的策略,即在更新資料庫之後,延遲一段時間再次刪除緩存,為了保證第二次刪除緩存的時間點在讀請求更新緩存之後,這個延遲時間的經驗值通常應稍大於業務中讀請求的耗時。

延遲的實現可以在代碼中 sleep 或採用延遲隊列。顯而易見的是,無論這個值如何預估,都很難和讀請求的完成時間點准確銜接,這也是延時雙刪被詬病的主要原因。


4、那麼 Cache-Aside 存在數據不一致的可能嗎?

在 Cache-Aside 中,也存在數據不一致的可能性。在下面的讀寫並發場景下,首先來自線程 1 的讀請求在未命中緩存的情況下查詢資料庫(step 1),接著來自線程 2 的寫請求更新資料庫(step 2),但由於一些極端原因,線程 1 中讀請求的更新緩存操作晚於線程 2 中寫請求的刪除緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存中的是來自線程 1 的舊值,而寫入資料庫中的是來自線程 2 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。

這種場景的出現,不僅需要緩存失效且讀寫並發執行,而且還需要讀請求查詢資料庫的執行早於寫請求更新資料庫,同時讀請求的執行完成晚於寫請求。足以見得,這種 不一致場景產生的條件非常嚴格,在實際的生產中出現的可能性較小 。


除此之外,在並發環境下,Cache-Aside 中也存在讀請求命中緩存的時間點在寫請求更新資料庫之後,刪除緩存之前,這樣也會導致讀請求查詢到的緩存落後於資料庫的情況。


雖然在下一次讀請求中,緩存會被更新,但如果業務層面對這種情況的容忍度較低,那麼可以採用加鎖在寫請求中保證「更新資料庫&刪除緩存」的串列執行為原子性操作(同理也可對讀請求中緩存的更新加鎖)。 加鎖勢必會導致吞吐量的下降,故採取加鎖的方案應該對性能的損耗有所預期。


2

補償機制


我們在上面提到了,在 Cache-Aside 中可能存在更新資料庫成功,但刪除緩存失敗的場景,如果發生這種情況,那麼便會導致緩存中的數據落後於資料庫,產生數據的不一致的問題。

其實,不僅 Cache-Aside 存在這樣的問題,在延時雙刪等策略中也存在這樣的問題。針對可能出現的刪除失敗問題,目前業界主要有以下幾種補償機制。

1、刪除重試機制

由於同步重試刪除在性能上會影響吞吐量,所以常通過引入消息隊列,將刪除失敗的緩存對應的 key 放入消息隊列中,在對應的消費者中獲取刪除失敗的 key ,非同步重試刪除。這種方法在實現上相對簡單,但由於刪除失敗後的邏輯需要基於業務代碼的 trigger 來觸發 ,對業務代碼具有一定入侵性。


鑒於上述方案對業務代碼具有一定入侵性,所以需要一種更加優雅的解決方案,讓緩存刪除失敗的補償機制運行在背後,盡量少的耦合於業務代碼。一個簡單的思路是通過後台任務使用更新時間戳或者版本作為對比獲取資料庫的增量數據更新至緩存中,這種方式在小規模數據的場景可以起到一定作用,但其擴展性、穩定性都有所欠缺。

一個相對成熟的方案是基於 MySQL 資料庫增量日誌進行解析和消費,這里較為流行的是阿里巴巴開源的作為 MySQL binlog 增量獲取和解析的組件 canal(類似的開源組件還有 Maxwell、Databus 等)。

canal sever 模擬 MySQL slave 的交互協議,偽裝為 MySQL slave,向 MySQL master 發送 mp 協議,MySQL master 收到 mp 請求,開始推送 binary log 給 slave (即 canal sever ),canal sever 解析 binary log 對象(原始為 byte 流),可由 canal client 拉取進行消費,同時 canal server 也默認支持將變更記錄投遞到 MQ 系統中,主動推送給其他系統進行消費。

在 ack 機制的加持下,不管是推送還是拉取,都可以有效的保證數據按照預期被消費。當前版本的 canal 支持的 MQ 有 Kafka 或者 RocketMQ。另外, canal 依賴 ZooKeeper 作為分布式協調組件來實現 HA ,canal 的 HA 分為兩個部分:


那麼,針對緩存的刪除操作便可以在 canal client 或 consumer 中編寫相關業務代碼來完成。這樣,結合資料庫日誌增量解析消費的方案以及 Cache-Aside 模型,在讀請求中未命中緩存時更新緩存(通常這里會涉及到復雜的業務邏輯),在寫請求更新資料庫後刪除緩存,並基於日誌增量解析來補償資料庫更新時可能的緩存刪除失敗問題,在絕大多數場景下,可以有效的保證緩存的最終一致性。

另外需要注意的是,還應該隔離事務與緩存,確保資料庫入庫後再進行緩存的刪除操作。 比如考慮到資料庫的主從架構,主從同步及讀從寫主的場景下,可能會造成讀取到從庫的舊數據後便更新了緩存,導致緩存落後於資料庫的問題,這就要求對緩存的刪除應該確保在資料庫操作完成之後。所以,基於 binlog 增量日誌進行數據同步的方案,可以通過選擇解析從節點的 binlog,來避免主從同步下刪除緩存過早的問題。

3、數據傳輸服務 DTS


3

Read-Through


Read-Through 意為讀穿透模式,它的流程和 Cache-Aside 類似,不同點在於 Read-Through 中多了一個訪問控制層,讀請求只和該訪問控制層進行交互,而背後緩存命中與否的邏輯則由訪問控制層與數據源進行交互,業務層的實現會更加簡潔,並且對於緩存層及持久化層交互的封裝程度更高,更易於移植。


4

Write-Through


Write-Through 意為直寫模式,對於 Write-Through 直寫模式來說,它也增加了訪問控制層來提供更高程度的封裝。不同於 Cache-Aside 的是,Write-Through 直寫模式在寫請求更新資料庫之後,並不會刪除緩存,而是更新緩存。


這種方式的 優勢在於讀請求過程簡單 ,不需要查詢資料庫更新緩存等操作。但其劣勢也非常明顯,除了上面我們提到的更新資料庫再更新緩存的弊端之外,這種方案還會造成更新效率低,並且兩個寫操作任何一次寫失敗都會造成數據不一致。

如果要使用這種方案, 最好可以將這兩個操作作為事務處理,可以同時失敗或者同時成功,支持回滾,並且防止並發環境下的不一致 。另外,為了防止緩存擾動的頻發,也可以給緩存增加 TTL 來緩解。

站在可行性的角度,不管是 Write-Through 模式還是 Cache-Aside 模式,理想狀況下都可以通過分布式事務保證緩存層數據與持久化層數據的一致性,但在實際項目中,大多都對一致性的要求存在一些寬容度,所以在方案上往往有所折衷。

Write-Through 直寫模式適合寫操作較多,並且對一致性要求較高的場景,在應用 Write-Through 模式時,也需要通過一定的補償機制來解決它的問題。首先,在並發環境下,我們前面提到了先更新資料庫,再更新緩存會導致緩存和資料庫的不一致,那麼先更新緩存,再更新資料庫呢?

這樣的操作時序仍然會導致下面這樣線程 1 先更新緩存,最後更新資料庫的情況,即由於線程 1 和 線程 2 的執行不確定性導致資料庫和緩存的不一致。這種由於線程競爭導致的緩存不一致,可以通過分布式鎖解決,保證對緩存和資料庫的操作僅能由同一個線程完成。對於沒有拿到鎖的線程,一是通過鎖的 timeout 時間進行控制,二是將請求暫存在消息隊列中順序消費。


在下面這種並發執行場景下,來自線程 1 的寫請求更新了資料庫,接著來自線程 2 的讀請求命中緩存,接著線程 1 才更新緩存,這樣便會導致線程 2 讀取到的緩存落後於資料庫。同理,先更新緩存後更新資料庫在寫請求和讀請求並發時,也會出現類似的問題。面對這種場景,我們也可以加鎖解決。


另在,在 Write-Through 模式下,不管是先更新緩存還是先更新資料庫,都存在更新緩存或者更新資料庫失敗的情況,上面提到的重試機制和補償機制在這里也是奏效的。


5

Write-Behind


Write behind 意為非同步回寫模式,它也具有類似 Read-Through/Write-Through 的訪問控制層,不同的是,Write behind 在處理寫請求時,只更新緩存而不更新資料庫,對於資料庫的更新,則是通過批量非同步更新的方式進行的,批量寫入的時間點可以選在資料庫負載較低的時間進行。

在 Write-Behind 模式下,寫請求延遲較低,減輕了資料庫的壓力,具有較好的吞吐性。但資料庫和緩存的一致性較弱,比如當更新的數據還未被寫入資料庫時,直接從資料庫中查詢數據是落後於緩存的。同時,緩存的負載較大,如果緩存宕機會導致數據丟失,所以需要做好緩存的高可用。顯然,Write behind 模式下適合大量寫操作的場景,常用於電商秒殺場景中庫存的扣減。


6

Write-Around


如果一些非核心業務,對一致性的要求較弱,可以選擇在 cache aside 讀模式下增加一個緩存過期時間,在寫請求中僅僅更新資料庫,不做任何刪除或更新緩存的操作,這樣,緩存僅能通過過期時間失效。這種方案實現簡單,但緩存中的數據和資料庫數據一致性較差,往往會造成用戶的體驗較差,應慎重選擇。


7

總結


在解決緩存一致性的過程中,有多種途徑可以保證緩存的最終一致性,應該根據場景來設計合適的方案,讀多寫少的場景下,可以選擇採用「Cache-Aside 結合消費資料庫日誌做補償」的方案,寫多的場景下,可以選擇採用「Write-Through 結合分布式鎖」的方案 ,寫多的極端場景下,可以選擇採用「Write-Behind」的方案。

❷ 並發操作會帶來哪些數據不一致性

並發操作帶來的數據不一致性包括三類:丟失修改、不可重復讀和讀「臟」數據。
避免不一致性的方法和技術就是並發控制,最常用的技術是封鎖技術;也可以用其他技術,例如在分布式資料庫系統中可以採用時間戳方法來進行並發控制。
丟失修改:兩個事務T1和T2讀入同一數據並修改,T2提交的結果破壞了(覆蓋了)T1提交的結果,導致T1的修改被丟失。

不可重復讀:不可重復讀是指事務T1讀取數據後,事務T2執行更新操作,使T1無法再現前一次讀取結果。
讀「臟」數據:讀「臟」數據是指事務T1修改某一數據,並將其寫回磁碟,事務T2讀取同一數據後,T1由於某種原因被撤銷,這時T1已修改過的數據恢復原值,T2讀到的數據就與資料庫中的數據不一致,則T2讀到的數據就為「臟」數據,即不正確的數據。

❸ 銀行或者金融系統如何保證數據一致性的呢

在系統間保持數據一致性的方法無非這么幾種:
掉單查詢,在發現有未知數據時通過查詢的方式恢復流水。
對賬恢復,T+1針對T日的全量數據進行比對一致性,包括狀態、金額等關鍵信息。
異常恢復,可以和掉單查詢互為補充,由下游檢測異常並進行恢復

❹ 雙十一是怎麼保證高並發,分布式系統中,數據一致性

前言 在系統開發過程中,經常遇到數據重復插入、重復更新、消息重發發送等等問題,因為應用系統的復雜邏輯以及網路交互存在的不確定性,會導致這一重復現象,但是有些邏輯是需要有冪等特性的,否則造成的後果會比較嚴重,例如訂單重復創建,這時候帶來的問題可是非同一般啊。 什麼是系統的冪等性 冪等是數據中得一個概念,表示N次變換和1次變換的結果相同。 高並發的系統如何保證冪等性? 1.查詢 查詢的API,可以說是天然的冪等性,因為你查詢一次和查詢兩次,對於系統來講,沒有任何數據的變更,所以,查詢一次和查詢多次一樣的。 2.MVCC方案 多版本並發控制,update with condition,更新帶條件,這也是在系統設計的時候,合理的選擇樂觀鎖,通過version或者其他條件,來做樂觀鎖,這樣保證更新及時在並發的情況下,也不會有太大的問題。 例如:update table_xxx set name=#name#,version=version+1 where version=#version# ,或者是 update table_xxx set quality=quality-#subQuality# where quality-#subQuality# >= 0 。 3.單獨的去重表 如果涉及到的去重的地方特別多,例如ERP系統中有各種各樣的業務單據,每一種業務單據都需要去重,這時候,可以單獨搞一張去重表,在插入數據的時候,插入去重表,利用資料庫的唯一索引特性,保證唯一的邏輯。 4.分布式鎖 還是拿插入數據的例子,如果是分布是系統,構建唯一索引比較困難,例如唯一性的欄位沒法確定,這時候可以引入分布式鎖,通過第三方的系統,在業務系統插入數據或者更新數據,獲取分布式鎖,然後做操作,之後釋放鎖,這樣其實是把多線程並發的鎖的思路,引入多多個系統,也就是分布式系統中得解決思路。 5.刪除數據 刪除數據,僅僅第一次刪除是真正的操作數據,第二次甚至第三次刪除,直接返回成功,這樣保證了冪等。 6.插入數據的唯一索引 插入數據的唯一性,可以通過業務主鍵來進行約束,例如一個特定的業務場景,三個欄位肯定確定唯一性,那麼,可以在資料庫表添加唯一索引來進行標示。 這里有一個場景,API層面的冪等,例如提交數據,如何控制重復提交,這里可以在提交數據的form表單或者客戶端軟體,增加一個唯一標示,然後服務端,根據這個UUID來進行去重,這樣就能比較好的做到API層面的唯一標識。 7.狀態機冪等 在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機,就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候,如果狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。 以上就是高並發系統數據冪等的解決方案的資料整理,後續繼續補充相關知識,謝謝大家對本站的支持!

❺ 保證分布式系統數據一致性的6種方案

編者按 :本文由「高可用架構後花園」群討論整理而成。

有人的地方,就有江湖

有江湖的地方,就有紛爭

在電商等業務中,系統一般由多個獨立的服務組成,如何解決分布式調用時候數據的一致性?

具體業務場景如下,比如一個業務操作,如果同時調用服務 A、B、C,需要滿足要麼同時成功;要麼同時失敗。A、B、C 可能是多個不同部門開發、部署在不同伺服器上的遠程服務。

在分布式系統來說,如果不想犧牲一致性,CAP 理論告訴我們只能放棄可用性,這顯然不能接受。為了便於討論問題,先簡單介紹下數據一致性的基礎理論。

強一致

弱一致性

最終一致性

在工程實踐上,為了保障系統的可用性,互聯網系統大多將強一致性需求轉換成最終一致性的需求,並通過系統執行冪等性的保證,保證數據的最終一致性。但在電商等場景中,對於數據一致性的解決方法和常見的互聯網系統(如 MySQL 主從同步)又有一定區別,群友的討論分成以下 6 種解決方案。

業務整合方案主要採用將介面整合到本地執行的方法。拿問題場景來說,則可以將服務 A、B、C 整合為一個服務 D 給業務,這個服務 D 再通過轉換為本地事務的方式,比如服務 D 包含本地服務和服務 E,而服務 E 是本地服務 A ~ C 的整合。

優點: 解決(規避)了分布式事務。

缺點: 顯而易見,把本來規劃拆分好的業務,又耦合到了一起,業務職責不清晰,不利於維護。

由於這個方法存在明顯缺點,通常不建議使用。

此方案的核心是將需要分布式處理的任務通過消息日誌的方式來非同步執行。消息日誌可以存儲到本地文本、資料庫或消息隊列,再通過業務規則自動或人工發起重試。人工重試更多的是應用於支付場景,通過對賬系統對事後問題的處理。

消息日誌方案的核心是保證服務介面的冪等性。

考慮到網路通訊失敗、數據丟包等原因,如果介面不能保證冪等性,數據的唯一性將很難保證。

eBay 方式的主要思路如下。

Base:一種 Acid 的替代方案

此方案是 eBay 的架構師 Dan Pritchett 在 2008 年發表給 ACM 的文章,是一篇解釋 BASE 原則,或者說最終一致性的經典文章。文中討論了 BASE 與 ACID 原則在保證數據一致性的基本差異。

如果 ACID 為分區的資料庫提供一致性的選擇,那麼如何實現可用性呢?答案是

BASE (basically available, soft state, eventually consistent)

BASE 的可用性是通過 支持局部故障 而不是系統全局故障來實現的。下面是一個簡單的例子:如果將用戶分區在 5 個資料庫伺服器上,BASE 設計鼓勵類似的處理方式,一個用戶資料庫的故障隻影響這台特定主機那 20% 的用戶。這里不涉及任何魔法,不過它確實可以帶來更高的可感知的系統可用性。

文章中描述了一個最常見的場景,如果產生了一筆交易,需要在交易表增加記錄,同時還要修改用戶表的金額。這兩個表屬於不同的遠程服務,所以就涉及到分布式事務一致性的問題。

文中提出了一個經典的解決方法,將主要修改操作以及更新用戶表的消息 放在一個本地事務 來完成。同時為了避免重復消費用戶表消息帶來的問題,達到多次重試的冪等性, 增加一個更新記錄表 updates_applied 來記錄已經處理過的消息。

系統的執行偽代碼如下

(點擊可全屏縮放圖片)

基於以上方法,在第一階段,通過本地的資料庫的事務保障,增加了 transaction 表及消息隊列 。

在第二階段,分別讀出消息隊列(但不刪除),通過判斷更新記錄表 updates_applied 來檢測相關記錄是否被執行,未被執行的記錄會修改 user 表,然後增加一條操作記錄到 updates_applied,事務執行成功之後再刪除隊列。

通過以上方法,達到了分布式系統的最終一致性。進一步了解 eBay 的方案可以參考文末鏈接。

隨著業務規模不斷地擴大,電商網站一般都要面臨拆分之路。就是將原來一個單體應用拆分成多個不同職責的子系統。比如以前可能將面向用戶、客戶和運營的功能都放在一個系統里,現在拆分為訂單中心、代理商管理、運營系統、報價中心、庫存管理等多個子系統。

拆分首先要面臨的是什麼呢?

最開始的單體應用所有功能都在一起,存儲也在一起。比如運營要取消某個訂單,那直接去更新訂單表狀態,然後更新庫存表就 ok 了。因為是單體應用,庫在一起,這些都可以在一個事務里,由關系資料庫來保證一致性。

但拆分之後就不同了,不同的子系統都有自己的存儲。比如訂單中心就只管理自己的訂單庫,而庫存管理也有自己的庫。那麼運營系統取消訂單的時候就是通過介面調用等方式來調用訂單中心和庫存管理的服務了,而不是直接去操作庫。這就涉及一個『 分布式事務 』的問題。

分布式事務有兩種解決方式

1. 優先使用非同步消息。

上文已經說過,使用非同步消息 Consumer 端需要實現冪等。

冪等有兩種方式, 一種方式是業務邏輯保證冪等 。比如接到支付成功的消息訂單狀態變成支付完成,如果當前狀態是支付完成,則再收到一個支付成功的消息則說明消息重復了,直接作為消息成功處理。

另外一種方式如果業務邏輯無法保證冪等,則要增加一個去重表或者類似的實現 。對於 procer 端在業務資料庫的同實例上放一個消息庫,發消息和業務操作在同一個本地事務里。發消息的時候消息並不立即發出,而是向消息庫插入一條消息記錄,然後在事務提交的時候再非同步將消息發出,發送消息如果成功則將消息庫里的消息刪除,如果遇到消息隊列服務異常或網路問題,消息沒有成功發出那麼消息就留在這里了,會有另外一個服務不斷地將這些消息掃出重新發送。

2. 有的業務不適合非同步消息的方式,事務的各個參與方都需要同步的得到結果。 這種情況的實現方式其實和上面類似,每個參與方的本地業務庫的同實例上面放一個事務記錄庫。

比如 A 同步調用 B,C。A 本地事務成功的時候更新本地事務記錄狀態,B 和 C 同樣。如果有一次 A 調用 B 失敗了,這個失敗可能是 B 真的失敗了,也可能是調用超時,實際 B 成功。則由一個中心服務對比三方的事務記錄表,做一個最終決定。假設現在三方的事務記錄是 A 成功,B 失敗,C 成功。那麼最終決定有兩種方式,根據具體場景:

對 b 場景做一個特殊說明:比如 B 是扣庫存服務,在第一次調用的時候因為某種原因失敗了,但是重試的時候庫存已經變為 0,無法重試成功,這個時候只有回滾 A 和 C 了。

那麼可能有人覺得在業務庫的同實例里放消息庫或事務記錄庫,會對業務侵入,業務還要關心這個庫,是否一個合理的設計?

實際上可以依靠運維的手段來簡化開發的侵入,我們的方法是讓 DBA 在公司所有 MySQL 實例上預初始化這個庫,通過框架層(消息的客戶端或事務 RPC 框架)透明的在背後操作這個庫,業務開發人員只需要關心自己的業務邏輯,不需要直接訪問這個庫。

總結起來,其實兩種方式的根本原理是類似的,也就是 將分布式事務轉換為多個本地事務,然後依靠重試等方式達到最終一致性

交易創建的一般性流程

我們把交易創建流程抽象出一系列可擴展的功能點,每個功能點都可以有多個實現(具體的實現之間有組合/互斥關系)。把各個功能點按照一定流程串起來,就完成了交易創建的過程。

面臨的問題

每個功能點的實現都可能會依賴外部服務。那麼如何保證各個服務之間的數據是一致的呢?比如鎖定優惠券服務調用超時了,不能確定到底有沒有鎖券成功,該如何處理?再比如鎖券成功了,但是扣減庫存失敗了,該如何處理?

方案選型

服務依賴過多,會帶來管理復雜性增加和穩定性風險增大的問題。試想如果我們強依賴 10 個服務,9 個都執行成功了,最後一個執行失敗了,那麼是不是前面 9 個都要回滾掉?這個成本還是非常高的。

所以在拆分大的流程為多個小的本地事務的前提下,對於非實時、非強一致性的關聯業務寫入,在本地事務執行成功後,我們選擇發消息通知、關聯事務非同步化執行的方案。

消息通知往往不能保證 100% 成功;且消息通知後,接收方業務是否能執行成功還是未知數。前者問題可以通過重試解決;後者可以選用事務消息來保證。

所以目前只剩下需要實時同步做、有強一致性要求的業務場景了。在交易創建過程中,鎖券和扣減庫存是這樣的兩個典型場景。

要保證多個系統間數據一致,乍一看,必須要引入分布式事務框架才能解決。但引入非常重的類似二階段提交分布式事務框架會帶來復雜性的急劇上升;在電商領域,絕對的強一致是過於理想化的,我們可以選擇准實時的最終一致性。

我們在交易創建流程中, 首先創建一個不可見訂單 ,然後在同步調用鎖券和扣減庫存時,針對調用異常(失敗或者超時),發出廢單消息到MQ。如果消息發送失敗,本地會做時間階梯式的非同步重試;優惠券系統和庫存系統收到消息後,會進行判斷是否需要做業務回滾,這樣就准實時地保證了多個本地事務的最終一致性。

業界常用的還有支付寶的一種 xts 方案,由支付寶在 2PC 的基礎上改進而來。主要思路如下,大部分信息引用自官方網站。

分布式事務服務簡介

分布式事務服務 (Distributed Transaction Service, DTS) 是一個分布式事務框架,用來保障在大規模分布式環境下事務的最終一致性。DTS 從架構上分為 xts-client 和 xts-server 兩部分,前者是一個嵌入客戶端應用的 JAR 包,主要負責事務數據的寫入和處理;後者是一個獨立的系統,主要負責異常事務的恢復。

核心特性

傳統關系型資料庫的事務模型必須遵守 ACID 原則。在單資料庫模式下,ACID 模型能有效保障數據的完整性,但是在大規模分布式環境下,一個業務往往會跨越多個資料庫,如何保證這多個資料庫之間的數據一致性,需要其他行之有效的策略。在 JavaEE 規范中使用 2PC (2 Phase Commit, 兩階段提交) 來處理跨 DB 環境下的事務問題,但是 2PC 是反可伸縮模式,也就是說,在事務處理過程中,參與者需要一直持有資源直到整個分布式事務結束。這樣,當業務規模達到千萬級以上時,2PC 的局限性就越來越明顯,系統可伸縮性會變得很差。基於此,我們採用 BASE 的思想實現了一套類似 2PC 的分布式事務方案,這就是 DTS。DTS在充分保障分布式環境下高可用性、高可靠性的同時兼顧數據一致性的要求,其最大的特點是保證數據最終一致 (Eventually consistent)。

簡單的說,DTS 框架有如下特性:

以下是分布式事務框架的流程圖

實現

與 2PC 協議比較

1. 電商業務

公司的支付部門,通過接入其它第三方支付系統來提供支付服務給業務部門,支付服務是一個基於 Dubbo 的 RPC 服務。

對於業務部門來說,電商部門的訂單支付,需要調用

從業務規則上需要同時保證業務數據的實時性和一致性,也就是支付成功必須加積分。

我們採用的方式是同步調用,首先處理本地事務業務。考慮到積分業務比較單一且業務影響低於支付,由積分平台提供增加與回撤介面。

具體的流程是先調用積分平台增加用戶積分,再調用支付平台進行支付處理,如果處理失敗,catch 方法調用積分平台的回撤方法,將本次處理的積分訂單回撤。

(點擊圖片可以全屏縮放)

2. 用戶信息變更

分布式服務對衍生的配套系統要求比較多,特別是我們基於消息、日誌的最終一致性方案,需要考慮消息的積壓、消費情況、監控、報警等。

In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability.

英文版 : http://queue.acm.org/detail.cfm?id=1394128

中文版: http://article.yeeyan.org/view/167444/125572

感謝李玉福、余昭輝、蘑菇街七公提供方案,其他多位群成員對本文內容亦有貢獻。

本文編輯李玉福、Tim Yang,轉載請註明來自@高可用架構

❻ 如何保證數據的一致性

數據一致性通常指關聯數據之間的邏輯關系是否正確和完整。而數據存儲的一致性模型則可以認為是存儲系統和數據使用者之間的一種約定。如果使用者遵循這種約定,則可以得到系統所承諾的訪問結果常用的一致性模型有:
a、嚴格一致性(linearizability, strict/atomic Consistency):讀出的數據始終為最近寫入的數據。這種一致性只有全局時鍾存在時才有可能,在分布式網路環境不可能實現。

b、順序一致性(sequential consistency):所有使用者以同樣的順序看到對同一數據的操作,但是該順序不一定是實時的。
c、因果一致性(causal consistency):只有存在因果關系的寫操作才要求所有使用者以相同的次序看到,對於無因果關系的寫入則並行進行,無次序保證。因果一致性可以看做對順序一致性性能的一種優化,但在實現時必須建立與維護因果依賴圖,是相當困難的。
d、管道一致性(PRAM/FIFO consistency):在因果一致性模型上的進一步弱化,要求由某一個使用者完成的寫操作可以被其他所有的使用者按照順序的感知到,而從不同使用者中來的寫操作則無需保證順序,就像一個一個的管道一樣。 相對來說比較容易實現。
e、弱一致性(weak consistency):只要求對共享數據結構的訪問保證順序一致性。對於同步變數的操作具有順序一致性,是全局可見的,且只有當沒有寫操作等待處理時才可進行,以保證對於臨界區域的訪問順序進行。在同步時點,所有使用者可以看到相同的數據。
f、 釋放一致性(release consistency):弱一致性無法區分使用者是要進入臨界區還是要出臨界區, 釋放一致性使用兩個不同的操作語句進行了區分。需要寫入時使用者acquire該對象,寫完後release,acquire-release之間形成了一個臨界區,提供 釋放一致性也就意味著當release操作發生後,所有使用者應該可以看到該操作。
g、最終一致性(eventual consistency):當沒有新更新的情況下,更新最終會通過網路傳播到所有副本點,所有副本點最終會一致,也就是說使用者在最終某個時間點前的中間過程中無法保證看到的是新寫入的數據。可以採用最終一致性模型有一個關鍵要求:讀出陳舊數據是可以接受的。
h、delta consistency:系統會在delta時間內達到一致。這段時間內會存在一個不一致的窗口,該窗口可能是因為log shipping的過程導致。這是書上的原話。。我也搞不很清楚。。 資料庫完整性(Database Integrity)是指資料庫中數據的正確性和相容性。資料庫完整性由各種各樣的完整性約束來保證,因此可以說資料庫完整性設計就是資料庫完整性約束的設計。包括實體完整性。域完整性。參照完整性。用戶定義完整性。可以主鍵。check約束。外鍵來一一實現。這個使用較多。

❼ 並發編程數據共享有哪幾種機制如何保證數據同步

1、多進程並發
在傳統UNIX中較常用,針對每一種單獨的業務邏輯的實例生成不同的線程進行處理。典型的程序實例是針對TCP的多個不同的客戶端連接,fork出多個子進程進行處理,每一個客戶端對應一個單獨的子進程,在子進程處理退出後,由父進程回收其資源。
優點:各進程間的地址空間相互隔離,不會因為某些不當操作將整個應用搞掛。
業務邏輯代碼簡單清晰,代碼平鋪直敘,沒有復雜的非同步狀態邏輯。
缺點:如果需要在進程間進行交互或者共享數據,需要使用IPC。
2、多線程並發
在現代操作系統windows、linux中很常用,針對單獨的業務邏輯的不同的實例在同一個進程中創建多個線程進行並發處理。典型的例子是,TCP的多個客戶端在同一個進程中處理,針對每個客戶端都單獨對應的線程進行交互,共享同一個進程的所有資源。
優點:共享進程空間,訪問共享數據非常容易。
沒有多的進程空間開銷,線程上下文切換快,調度效率比多進程高。
業務邏輯代碼簡單清晰,代碼平鋪直敘,沒有復雜的非同步狀態邏輯。
缺點:維護線程的工作由進程內部代碼處理,比如線程數量,增加一定的復雜性。
線程間共享數據的競爭關系復雜,需要處理同步和死鎖問題。
3、IO多路復用
即在單線程式控制制多個非同步業務邏輯,也就是事件驅動多個業務的狀態處理,典型的有windows中的消息處理機制,還有linux中的信號量處理。可以在單一線程中,處理多種不同的業務邏輯,比如同時處理用戶輸出,滑鼠點擊,窗口重繪和網路輸入。
優點:所有業務實例的邏輯在單一線程中處理,排除代碼時序BUG,理論上不存在競爭和死鎖問題。
沒有多的進程空間開銷,也沒有上下文切換問題,CPU利用率高。
共享進程空間,訪問共享數據非常容易。
缺點:
線程需要管理多個不同實例的狀態機,並正確處理對應事件導致不同狀態的遷移。
業務種類多的情況下,需要人為代碼控制多種狀態機。
並發點越多造成狀態越多,管理粒度越細, 業務邏輯代碼不是順序的,不容易維護和理解。
非同步狀態過多,造成資源管理較為復雜,容易產生資源泄漏。

❽ 如何確保不同系統數據的一致性

源系統通常會相互交換數據,然後修改或傳輸這些數據。在許多報表系統中,最難完成的任務之一就是保持非規范化數據的一致性和同步性。 我們通常使用一個中央數據倉庫確定特定數據元素的唯一可靠數據源。如果多個源系統擁有相同的數據副本,數據倉庫中存儲的單個數據表現可以作為真實性的判斷。另一個相關問題是過載問題。例如,如果有三個系統,每個系統都保存訂單信息,一個作為訂單處理系統,第二個負責連接合作夥伴供應商,第三個是支付帳號系統。每一個系統都擁有日期輸入域,但是每一個域都不相同,而且每一個數據點都非常重要。用戶必須確定他們需要的各個數據點,使數據在各個報表中保持一致。大多數組織都會使用多個報表系統,它們擁有不同的數據處理方式。有一些需要詳細的交易數據,其他系統則需要匯總數據。從一個數據源獲得數據,有利於保證不同系統的數據一致性,以及減少數據混亂和報表錯誤問題。 擁有一個中央信息源也有利於簡化新報表的創建過程。由於數據位於一個位置,而且數據相關性已經完全確定,所以報表的創建會更簡單一些。高效地確定目標知識庫報表數據,有利於加快新報表項目的完成和降低其成本。 當然,從這個數據源創建的報表越多,對服務的要求就越高。與只包含小規模數據的小型獨立系統相比,這種系統需要緩存數據,快速響應查詢,其要求是完全不一樣的。實現唯一企業數據源的效率還需要對數據倉庫進行優化,使之能夠支持更大的數據量要求。 註:雖然擁有唯一一個真實數據源有很多好處,但是其實現難度也很大。人們偏向於保存自己的本地數據。創建一個中央數據倉庫,保存大規模的統一數據,會迫使人們交出「自己的」數據——以換取一個更大范圍的整體組織數據視圖,以及了解其他部門的交互。 這些報表需求也迫使系統提到創建報表信息的速度。將源系統的數據移動到數據倉庫,以及准備數據時進行的ETL和分析處理,都有利於確定數據倉庫的容量和性能需求。 中央企業數據能夠高數據查找速度和簡化數據查找過程。它有利於簡化新報表的創建過程和降低其成本。它能夠優化交流和減少錯誤。 通過建立統一且簡單的唯一數據源,公司可以獲得許多的好處。隨著業務的增長和發展,系統本身也會變大,成為一種大型數據倉庫。

❾ 如何用封鎖機制保證數據的一致性

用封鎖機制保證數據的一致性的幾種方法。
1、內部級封鎖,內部級封鎖是用於保護ORACLE內部結構,由系統內部實現,用戶不能訪問,因此我們不必對此做過多的了解。
2、DDL級封鎖(字典/語法分析封鎖)DDL級封鎖也是由ORACLERDBMS來控制,它用於保護數據字典和數據定義改變時的一致性和完整性。它是系統在對SQL定義語句作語法分析時自動地加鎖,無需用戶干予。字典/語法分析封鎖共分三類:
(1)、字典操作鎖:用於對字典操作時,鎖住數據字典,此封鎖是獨占的,從而保護任何一個時刻僅能對一個字典操作。
(2)、字典定義鎖:用於防止在進行字典操作時又進行語法分析,這樣可以避免在查詢字典的同時改動某個表的結構。
(3)、表定義鎖:用於一個SQL語句正當訪問某個表時,防止字典中與該表有關的項目被修改。
3、DML級封鎖,DML級封鎖用於控制並發事務中的數據操縱,保證數據的一致性和完整性,其封鎖對象可以是表或行。
對用戶的數據操縱,Oracle可以自動為操縱的數據進行封鎖,但如果有操縱授權,則為滿足並發操縱的需要另外實施封鎖。DML封鎖可由一個用戶進程以顯式的方式加鎖,也可通過某些SQL語句隱含方式實現。

閱讀全文

與並發扣款如何保證數據一致性相關的資料

熱點內容
喜歡消費的女人用什麼產品 瀏覽:527
表格數據變日期了怎麼辦 瀏覽:470
秋眸是什麼產品 瀏覽:547
門面招租怎麼寫信息 瀏覽:885
有人咨詢代理怎麼發朋友圈 瀏覽:915
什麼叫長期交易者 瀏覽:321
混合型皮膚適合什麼牌子的產品 瀏覽:306
程序員屬於霍蘭德里哪個類型 瀏覽:583
所需的五個數據在同一列如何引用 瀏覽:286
微商怎麼做人脈代理 瀏覽:357
怎麼去龍洞華僑職業技術學校 瀏覽:299
你是如何獲取租房信息的 瀏覽:931
代理費多少錢什麼意思 瀏覽:189
安信證券交易界面怎麼顯示 瀏覽:183
東陽賣狗的寵物市場在哪裡 瀏覽:98
不當交易檢察官最後怎麼樣了 瀏覽:964
xbox怎麼交易 瀏覽:296
交易貓如何100退款成功 瀏覽:995
高中生信息泄露有什麼後果 瀏覽:332
如何構建企業信息利用 瀏覽:978