程式設計師,你怎麼對待常見的資料一致性問題?

lu_s發表於2018-11-16

現象

應用系統中的關鍵服務絕大部分都會是對資料庫的依賴。

當多個程式同時操作同一個資料,會產生資源爭搶,資料一致性的問題。

如果只有一個資料庫伺服器,資料一致性問題也就不存在了。

可是,隨著系統訪問量、資料量的不斷增長,資料庫出現多個伺服器,又出現快取服務,又要拆分資料庫,還要分拆到不同的子應用等等。

這樣一來,資料一致性問題就會變得越來越突出。

程式設計師,你怎麼對待常見的資料一致性問題?

舉個栗子

我們來看這樣一個資料流程。

  • 使用者提交一個訂單(2個不同商家各一件商品)——資料來源頭

  • 應用伺服器驗證使用者資訊、訂單資訊、庫存資訊等等,然後將這個訂單傳送到訂單訊息佇列——訊息佇列

  • 訂單處理伺服器從訊息佇列中拿到新訂單,接下來的處理,可能做的資料操作有:

    1. 生成一個訂單/也可能會分拆為兩個訂單

    2. 更新兩個商品庫存數量

    3. 更新商家的銷售資料

    4. 生成訂單對應的支付資訊

    5. 生成使用者訂單成功的狀態資訊

思路

上面的資料處理中,涉及到的資料有:訂單資料、商品資料、商家資料、支付資料、使用者資料。

涉及到的應用和服務有:前端應用系統,訊息佇列,後端應用系統,資料庫,快取,甚至訂單、商品、商家、支付、使用者可能都是獨立的子應用。

可能大部分系統不會像上面這麼龐大。

如果前後端都是一起的,也就沒有訊息佇列。

如果也沒有這些子系統,資料庫是集中的,那可能資料一致性問題會稍微小些。

這時候,只需要注意資料庫更新的一致性就好了,比較容易想到的應對方法,就是用資料庫事務來保證。

如果這些資料不只是一份資料庫,還有快取中一份,又要考慮快取資料的更新,所以問題還是複雜了。

程式設計師,你怎麼對待常見的資料一致性問題?

資料庫更新,怎麼保證快取也能正常更新呢?

  • 程式中處理,資料庫更新後,就要馬上更新快取資料

  • 如果快取更新失敗或者程式出現異常,要有異常處理方法

  • 異常處理方法可以是程式中實時的糾正或者重試

  • 異常處理方法也可以是針對資料庫的更新,二次檢查快取資料的更新

這裡還只是一個資料庫和一個快取的情況,已經要做出這麼多事情。

那這些工作帶來的影響有哪些呢?

  • 程式開發更加複雜,不能有些許的遺漏

  • 資料驗證和重試帶來的效能下降

  • 資料庫事務帶來的資料庫瓶頸明顯

  • 二次檢查再次增加複雜度和額外開銷

程式設計師,你怎麼對待常見的資料一致性問題?

本來一個訂單處理,如果不考慮資料一致性問題,資料庫寫入/更新510次,快取寫入/更新510次,整個過程應該在10ms內完成。

但是加上資料庫事務之後,會把這些操作中涉及到的幾個表都加鎖,意味著資料的讀、寫都序列化了,整個應用系統的併發能力急劇下降。

當然,因為這裡引入快取,對資料庫的依賴會減少很多,而且還有從庫可以提供讀的服務,應用系統的訪問併發能力不至於下降太多。

但這些代價在交易處理中是難以避免的,為了解決資料一致性問題,犧牲的是訂單處理的併發能力。

對於大部分商城、網站,訂單併發量也不高,這類問題不太常發生,所以也就這麼過去了。

但是在一些促銷活動的時候,肯定還是會遇到下單等待太久的問題。

瓶頸

為了具備更大併發的訂單處理能力,單資料庫、快取肯定是行不通了。

那麼要在這麼多的子應用、大量的資料庫、快取服務中保持資料一致性又要怎麼做呢?

  • 每個子應用都要支援分散式事務,共同保證資料庫全部成功更新

  • 每個子應用各自要保證自己的資料更新一致性(異常處理、重試、二次檢查等方法同上)

上面看上去只有兩條,但是要做的事情和困難會比上面要多十倍,難百倍。

程式設計師,你怎麼對待常見的資料一致性問題?

看到這裡,是不是對於資料一致性的問題都有點絕望了。

真相

正因如此,大部分的分散式系統,大部分應用,是沒有做到資料一致性,哪怕是弱一致性。

比如:論壇裡面發帖,要更新10份左右的資料,出現髒資料是常有的,這就是沒有做到資料一致性。

比如:商城裡面庫存超賣,訂單狀態不一致等,也是因為沒有做到資料一致性。

之所以會這樣,因為投入產出嚴重不成比例,是很無奈的選擇。

資料不一致的情況畢竟比例極低,但是投入的代價卻極大。

資料不一致引發的後果,可以忍受和容忍,哪怕是發現後再修正。

那麼,還有什麼辦法可以避免或減少出現資料一致性問題呢?

下面有幾個方法可以考慮:

  • 將系統規模和容量降低,保證系統的穩定性和高效;

一個每秒鐘上百萬請求的應用系統能不能分拆為1000個每秒鐘1000請求的獨立叢集呢?

一個上百萬的商家、商品、訂單庫,能不能分拆為1000個只有1000個商家、商品、訂單的子庫呢?

  • 將資料關聯降低,減少更新次數,減少不一致問題的出現概率;

上面的訂單、庫存、商家、支付、使用者幾個資料,核心資料只有訂單,其他的幾個資料完全可以從訂單資料推匯出來,減少訂單處理中的一致性要求。

  • 將應用分拆,對效能和一致性要求高的應用獨立實現;

減少業務耦合,集中資源重點投入。


相關文章