由於資料量的巨大,大部分web應用都需要部署很多個資料庫例項。這樣,有些使用者操作就可能需要去修改多個資料庫例項中的資料。傳統的解決方法是使用分布式事務保證資料的全域性一致性,經典的方法是使用兩階段提交協議。
長期以來,分布式事務提供的優雅的全域性acid保證麻醉了應用開發者的心靈,很多人都不敢越雷池一步,想像沒有分布式事務的世界會是怎樣。如今就如mysql和postgresql這類面向低端使用者的開源資料庫都支援分布式事務了,開發者更是沉醉其中,不去考慮分布式事務是否給系統帶來了傷害。
事實上,有所得必有所失,分布式事務提供的acid保證是以損害系統的可用性、效能與可伸縮性為代價的。只有在參與分布式事務的各個資料庫例項都能夠正常工作的前提下,分布式事務才能夠順利完成,只要有乙個工作不正常,整個事務就不能完成。這樣,系統的可用性就相當於參加分布式事務的各例項的可用性之積,例項越多,可用性下降越明顯。從效能和可伸縮性角度看,首先是事務的總持續時間通常是各例項操作時間之和,因為乙個事務中的各個操作通常是順序執行的,這樣事務的響應時間就會增加很多;其次是一般web應用的事務都不大,單機操作時間也就幾毫秒甚至不到1毫秒,一但涉及到分布式事務,提交時節點間的網路通訊往返過程也為毫秒級別,對事務響應時間的影響也不可忽視。由於事務持續時間延長,事務對相關資源的鎖定時間也相應增加,從而可能嚴重增加了併發衝突,影響到系統吞吐率和可伸縮性。
正是由於分布式事務有以上問題,ebay在設計上就不採用分布式事務,而是通過其它途徑來解決資料一致性問題。其中使用的最重要的技術就是訊息佇列和訊息應用狀態表。
舉個例子。假設系統中有以下兩個表
user(id, name, amt_sold, amt_bought)
transaction(xid, seller_id, buyer_id, amount)
其中user表記錄使用者交易彙總資訊,transaction表記錄每個交易的詳細資訊。
這樣,在進行一筆交易時,若使用事務,就需要對資料庫進行以下操作:
begin;
insert into transaction values(xid, $seller_id, $buyer_id, $amount);
update user set amt_sold = amt_sold + $amount where id = $seller_id;
update user set amt_bought = amt_bought + $amount where id = $buyer_id;
commit;
即在transaction表中記錄交易資訊,然後更新賣家和買家的狀態。
假設transaction表和user表儲存在不同的節點上,那麼上述事務就是乙個分布式事務。要消除這一分布式事務,將它拆分成兩個子事務,乙個更新transaction表,乙個更新user表是不行的,因為有可能transaction表更新成功後,更新user失敗,系統將不能恢復到一致狀態。
解決方案是使用訊息佇列。如下所示,先啟動乙個事務,更新transaction表後,並不直接去更新user表,而是將要對user表進行的更新插入到訊息佇列中。另外有乙個非同步任務輪詢佇列內容進行處理。
begin;
insert into transaction values(xid, $seller_id, $buyer_id, $amount);
put_to_queue 「update user(「seller」, $seller_id, amount);
put_to_queue 「update user(「buyer」, $buyer_id, amount);
commit;
for each message in queue
begin;
dequeue message;
if message.type = 「seller」 then
update user set amt_sold = amt_sold + message.amount where id = message.user_id;
else
update user set amt_bought = amt_bought + message.amount where id = message.user_id;
endcommit;
end上述解決方案看似完美,實際上還沒有解決分布式問題。為了使第乙個事務不涉及分布式操作,訊息佇列必須與transaction表使用同一套儲存資源,但為了使第二個事務是本地的,訊息佇列儲存又必須與user表在一起。這兩者是不可能同時滿足的。
如果訊息具有操作冪等性,也就是乙個訊息被應用多次與應用一次產生的效果是一樣的話,上述問題是很好解決的,只要將訊息佇列放到transaction表一起,然後在第二個事務中,先應用訊息,再從訊息佇列中刪除。由於訊息佇列儲存與user表不在一起,應用訊息後,可能還沒來得及將應用過的訊息從佇列中刪除時系統就出故障了。這時系統恢復後會重新應用一次這一訊息,由於冪等性,應用多次也能產生正確的結果。
雖然由於沒有分布式事務的強一致性保證,使用上述方案在系統發生故障時,系統將短時間內處於不一致狀態。但基於訊息佇列和訊息應用狀態表,最終可以將系統恢復到一致。使用訊息佇列方案,解除了兩個資料庫例項之間的緊密耦合,其效能和可伸縮性是分布式事務不可比擬的。
當然,使用分布式事務有助於簡化應用開發,使用訊息佇列明顯需要更多的工作量,兩者各有優缺點。個人觀點是,對於時間緊迫或者對效能要求不高的系統,應採用分布式事務加快開發效率,對於時間需求不是很緊,對效能要求很高的系統,應考慮使用訊息佇列方案。對於原使用分布式事務,且系統已趨於穩定,效能要求高的系統,則可以使用訊息佇列方案進行重構來優化效能。
事務 分布式事務解決方案
事務acid特性 事務隔離級別 指的是讀和寫同時出現時出現的資料不一致問題。事務的一致性問題 存在問題問題描述 髒讀 dirty read 針對的是單條資料。即乙個更新操作a修改了某一條資料,但尚未提交該事務,此時另乙個讀操作b來查詢該條資料,讀到的是修改後的但尚未提交的資料。不可重複讀 unrep...
分布式事務解決方案
一 結合mq訊息中介軟體實現的可靠訊息最終一致性 二 tcc補償性事務解決 三 最大努力通知型方案 第一種方案 可靠訊息最終一致性,需要業務系統結合mq訊息中介軟體實現,在實現過程中需要保證訊息的成功傳送及成功消費。即需要通過業務系統控制mq的訊息狀態 第二種方案 tcc補償性,分為三個階段tryi...
分布式事務解決方案
當資料庫單錶一年產生的資料超過1000w,那麼就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以後有空詳細說,簡單的說就是原來的乙個資料庫變成了多個資料庫。這時候,如果乙個操作既訪問01庫,又訪問02庫,而且要保證資料的一致性,那麼就要用到分布式事務。所謂的soa化,就是業務的服務化。比如原來單機...