網際網路下的分布式事務

2021-07-24 22:07:56 字數 3724 閱讀 7589

由於資料量的巨大,大部分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表不在一起,應用訊息後,可能還沒來得及將應用過的訊息從佇列中刪除時系統就出故障了。這時系統恢復後會重新應用一次這一訊息,由於冪等性,應用多次也能產生正確的結果。

但實際情況下,訊息很難具有冪等性,比如上述的update操作,執行一次和執行多次的結束顯然是不一樣的。解決這一問題的方法是使用另乙個表記錄已經被成功應用的訊息,並且這個表使用與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;

if cnt = 0 then 

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;

endend

commit;

if 上述事務成功

dequeue message

endend

我們來仔細分析一下:

1、訊息佇列與transaction使用同一例項,因此第乙個事務不涉及分布式操作;

雖然由於沒有分布式事務的強一致性保證,使用上述方案在系統發生故障時,系統將短時間內處於不一致狀態。但基於訊息佇列和訊息應用狀態表,最終可以將系統恢復到一致。使用訊息佇列方案,解除了兩個資料庫例項之間的緊密耦合,其效能和可伸縮性是分布式事務不可比擬的。

當然,使用分布式事務有助於簡化應用開發,使用訊息佇列明顯需要更多的工作量,兩者各有優缺點。個人觀點是,

對於時間緊迫或者對效能要求不高的系統,應採用分布式事務加快開發效率,對於時間需求不是很緊,對效能要求很高的系統,應考慮使用訊息佇列方案。對於原使用分布式事務,且系統已趨於穩定,效能要求高的系統,則可以使用訊息佇列方案進行重構來優化效能。

網際網路分布式事務解決方案

由於網際網路行業對資料的絕對一致性 二階段提交等方式 要求並不是那麼高,而是對快速響應要求較高,基於cap理論所以一般採用柔性事務進行解決。一 最終一致性解決方案 ebay模式 一般實現方式是訊息中介軟體非同步通知 可靠訊息等一般由訊息中介軟體進行保證,至少重複訊息傳送的情況可以有訊息集群進行保證,...

分布式大型網際網路企業架構

分布式 分布式服務 dubbo zookeeper proxy restful 分布式訊息中介軟體 kafka flume zookeeper 分布式快取 redis 分布式檔案 fastdfs 負載均衡 keepalived nginx proxy 三重負載 雲服務子系統 後台管理系統 restf...

網際網路之父Vint Cerf 網際網路的下乙個十年

據國外 報道,谷歌在自己的官方部落格上發表了對網際網路未來發展的觀點。在採訪了10位頂級電腦專家後,google首席網際網路顧問 有網際網路之父美譽的 vint cerf發表文章認為,10年來網際網路的發展對人們的生活產生了巨大影響,改變了政治 娛樂 文化 商業 醫療 環境和所有能想到的東西。未來網...