Nodejs 分佈式事務

事務是恢復和併發控制的基本單位,保證 ACID:原子性、一致性、隔離性、持久性。git

對於全是異步的 Nodejs 而言, 並不適合作事務操做:github

  1. 代碼書寫上:數據庫

    • try ... catch ... 是寫給人看的,可是屬於同步方法,侷限性很大。併發

    • callback 簡直是噩夢。異步

    • Promise.then(...).catch(...) 相對而言好一點。async

    • ES7 的 async ... await ... 比較清爽,使用 Babel 編譯,這是筆者目前找到的最人類的方式,可是和原來的 Promise 混合使用的時候有時候會出問題,由於編譯以後的代碼無法看,最後還得重構回 Promise分佈式

  2. 異步:性能

    • 異步致使有可能有意料以外的 uncaughtExceptionErrorspa

    • 對於 JAVA/C++ 這樣語言,出錯能直接轉到 catch 中,可是 Node 不是,uncaughtExceptionError 將直接致使處理鏈斷掉,你只能經過其餘方式保證數據一致性。設計

雖然 Node 作事務至關非人類,可是考慮開發效率 / 成本,使用 Node 進行開發並不比換語言開差,畢竟事務只有核心業務須要用到。

單點

單機的事務至關容易保證,特別在依賴 MySQL 或者其餘關係數據庫時。

Nodejs 有 ORM (如 Sequelize ) 支持事務,也能夠直接使用 PROCEDURE/FUNCTION

二者各有優點:

  • ORM 適合複雜邏輯的事務;

  • 存儲過程能夠有效減小 IO 次數,防止使用 ORM 時回滾失敗。

實際開發過程當中能夠將二者結合起來一塊兒使用,使用 ORM 完成邏輯,使用存儲過程減小 IO 次數。

單機事務

分佈式

對於分佈式系統,相信不少人都知道 CAP 理論,即任何一個分佈式系統沒法同時知足:

  • Consistency (一致性)

  • Availability (可用性)

  • Partition tolerance (分區容錯性)

可是實際上 Consistency 是任何一個系統都不可能放棄的,分佈式事務亦是爲了保證數據一致性,有時候爲了妥協另外兩個特性,會放棄強一致性,保證最終一致性

解決方式

目前業界有不少解決分佈式事務的方案,根據對數據一致性的強弱要求,能夠選擇不一樣的方案,可是解決思路大體以下:

  1. 兩階段提交

如 XA 協議(TM(事務管理器)和RM(資源管理器)之間的接口)。

假設有 A、B、C 三個操做,第一階段,等待 A B C 均就緒,第二階段,提交 A B C;若是第一階段 A 失敗了,則第二階段回滾 B C。

兩階段提交

  1. 本地事務

使用本地消息表,將遠程事務拆分紅一個個本地事務,寫入本地表中,而後 定時 / 使用 MQ 通知事務方。

二者各有利弊,定時掃描可能大部分時候都在作無用功,而只使用 MQ 可能會有失敗 / 屢次消費的問題。

  1. 使用回滾接口

如 A B 兩個接口,串行處理,B 失敗了回滾 A ,可是回滾也可能失敗,因此也須要使用本地事務表 / MQ。

回滾接口

使用 Node 開發,1 比較重型,不適合;2 和 3 是比較好的選擇:

  1. 選擇一款可靠的 MQ 服務(單次消費 / 失敗重試);

  2. 拆分本地事務;

  3. 不能拆分的事務,保證回滾。

舉個栗子

作一個搶購系統,用戶使用虛擬幣進行搶購,虛擬幣是另一套系統。爲了考慮到公平,每一個用戶還可能要限制購買上限。

這樣用戶一次搶購的完整流程以下:

  1. 檢查購買上限

  2. 檢查總數

  3. 扣除虛擬幣

  4. 寫入數據庫

須要事務保證的地方就是 3 和 4,3 是遠程事務,4 是本地事務,此栗子中必然是串行操做,3 在前,4 在後。

0x0001

這個時候流量不多,併發不高,將 3 和 4 做爲一個事務,保證一塊兒成功,而失敗一塊兒回滾。

事務 4 即便使用 ORM 完成,也能完成功能,這個時候系統能很好的工做。

0x0010

流量上升中,搶購的商品變多,併發也變大,這個時候,考慮使用 Redis 來提升性能了(犧牲強一致性):

將 購買上限 與 總數 寫入 Redis,在壓力轉嫁到數據庫以前就擋掉,因爲 Redis 的強大性能,能夠假設 Redis 等同於內存操做,作好回滾就能夠了。

同時能夠將事務 4 重構成 PROCEDURE 防止 ORM 可能回滾失敗。

0x0011

流量大到數據庫扛不住了,加入 MQ 服務:

version 3

使用 Redis 抗住流量,使用 MQ 抗住壓力,使用 PROCEDURE 下降 IO 。

0x0011 看起來像一個可靠的系統了,可是還有一個隱患: uncaughtExceptionError 或者 程序宕掉了,這個會影響最終一致性,致使 Redis 數據與 Database 中的數據在搶購臨界結束的時候不一致。

0x0100

增長最終一致性保證。

搶購的栗子有個很特別的地方,就是 total limit ,達到總數上限以後,就只有 MQ 中的部分須要處理了,所以能夠很巧妙的利用時間差,即考慮在達到上限以後,取一次數據庫快照,延遲一段時間以後,再對比一次數據庫,判斷是數據不一致仍是正常邏輯。

一致性修復

這是一個投機取巧的處理方式,必定程度上能夠保證最終一致性。固然,仍是人最靠譜了,程序搞不定,人工修復嘛,ORZ~。

終語

分佈式事務是一個很大的話題,依據業務量大小能夠給出不少實現。

Nodejs 作分佈式事務勉勉強強,異步裏面的雷不少,不過依賴良好的設計和邏輯同樣能夠實現。

相關文章
相關標籤/搜索