事務是恢復和併發控制的基本單位,保證 ACID:原子性、一致性、隔離性、持久性。git
對於全是異步的 Nodejs 而言, 並不適合作事務操做:github
代碼書寫上:數據庫
try ... catch ...
是寫給人看的,可是屬於同步方法,侷限性很大。併發
callback
簡直是噩夢。異步
Promise.then(...).catch(...)
相對而言好一點。async
ES7 的 async ... await ...
比較清爽,使用 Babel 編譯,這是筆者目前找到的最人類的方式,可是和原來的 Promise
混合使用的時候有時候會出問題,由於編譯以後的代碼無法看,最後還得重構回 Promise
。分佈式
異步:性能
異步致使有可能有意料以外的 uncaughtExceptionError
。spa
對於 JAVA/C++ 這樣語言,出錯能直接轉到 catch
中,可是 Node 不是,uncaughtExceptionError
將直接致使處理鏈斷掉,你只能經過其餘方式保證數據一致性。設計
雖然 Node 作事務至關非人類,可是考慮開發效率 / 成本,使用 Node 進行開發並不比換語言開差,畢竟事務只有核心業務須要用到。
單機的事務至關容易保證,特別在依賴 MySQL 或者其餘關係數據庫時。
Nodejs 有 ORM (如 Sequelize ) 支持事務,也能夠直接使用 PROCEDURE/FUNCTION
。
二者各有優點:
ORM 適合複雜邏輯的事務;
存儲過程能夠有效減小 IO 次數,防止使用 ORM 時回滾失敗。
實際開發過程當中能夠將二者結合起來一塊兒使用,使用 ORM 完成邏輯,使用存儲過程減小 IO 次數。
對於分佈式系統,相信不少人都知道 CAP 理論,即任何一個分佈式系統沒法同時知足:
Consistency (一致性)
Availability (可用性)
Partition tolerance (分區容錯性)
可是實際上 Consistency 是任何一個系統都不可能放棄的,分佈式事務亦是爲了保證數據一致性,有時候爲了妥協另外兩個特性,會放棄強一致性,保證最終一致性。
目前業界有不少解決分佈式事務的方案,根據對數據一致性的強弱要求,能夠選擇不一樣的方案,可是解決思路大體以下:
兩階段提交
如 XA 協議(TM(事務管理器)和RM(資源管理器)之間的接口)。
假設有 A、B、C 三個操做,第一階段,等待 A B C 均就緒,第二階段,提交 A B C;若是第一階段 A 失敗了,則第二階段回滾 B C。
本地事務
使用本地消息表,將遠程事務拆分紅一個個本地事務,寫入本地表中,而後 定時 / 使用 MQ 通知事務方。
二者各有利弊,定時掃描可能大部分時候都在作無用功,而只使用 MQ 可能會有失敗 / 屢次消費的問題。
使用回滾接口
如 A B 兩個接口,串行處理,B 失敗了回滾 A ,可是回滾也可能失敗,因此也須要使用本地事務表 / MQ。
使用 Node 開發,1 比較重型,不適合;2 和 3 是比較好的選擇:
選擇一款可靠的 MQ 服務(單次消費 / 失敗重試);
拆分本地事務;
不能拆分的事務,保證回滾。
作一個搶購系統,用戶使用虛擬幣進行搶購,虛擬幣是另一套系統。爲了考慮到公平,每一個用戶還可能要限制購買上限。
這樣用戶一次搶購的完整流程以下:
檢查購買上限
檢查總數
扣除虛擬幣
寫入數據庫
須要事務保證的地方就是 3 和 4,3 是遠程事務,4 是本地事務,此栗子中必然是串行操做,3 在前,4 在後。
這個時候流量不多,併發不高,將 3 和 4 做爲一個事務,保證一塊兒成功,而失敗一塊兒回滾。
事務 4 即便使用 ORM 完成,也能完成功能,這個時候系統能很好的工做。
流量上升中,搶購的商品變多,併發也變大,這個時候,考慮使用 Redis 來提升性能了(犧牲強一致性):
將 購買上限 與 總數 寫入 Redis,在壓力轉嫁到數據庫以前就擋掉,因爲 Redis 的強大性能,能夠假設 Redis 等同於內存操做,作好回滾就能夠了。
同時能夠將事務 4 重構成 PROCEDURE
防止 ORM 可能回滾失敗。
流量大到數據庫扛不住了,加入 MQ 服務:
使用 Redis 抗住流量,使用 MQ 抗住壓力,使用 PROCEDURE
下降 IO 。
0x0011 看起來像一個可靠的系統了,可是還有一個隱患: uncaughtExceptionError
或者 程序宕掉了,這個會影響最終一致性,致使 Redis 數據與 Database 中的數據在搶購臨界結束的時候不一致。
增長最終一致性保證。
搶購的栗子有個很特別的地方,就是 total limit
,達到總數上限以後,就只有 MQ 中的部分須要處理了,所以能夠很巧妙的利用時間差,即考慮在達到上限以後,取一次數據庫快照,延遲一段時間以後,再對比一次數據庫,判斷是數據不一致仍是正常邏輯。
這是一個投機取巧的處理方式,必定程度上能夠保證最終一致性。固然,仍是人最靠譜了,程序搞不定,人工修復嘛,ORZ~。
分佈式事務是一個很大的話題,依據業務量大小能夠給出不少實現。
Nodejs 作分佈式事務勉勉強強,異步裏面的雷不少,不過依賴良好的設計和邏輯同樣能夠實現。