文章目錄
- 一、Seata 框架概述
- 二、核心功能特性
- 三、整體架構與三大角色
- 1. Transaction Coordinator (TC) - 事務協調器(Seata Server)
- 2. Transaction Manager (TM) - 事務管理器(集成在客戶端)
- 3. Resource Manager (RM) - 資源管理器(集成在客戶端)
- 四、工作流程(以最常用的 AT 模式為例)
- 五、一個大比喻:Seata 就像一次「跨銀行轉賬」
- Seata 的三大角色在這場“轉賬”中是誰?
- 六、Seata 的四種模式就是四種不同的轉賬策略
- 1. AT模式(自動擋模式) - 「預授權」轉賬
- 2. TCC模式(手動擋模式) - 「自己打電話確認」轉賬
- 3. Saga模式 - 「先墊付,后報銷」模式
- 4. XA模式 - 「柜臺排隊,同時蓋章」模式
- 總結一下
- 七、如何實現 AT 模式(自動擋模式)
- 實現步驟(基于 Spring Cloud + Spring Boot):
- 八、使用 Seata 實現 TCC 模式(手動擋模式)
- 實現步驟:
- 九、使用 Seata 實現 Saga 和 XA 模式
- 1. Saga 模式(長事務模式)
- 2. XA 模式(傳統強一致模式)
- 總結與選擇
一、Seata 框架概述
Seata(Simple Extensible Autonomous Transaction Architecture)是一款開源的分布式事務解決方案,其愿景是讓分布式事務的使用像本地事務一樣簡單和高效。
它的核心思想是:通過一個全局事務來協調和管理多個分支事務(即本地事務)的提交和回滾,從而保證全局數據的一致性。在微服務架構中,業務邏輯通常需要跨多個服務、多個數據庫,Seata 正是為了解決由此產生的分布式事務問題而生的。
二、核心功能特性
-
多事務模式支持:這是 Seata 最強大的特性,它提供了多種分布式事務解決方案,以適應不同的業務場景。
- AT 模式(默認, Automatic Transaction):
- 無侵入:對業務代碼幾乎無侵入,只需要使用
@GlobalTransactional
注解即可。 - 原理:基于兩階段提交(2PC) 的增強版。它通過代理數據源,在第一階段就提交本地事務,并通過全局鎖來保證隔離性。第二階段由 TC 統一指揮進行提交或回滾(通過 undo_log 日志進行補償回滾)。
- 優點:使用簡單,性能較好。
- 缺點:對數據庫支持有要求(需要支持本地事務和行鎖),全局鎖的存在會對性能有輕微影響。
- 無侵入:對業務代碼幾乎無侵入,只需要使用
- TCC 模式(Try-Confirm-Cancel):
- 侵入性:需要業務代碼實現 Try、Confirm、Cancel 三個接口。
- 原理:也是兩階段提交,但需要開發者自己實現資源檢查和預留(Try)、確認提交(Confirm)、補償回滾(Cancel)的邏輯。
- 優點:性能非常高,不存在全局鎖,能獲得更好的并發性。適用于對性能要求高、有更高隔離性要求的場景(如金融、電商等)。
- 缺點:代碼侵入性強,業務改造量大,設計模式需要謹慎。
- Saga 模式:
- 侵入性:基于狀態機或注解+服務的形式,需要定義每個服務的補償服務。
- 原理:一種長事務解決方案。每個參與者都提交本地事務,如果有一個參與者失敗,則通過之前成功的參與者提供的補償操作來逆向回滾整個事務。
- 優點:適用于業務流程長、需要調用大量服務的場景(如機票預訂、旅游套餐),參與者可以異步執行,吞吐量高。
- 缺點:不保證隔離性,可能出現“臟寫”,需要業務邏輯能夠處理這種中間狀態(例如通過預留、校驗等機制)。
- XA 模式:
- 標準:基于 X/Open 組織定義的 XA 協議的實現。
- 原理:也是兩階段提交。TM 和 RM 之間通過 XA 接口進行通信。在第一階段,所有分支事務執行但不提交(
XA prepare
);第二階段,TC 通知所有分支提交或回滾。 - 優點:強一致性,事務隔離性好。得到了多數主流數據庫的支持。
- 缺點:資源鎖定時間長,在第二階段完成前,所有資源都被鎖定,性能較差。
- AT 模式(默認, Automatic Transaction):
-
高可用(High Availability):TC(事務協調器)支持基于注冊中心(如 Nacos、Eureka)和配置中心(如 Apollo、Nacos)的集群部署,避免單點故障。
-
開箱即用:提供了豐富的配置選項,與主流的微服務框架(Spring Cloud、Dubbo)、注冊中心、配置中心都有很好的集成。
-
豐富的生態:與多家云廠商和開源項目做了適配和集成。
三、整體架構與三大角色
Seata 的架構中包含了三個核心角色,其交互關系如下圖所示:
1. Transaction Coordinator (TC) - 事務協調器(Seata Server)
- 職責:事務的大腦和指揮官。它是獨立的中間件服務器,需要單獨部署。
- 功能:
- 維護全局事務和分支事務的狀態。
- 驅動全局事務的提交或回滾。
- 管理全局鎖(特別是在 AT 模式下)。
- 部署:通常以集群模式部署,通過注冊中心提供服務發現。
2. Transaction Manager ? - 事務管理器(集成在客戶端)
- 職責:全局事務的發起者。它定義了全局事務的邊界。
- 功能:
- 向 TC 發起指令,開啟一個全新的全局事務。
- 向 TC 發起指令,提交或回滾一個全局事務。
- 位置:通常位于發起全局事務的微服務應用程序中。例如,在訂單服務中,一個創建訂單的方法可能會被
@GlobalTransactional
注解標記,該方法中的代碼就扮演了 TM 的角色。
3. Resource Manager (RM) - 資源管理器(集成在客戶端)
- 職責:分支事務的執行者。負責管理本地資源(通常是數據庫)。
- 功能:
- 向 TC 注冊分支事務,并報告分支事務的狀態。
- 接收來自 TC 的指令,驅動分支事務的本地提交或回滾。
- 在 AT 模式下,RM 會代理數據源,自動生成和執行 SQL 的逆向回滾日志(
undo_log
)。
- 位置:每一個參與分布式事務的微服務應用程序中都有一個 RM。它負責操作自己管轄的數據庫。
四、工作流程(以最常用的 AT 模式為例)
假設一個下單流程:訂單服務
-> 庫存服務
-> 賬戶服務
。
- TM 開啟全局事務:
- 用戶調用訂單服務的“創建訂單”接口。
- 訂單服務中的 TM(被
@GlobalTransactional
注解的方法)向 TC 申請開啟一個全局事務(XID),TC 會生成一個唯一的 XID 并返回。
- RM 注冊分支事務:
- 訂單服務執行本地事務,在向訂單表插入數據前,RM 會先攔截 SQL,生成查詢快照和回滾日志(
undo_log
),并寫入數據庫。 - 訂單服務的 RM 向 TC 注冊一個分支事務,并將這個分支事務與之前的 XID 關聯。
- 訂單服務提交本地事務。
- 訂單服務執行本地事務,在向訂單表插入數據前,RM 會先攔截 SQL,生成查詢快照和回滾日志(
- 調用鏈傳播 XID:
- 訂單服務通過 Feign/Ribbon 調用庫存服務。Seata 會自動將 XID 通過請求頭(例如
tx-seata-xid
)傳遞到下游服務。
- 訂單服務通過 Feign/Ribbon 調用庫存服務。Seata 會自動將 XID 通過請求頭(例如
- 下游服務執行:
- 庫存服務收到請求,其 RM 也感知到了這個 XID。
- 庫存服務執行扣減庫存的本地事務,同樣生成
undo_log
并向 TC 注冊屬于自己的分支事務,然后提交本地事務。 - 賬戶服務同理。
- 全局提交或回滾:
- 成功情況:所有分支事務都成功執行并注冊。TM(訂單服務)會向 TC 發起全局提交的指令。TC 會異步地通知所有 RM 刪除各自的
undo_log
日志。整個過程非常快,因為一階段已經提交了。 - 失敗情況:如果賬戶服務扣款失敗,它會拋出異常。這個異常會沿著調用鏈向上傳播,最終被訂單服務中的 TM 捕獲。
- TM 會向 TC 發起全局回滾的指令。
- TC 根據 XID 找到所有相關分支事務,并向它們的 RM 下達回滾命令。
- 各個 RM 收到回滾命令后,根據本地數據庫中的
undo_log
日志,生成反向的 SQL 語句(例如 INSERT 變成 DELETE,UPDATE 變回舊值)并執行,完成數據回滾,最后刪除undo_log
。
- 成功情況:所有分支事務都成功執行并注冊。TM(訂單服務)會向 TC 發起全局提交的指令。TC 會異步地通知所有 RM 刪除各自的
總結
特性/模式 | AT 模式 | TCC 模式 | Saga 模式 | XA 模式 |
---|---|---|---|---|
侵入性 | 低(無侵入) | 高(需實現接口) | 中(需定義補償) | 低(無侵入) |
性能 | 較好 | 非常好 | 非常好(可異步) | 差(資源鎖定) |
隔離性 | 讀未提交(靠全局鎖) | 依賴業務實現 | 無隔離 | 強隔離 |
適用場景 | 大部分場景,希望簡單高效 | 高性能要求,金融支付 | 長事務,業務流程多的系統 | 傳統銀行、強一致性需求 |
Seata 通過其清晰的角色劃分和對多種模式的支持,為復雜的分布式系統環境提供了靈活而強大的一致性保證,是構建現代微服務架構不可或缺的重要組件。
五、一個大比喻:Seata 就像一次「跨銀行轉賬」
想象一下,你要從【建設銀行】的賬戶轉 100 塊錢到你的朋友在【工商銀行】的賬戶。這個操作其實就是一個典型的分布式事務,它涉及了兩個不同的銀行系統(兩個獨立的數據庫)。
Seata 的三大角色在這場“轉賬”中是誰?
-
TC (事務協調器) - 總行清算中心
- 它是誰:一個獨立的核心機構,不屬于任何一家地方銀行。它知道全國所有跨行交易的進展。
- 干什么活:
- 記錄每一筆跨行轉賬的全局狀態(開始了?成功了?失敗了?)。
- 負責最終下達命令:“所有銀行,請最終確認提交!” 或者 “所有銀行,立刻撤銷剛才的操作!”
- 特點:必須非常可靠,所以通常是集群部署(好幾個備份清算中心),防止一個掛了導致全國轉賬癱瘓。
-
TM (事務管理器) - 建設銀行的那個柜員/ATM機
- 它是誰:發起整個事務的人。就是你面對的那個幫你操作轉賬的柜員,或者那臺ATM機。
- 干什么活:
- 他/它先向“總行清算中心(TC)”匯報:“您好,我要開始一筆從建行轉工行的轉賬了,編號是XXX”。
- 如果整個流程順利,他/它就向TC發起“最終提交”的指令。
- 如果中途出錯(比如工行賬戶不存在),他/它就立刻向TC發起“回滾”的指令。
-
RM (資源管理器) - 建設銀行和工商銀行自己的賬本系統
- 它是誰:每個參與事務的資源個體。在這里就是【建設銀行】的數據庫和【工商銀行】的數據庫。
- 干什么活:
- 建行RM:負責從你的賬戶上臨時扣下100塊(但先不真正給別人)。
- 工行RM:負責在你朋友的賬戶上臨時加上100塊(但先不讓他用)。
- 它們都要向“總行清算中心(TC)”匯報:“我這邊臨時操作完了,狀態OK,隨時等待您的最終命令”。
- 最后聽從TC的命令,要么把“臨時”變成“正式”,要么撤銷剛才的臨時操作。
六、Seata 的四種模式就是四種不同的轉賬策略
現在,總行清算中心(TC)提供了幾種不同的轉賬流程供你選擇:
1. AT模式(自動擋模式) - 「預授權」轉賬
這是最常用、最省心的模式,就像你用信用卡的預授權。
-
流程:
- 第一階段:
- 建行RM:先從你賬戶凍結100塊(就像酒店凍結你信用卡一定額度,錢還沒劃走,但你也不能用了)。
- 工行RM:在你朋友賬戶里先標記有100塊即將到賬(但還不能用)。
- 兩邊都向TC報告:“預授權成功”。
- 第二階段:
- 如果成功:TC說“確認”,兩家銀行就把預授權變成實際扣款和實際入賬。
- 如果失敗:TC說“撤銷”,建行就解凍你的100塊,工行就取消那100塊的標記。
- 第一階段:
-
優點:你(程序員)幾乎無感,Seata幫你自動搞定了一切,就像開車用自動擋。
-
缺點:用了“全局鎖”(即凍結資金),性能有一點點損失。
2. TCC模式(手動擋模式) - 「自己打電話確認」轉賬
這個模式要求高,需要你(業務開發者)自己介入更多,就像大額轉賬需要你親自打電話一步步確認。
-
流程:
- Try (嘗試)階段:你自己打電話。
- 打給建行:“麻煩你幫我試試看能不能扣100塊?”(資源檢查和預留)。
- 打給工行:“麻煩你試試看能不能收100塊?”。
- Confirm (確認)階段:如果兩邊都回電話說“沒問題”,你就再打一次電話:“好,請正式操作!”
- Cancel (取消)階段:如果工行說“賬戶不對,收不了”,你就得再打給建行:“剛才的嘗試作廢,解封吧。”
- Try (嘗試)階段:你自己打電話。
-
優點:性能最好,控制力最強,沒有全局鎖。
-
缺點:太麻煩!你需要寫(打)很多代碼(電話)來實現Try、Confirm、Cancel三個操作。
3. Saga模式 - 「先墊付,后報銷」模式
適用于一個非常長的鏈條,比如公司組織團建,你先自己墊錢,最后統一報銷。
-
流程:
- 你先自己掏錢買了機票(提交本地事務)。
- 你又自己掏錢訂了酒店(提交本地事務)。
- 最后你申請公司報銷。
- 如果報銷成功:完美!
- 如果報銷失敗:公司不認這筆錢。那么你就得反向操作:把機票退掉、把酒店退掉(補償操作)。
-
優點:吞吐量高,適合長流程業務。
-
缺點:可能你退機票的時候發現已經不能退了(缺乏隔離性),就得自己承擔損失。
4. XA模式 - 「柜臺排隊,同時蓋章」模式
傳統銀行的老做法,非常嚴格但效率低。
-
流程:
- 建行和工行的柜員都拿到了你的轉賬單。
- 他們互相打電話確認:“我準備好了,你呢?”“我也準備好了。”
- 等到兩邊都確認準備好后,在同一時刻一起蓋章生效。
-
優點:強一致性,最安全。
-
缺點:在準備和確認的過程中,你賬戶的錢和對方賬戶的錢都被鎖住,不能做任何其他操作,效率最低。
總結一下
- 想省心,就用 AT模式(默認),像開自動擋汽車。
- 追求極致性能和高控制,不怕麻煩就用 TCC模式,像開手動擋賽車。
- 業務流程特別長,就用 Saga模式,像先墊付后報銷。
- 傳統、強一致的系統(如銀行核心系統),會用 XA模式,像老式的柜臺同步操作。
所以,Seata 本質上就是一個分布式事務的調度中心,它提供了好幾種“工作流程”來保證:要么大家都成功,要么都失敗,絕不會出現“我的錢扣了,對方卻沒收到”的尷尬局面。
好的,我們接著用比喻和實例的方式,來深入講解如何實現 Seata 的這幾種模式。
七、如何實現 AT 模式(自動擋模式)
AT 模式是 Seata 的默認模式,對代碼侵入性極低,就像開自動擋汽車。
實現步驟(基于 Spring Cloud + Spring Boot):
-
“考駕照” - 引入依賴與配置
首先,你需要在你的微服務項目中“考取”操作 Seata 的資格。<!-- 在您的 order-service, storage-service, account-service 的 pom.xml 中 --> <dependency><groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
-
“給車加油和設置導航” - 配置 Seata
在每個微服務的application.yml
中,告訴它們 TC(事務協調器,即 Seata-Server)在哪里。seata:application-id: order-service # 當前服務名tx-service-group: my_tx_group # 事務組名,與seata-server配置對應registry:type: nacos # 注冊中心類型(假設你用nacos)nacos:server-addr: localhost:8848namespace: publicconfig:type: nacos # 配置中心類型nacos:server-addr: localhost:8848namespace: public
-
“創建交通規則” - 建立 undo_log 表
AT 模式的核心是回滾日志。你需要在每一個業務涉及的數據庫中,都創建一張undo_log
表。這張表就像是汽車的“黑匣子”,記錄了數據修改前的樣子,以便出問題時能還原。
(建表 SQL 在 Seata 官方文檔的腳本里可以找到) -
“握緊方向盤,踩下油門” - 使用全局事務注解
現在,你只需要在發起全局事務的入口方法上,加一個神奇的注解@GlobalTransactional
。@RestController public class OrderController {@Autowiredprivate OrderService orderService;@PostMapping("/createOrder")@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class) // 就是這個注解!public String createOrder(Order order) {// 1. 扣減庫存(調用庫存服務)storageService.decrease(order.getProductId(), order.getCount());// 2. 扣減余額(調用賬戶服務)accountService.decrease(order.getUserId(), order.getMoney());// 3. 創建訂單(本地事務)orderService.create(order);return "訂單創建成功!";} }
發生了什么?
當程序運行:
- Seata 會自動代理你的數據源。
- 在調用
storageService.decrease
時,Seata 會攔截 SQL,先將庫存修改前的數據作為“回滾日志”存入undo_log
表,然后再執行扣減。 - 扣減成功后,它會向 TC 注冊一個分支事務,報告:“我是庫存分支,我預執行成功了”。
- 賬戶服務和訂單服務執行同樣的操作。
- 如果所有分支都成功,TM(
@GlobalTransactional
注解的方法)會通知 TC,TC 再命令所有 RM 刪除各自的undo_log
日志,事務真正提交。 - 如果中間任何一步失敗(比如余額不足),TM 會通知 TC,TC 命令所有 RM:“請根據你們的
undo_log
黑匣子,把數據恢復原狀!”。
總結:實現 AT 模式 ≈ 引入依賴 + 配置 TC 地址 + 建 undo_log 表 + 加一個注解。
八、使用 Seata 實現 TCC 模式(手動擋模式)
TCC 模式需要你親自編寫業務邏輯,實現“嘗試”、“確認”、“取消”三個操作,就像開手動擋車。
實現步驟:
-
“定義三個踏板” - 編寫 TCC 接口
你需要為你的業務資源定義一個接口,包含try
,confirm
,cancel
三個方法。@LocalTCC public interface AccountTccAction {// try階段:資源檢查和預留// @BusinessActionContextParameter 用于將參數傳遞到confirm/cancel階段@TwoPhaseBusinessAction(name = "accountTccAction", commitMethod = "confirm", rollbackMethod = "cancel")boolean tryDecrease(@BusinessActionContextParameter(paramName = "userId") String userId,@BusinessActionContextParameter(paramName = "money") BigDecimal money);// confirm階段:真正執行boolean confirm(BusinessActionContext context);// cancel階段:補償恢復boolean cancel(BusinessActionContext context); }
-
“制造三個踏板” - 實現 TCC 接口
在實現類中,你手動編寫扣款業務的三個階段邏輯。@Service public class AccountTccActionImpl implements AccountTccAction {@Autowiredprivate AccountMapper accountMapper;@Overridepublic boolean tryDecrease(String userId, BigDecimal money) {// 1. 檢查余額是否足夠Account account = accountMapper.selectById(userId);if (account.getResidue().compareTo(money) < 0) {throw new RuntimeException("賬戶余額不足");}// 2. 嘗試階段:凍結金額(預留資源)accountMapper.updateFrozen(userId, money); // SQL: UPDATE account SET frozen = frozen + #{money} WHERE user_id = #{userId}return true;}@Overridepublic boolean confirm(BusinessActionContext context) {// 獲取try階段的參數String userId = (String) context.getActionContext("userId");BigDecimal money = (BigDecimal) context.getActionContext("money");// 確認階段:扣除凍結的金額accountMapper.updateFrozenToUsed(userId, money); // SQL: UPDATE account SET residue = residue - #{money}, frozen = frozen - #{money} WHERE user_id = #{userId}return true;}@Overridepublic boolean cancel(BusinessActionContext context) {// 獲取try階段的參數String userId = (String) context.getActionContext("userId");BigDecimal money = (BigDecimal) context.getActionContext("money");// 補償階段:釋放凍結的金額accountMapper.updateFrozenToResidue(userId, money); // SQL: UPDATE account SET frozen = frozen - #{money} WHERE user_id = #{userId}return true;} }
-
“掛擋給油” - 在業務中調用 TCC 服務
在你的業務代碼中,不再直接調用普通的賬戶服務,而是調用這個 TCC 服務。@RestController public class OrderController {// 注入TCC服務,而不是普通的AccountService@Autowiredprivate AccountTccAction accountTccAction;@PostMapping("/createOrder")@GlobalTransactional(name = "createOrderTcc", rollbackFor = Exception.class)public String createOrder(Order order) {// ...// 調用TCC服務的try階段accountTccAction.tryDecrease(order.getUserId(), order.getMoney());// ...} }
核心思想:TCC 模式就是把一個大事務拆成兩個階段。
- 一階段 (Try):不做最終操作,只做準備工作(檢查、預留資源、凍結資金)。
- 二階段:
- 如果所有 Try 都成功,就執行 Confirm(確認,使用預留的資源)。
- 如果有一個 Try 失敗,就執行 Cancel(取消,釋放預留的資源)。
注意事項:
- 空回滾:Try 階段可能因為網絡問題沒執行,但 Cancel 被執行了。你的 Cancel 方法需要能處理這種情況(查一下有沒有凍結記錄,沒有就不操作)。
- 冪等性:因為網絡問題,Confirm 或 Cancel 可能會被重復調用,你的方法需要保證多次調用結果一樣(比如用事務狀態表來判斷)。
九、使用 Seata 實現 Saga 和 XA 模式
1. Saga 模式(長事務模式)
Saga 模式通常用于業務流程非常長的場景,比如一個旅游預訂流程,包含訂機票、訂酒店、辦簽證等多個步驟,每個步驟都可能耗時很久。
實現方式(狀態機模式):
- 定義狀態機JSON:你需要用一個 JSON 文件來詳細定義整個業務流程的每一個步驟(
State
),以及每個步驟失敗后對應的補償操作(Compensation
)。 - 部署狀態機:將這個 JSON 文件部署到 Seata 的 Saga 狀態機引擎中。
- 觸發執行:你的程序只需要發起一個請求,狀態機引擎就會自動按照你定義的流程一步步執行,并在任何一步失敗時,自動反向執行之前所有成功步驟的補償操作。
特點:
- 一階段就提交:每個服務都直接提交本地事務,所以沒有鎖,性能高。
- 全靠補償:一致性靠的是補償操作,如果補償操作也失敗了,就需要人工介入處理。不保證隔離性。
適用場景:電商訂單履約、物流系統、工作流審批等長時間運行的業務流程。
2. XA 模式(傳統強一致模式)
XA 模式是數據庫層面提供的標準協議,Seata 也提供了支持。
實現步驟:
-
切換模式:在你的
application.yml
中,將數據源代理模式改為 XA。seata:data-source-proxy-mode: XA # 默認是 AT
-
使用注解:和 AT 模式一樣,在事務入口使用
@GlobalTransactional
注解。
背后原理:
- 一階段:Seata 會調用
XA Start
、XA End
、XA Prepare
。此時,數據庫會執行 SQL,但并不提交,而是將事務置于“準備就緒”狀態,數據對其他事務不可見。 - 二階段:
- 提交:TC 通知所有 RM
XA Commit
,所有數據庫同時提交。 - 回滾:TC 通知所有 RM
XA Rollback
,所有數據庫同時回滾。
- 提交:TC 通知所有 RM
特點:
- 強一致性:在整個事務結束前,數據都被鎖住,完美保證隔離性。
- 性能差:因為資源鎖定時間長,并發性能是幾種模式中最差的。
適用場景:傳統金融、銀行等對強一致性要求極高,且并發量不是天文數字的內部系統。
總結與選擇
模式 | 實現關鍵 | 比喻 | 適用場景 |
---|---|---|---|
AT | 加注解 @GlobalTransactional | 自動擋 | 絕大部分場景,追求簡單和效率 |
TCC | 自己寫 try 、confirm 、cancel 三個方法 | 手動擋 | 高性能、高并發場景,如資金交易 |
Saga | 用狀態機JSON定義流程和補償 | 先做后報 | 長流程業務,如訂單履約、工作流 |
XA | 配置 data-source-proxy-mode: XA + 加注解 @GlobalTransactional | 同步蓋章 | 傳統行業,強一致性要求極高,并發量不大的系統 |