SEATA AT模式:低侵入分布式事務(wù)解決方案的原理與實(shí)踐
1. 項(xiàng)目概述為什么我們需要SEATA的AT模式在微服務(wù)架構(gòu)里一個(gè)業(yè)務(wù)操作經(jīng)常需要跨多個(gè)服務(wù)、多個(gè)數(shù)據(jù)庫(kù)來完成。比如一個(gè)電商下單流程你可能需要調(diào)用訂單服務(wù)創(chuàng)建訂單調(diào)用庫(kù)存服務(wù)扣減庫(kù)存再調(diào)用賬戶服務(wù)扣減余額。如果一切順利那自然皆大歡喜。但現(xiàn)實(shí)是任何一個(gè)環(huán)節(jié)都可能出錯(cuò)庫(kù)存不足、賬戶余額不夠、網(wǎng)絡(luò)抖動(dòng)、服務(wù)宕機(jī)……這時(shí)候問題就來了訂單創(chuàng)建成功了但庫(kù)存沒扣減或者庫(kù)存扣了但賬戶余額沒動(dòng)數(shù)據(jù)就“打架”了業(yè)務(wù)一致性被破壞。這就是經(jīng)典的分布式事務(wù)問題。傳統(tǒng)的單機(jī)數(shù)據(jù)庫(kù)事務(wù)ACID在這里鞭長(zhǎng)莫及因?yàn)樗懿涣丝缇W(wǎng)絡(luò)、跨數(shù)據(jù)庫(kù)的操作。于是業(yè)界涌現(xiàn)了各種解決方案比如兩階段提交2PC、TCC、Saga以及我們今天要聊的主角——SEATA的AT模式。SEATASimple Extensible Autonomous Transaction Architecture是一款開源的分布式事務(wù)解決方案。它的ATAuto Transaction模式可以理解為對(duì)業(yè)務(wù)代碼“入侵”極低的一種兩階段提交實(shí)現(xiàn)。它最大的魅力在于你幾乎不用改業(yè)務(wù)邏輯只需要加個(gè)注解就能讓原本獨(dú)立的本地事務(wù)自動(dòng)協(xié)調(diào)成一個(gè)全局的分布式事務(wù)。對(duì)于很多從單體應(yīng)用拆分出來的團(tuán)隊(duì)或者希望快速引入分布式事務(wù)能力又不想大動(dòng)干戈的項(xiàng)目來說AT模式是一個(gè)非常平滑的切入點(diǎn)。2. SEATA AT模式的核心原理與設(shè)計(jì)思路拆解在深入使用之前我們必須先搞清楚AT模式是怎么工作的。知其然更要知其所以然這樣在出問題時(shí)你才知道該往哪里看。2.1 兩階段提交的“自動(dòng)化”演繹AT模式本質(zhì)上是對(duì)傳統(tǒng)兩階段提交2PC的一種優(yōu)化和封裝。傳統(tǒng)的2PC需要一個(gè)“協(xié)調(diào)者”Coordinator來指揮多個(gè)“參與者”Participant分為投票Prepare和提交Commit兩個(gè)階段。這個(gè)過程需要參與者實(shí)現(xiàn)復(fù)雜的接口對(duì)業(yè)務(wù)侵入大。SEATA的AT模式巧妙之處在于它把“協(xié)調(diào)者”的工作交給了SEATA ServerTCTransaction Coordinator而把“參與者”的準(zhǔn)備工作通過一個(gè)“數(shù)據(jù)代理層”自動(dòng)化了。這個(gè)代理層就是我們?cè)趹?yīng)用中引入的SEATA ClientRMResource Manager和對(duì)應(yīng)的數(shù)據(jù)源代理。它的工作流程可以拆解為以下幾個(gè)核心步驟第一階段業(yè)務(wù)執(zhí)行與本地提交當(dāng)一個(gè)被GlobalTransactional注解標(biāo)記的方法開始執(zhí)行時(shí)SEATA會(huì)向TC服務(wù)端發(fā)起請(qǐng)求開啟一個(gè)全局事務(wù)XID這個(gè)XID會(huì)在整個(gè)調(diào)用鏈中傳遞。業(yè)務(wù)SQL開始執(zhí)行。注意此時(shí)數(shù)據(jù)源代理已經(jīng)介入。在執(zhí)行UPDATE或DELETE語(yǔ)句前代理會(huì)攔截SQL查詢數(shù)據(jù)的前鏡像Before Image也就是修改前的數(shù)據(jù)狀態(tài)并保存下來。執(zhí)行業(yè)務(wù)SQL更新數(shù)據(jù)。執(zhí)行后代理再次查詢數(shù)據(jù)的后鏡像After Image即修改后的數(shù)據(jù)狀態(tài)。將前鏡像、后鏡像以及業(yè)務(wù)SQL本身組成一條回滾日志undo_log插入到業(yè)務(wù)數(shù)據(jù)庫(kù)的undo_log表中。這個(gè)操作和業(yè)務(wù)SQL在同一個(gè)本地事務(wù)中提交。至此第一階段完成。業(yè)務(wù)數(shù)據(jù)已經(jīng)提交對(duì)用戶可見。同時(shí)回滾日志也已持久化。第二階段全局提交或回滾如果所有分支事務(wù)都成功TC會(huì)向所有RM發(fā)送異步的提交指令。RM收到后只需異步、批量地刪除對(duì)應(yīng)的undo_log記錄即可。這個(gè)過程非常快因?yàn)椴恍枰僮鰯?shù)據(jù)操作。如果任何一個(gè)分支事務(wù)失敗TC會(huì)向所有已成功的RM發(fā)送回滾指令。RM收到后會(huì)根據(jù)undo_log表中的前鏡像數(shù)據(jù)生成一條反向的補(bǔ)償SQL比如之前是update set stockstock-1回滾就是update set stockstock1執(zhí)行它來恢復(fù)數(shù)據(jù)然后刪除undo_log記錄。注意這里有一個(gè)關(guān)鍵點(diǎn)也是AT模式被稱為“自動(dòng)補(bǔ)償”的原因。它的回滾不是通過數(shù)據(jù)庫(kù)的ROLLBACK命令而是通過執(zhí)行一條反向的補(bǔ)償SQL。這就要求undo_log記錄必須和業(yè)務(wù)數(shù)據(jù)在同一個(gè)本地事務(wù)中提交保證“只要有業(yè)務(wù)數(shù)據(jù)就一定有對(duì)應(yīng)的回滾日志”。2.2 AT模式的優(yōu)缺點(diǎn)與適用場(chǎng)景理解了原理我們就能更理性地看待它的適用邊界。優(yōu)勢(shì)對(duì)代碼侵入性極低這是最大的優(yōu)點(diǎn)。通常只需要一個(gè)GlobalTransactional注解。開發(fā)效率高開發(fā)者像寫本地事務(wù)一樣編寫業(yè)務(wù)代碼心智負(fù)擔(dān)小。一階段完成即提交業(yè)務(wù)數(shù)據(jù)立即可見減少了資源鎖定的時(shí)間性能相對(duì)較好。局限性與注意事項(xiàng)必須支持SQL解析AT模式依賴于對(duì)SQL的解析來生成回滾日志。這意味著它只適用于支持SQL的關(guān)系型數(shù)據(jù)庫(kù)且某些復(fù)雜的SQL如多表關(guān)聯(lián)更新、子查詢更新可能解析不了或支持不好。全局行鎖在一階段SEATA會(huì)通過SELECT FOR UPDATE在全局事務(wù)范圍內(nèi)鎖定要修改的行以防止其他全局事務(wù)并發(fā)修改。這雖然保證了隔離性但也可能引入性能熱點(diǎn)和死鎖風(fēng)險(xiǎn)。臟寫問題如果存在非SEATA管理的本地事務(wù)比如直接JDBC操作或其它框架同時(shí)修改同一行數(shù)據(jù)可能會(huì)發(fā)生臟寫。AT模式默認(rèn)的隔離級(jí)別是“讀未提交”在高并發(fā)場(chǎng)景下需要額外注意?;貪L日志表需要在每個(gè)業(yè)務(wù)數(shù)據(jù)庫(kù)中創(chuàng)建undo_log表有一定的運(yùn)維成本。適用場(chǎng)景業(yè)務(wù)邏輯以簡(jiǎn)單的CRUD為主SQL模式標(biāo)準(zhǔn)。對(duì)一致性有要求但可以接受“讀未提交”的隔離級(jí)別或可通過其他手段如版本號(hào)解決。希望快速引入分布式事務(wù)且團(tuán)隊(duì)對(duì)TCC、Saga等模式不熟悉。不適合金融級(jí)超高一致性要求的轉(zhuǎn)賬場(chǎng)景這類場(chǎng)景更推薦TCC。3. 環(huán)境搭建與核心配置詳解紙上得來終覺淺絕知此事要躬行。我們從一個(gè)最簡(jiǎn)單的場(chǎng)景開始搭建一個(gè)訂單服務(wù)Order Service和一個(gè)庫(kù)存服務(wù)Stock Service下單時(shí)需要同時(shí)調(diào)用兩者。3.1 SEATA ServerTC的部署TC是事務(wù)協(xié)調(diào)者需要獨(dú)立部署。推薦使用Docker最簡(jiǎn)單快捷。# 拉取SEATA Server鏡像 docker pull seataio/seata-server:latest # 運(yùn)行SEATA Server容器 docker run -d --name seata-server \ -p 8091:8091 \ -p 7091:7091 \ -e SEATA_IP你的服務(wù)器IP \ -e SEATA_PORT8091 \ -v /your_path/seata/config:/seata-server/resources \ seataio/seata-server關(guān)鍵參數(shù)解釋-p 8091:8091TC的服務(wù)端口客戶端RM通過這個(gè)端口與TC通信。-p 7091:7091控制臺(tái)端口可以通過http://你的服務(wù)器IP:7091訪問SEATA控制臺(tái)查看全局事務(wù)狀態(tài)。-e SEATA_IP這個(gè)非常重要必須設(shè)置為客戶端能夠訪問到的服務(wù)器IP地址不能是127.0.0.1或localhost??蛻舳丝窟@個(gè)地址找到TC。-v掛載配置文件目錄。你需要提前在/your_path/seata/config下準(zhǔn)備好registry.conf和file.conf。對(duì)于快速測(cè)試可以使用內(nèi)置的file配置模式。配置文件核心 (registry.conf- 使用file模式簡(jiǎn)化)registry { type file # 注冊(cè)中心類型測(cè)試用file生產(chǎn)環(huán)境建議用nacos, eureka等 } config { type file file { name file.conf } }配置文件核心 (file.conf- 事務(wù)日志存儲(chǔ)這里用file模式)store { mode file # 事務(wù)日志存儲(chǔ)模式可選file, db, redis。生產(chǎn)環(huán)境建議用db。 file { dir sessionStore # 文件存儲(chǔ)路徑 } }啟動(dòng)后訪問控制臺(tái)如果能看到SEATA的Logo說明服務(wù)端啟動(dòng)成功。3.2 客戶端RM的接入與配置我們以Spring Boot項(xiàng)目為例演示訂單服務(wù)的接入。第一步引入依賴dependency groupIdio.seata/groupId artifactIdseata-spring-boot-starter/artifactId version最新版本/version !-- 例如 1.8.0 -- /dependency !-- 還需要數(shù)據(jù)源、mybatis等依賴此處省略 --第二步配置數(shù)據(jù)源代理這是AT模式生效的關(guān)鍵。SEATA需要代理你的數(shù)據(jù)源以攔截SQL。# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/order_db?useUnicodetruecharacterEncodingutf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver seata: enabled: true application-id: order-service # 應(yīng)用ID用于在TC標(biāo)識(shí)自己 tx-service-group: my_test_tx_group # 事務(wù)組需要和TC配置對(duì)應(yīng) service: vgroup-mapping: my_test_tx_group: default # 將事務(wù)組映射到TC的集群名file模式下通常是default grouplist: default: 你的服務(wù)器IP:8091 # TC服務(wù)地址列表 config: type: file registry: type: file >-- 以MySQL為例 CREATE TABLE undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, ext VARCHAR(100) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE InnoDB AUTO_INCREMENT 1 DEFAULT CHARSET utf8;第四步在業(yè)務(wù)入口方法上添加注解Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private RestTemplate restTemplate; // 用于調(diào)用庫(kù)存服務(wù) Override GlobalTransactional(timeoutMills 300000, name createOrder-tx) // 核心注解 public Order createOrder(OrderDTO orderDTO) { // 1. 本地事務(wù)創(chuàng)建訂單 Order order convertToOrder(orderDTO); orderMapper.insert(order); // 2. 遠(yuǎn)程調(diào)用扣減庫(kù)存這是一個(gè)分布式調(diào)用 String url http://stock-service/stock/decrease?productId orderDTO.getProductId() count orderDTO.getCount(); ResponseEntityVoid response restTemplate.postForEntity(url, null, Void.class); if (!response.getStatusCode().is2xxSuccessful()) { // 如果調(diào)用失敗會(huì)拋出異常觸發(fā)全局回滾 throw new RuntimeException(庫(kù)存扣減失敗); } // 3. 模擬其他業(yè)務(wù)操作... // 如果這里拋出異常同樣會(huì)觸發(fā)全局回滾庫(kù)存扣減操作會(huì)被補(bǔ)償恢復(fù) return order; } }庫(kù)存服務(wù)Stock Service的配置和代碼類似也需要引入SEATA依賴、配置數(shù)據(jù)源代理、創(chuàng)建undo_log表。它的decrease方法雖然也是一個(gè)數(shù)據(jù)庫(kù)更新操作但不需要再添加GlobalTransactional只需要使用Transactional保證本地事務(wù)即可。全局事務(wù)的上下文XID會(huì)通過RestTemplate的攔截器或Feign、Dubbo的過濾器自動(dòng)在服務(wù)間傳遞。實(shí)操心得在配置seata.tx-service-group和service.vgroup-mapping時(shí)名字一定要對(duì)應(yīng)上這是客戶端找到正確TC集群的關(guān)鍵。很多初學(xué)者啟動(dòng)報(bào)“no available server to connect”錯(cuò)誤八成是這里配錯(cuò)了或者TC地址沒寫對(duì)。4. 核心環(huán)節(jié)實(shí)現(xiàn)與參數(shù)調(diào)優(yōu)環(huán)境搭好了注解也加上了但這只是開始。要讓SEATA AT模式在生產(chǎn)環(huán)境中穩(wěn)定運(yùn)行還需要關(guān)注一些核心環(huán)節(jié)和參數(shù)。4.1 全局事務(wù)IDXID的傳遞分布式事務(wù)的核心是XID在整個(gè)調(diào)用鏈中的透?jìng)?。SEATA提供了多種微服務(wù)RPC框架的集成模塊。Spring Cloud OpenFeign引入seata-spring-boot-starter后會(huì)自動(dòng)配置Feign的攔截器。Apache Dubbo需要使用GlobalTransactional的服務(wù)的Provider和Consumer都引入SEATA依賴并配置好Filter。RestTemplate需要手動(dòng)配置一個(gè)攔截器將RootContext.getXID()放入請(qǐng)求頭如TX_XID中下游服務(wù)再?gòu)恼?qǐng)求頭中取出并綁定到自己的上下文。示例RestTemplate攔截器配置Configuration public class SeataRestTemplateConfig { Bean public RestTemplate restTemplate() { RestTemplate restTemplate new RestTemplate(); // 添加SEATA XID傳遞攔截器 restTemplate.setInterceptors(Collections.singletonList(new ClientHttpRequestInterceptor() { Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { String xid RootContext.getXID(); if (StringUtils.isNotBlank(xid)) { request.getHeaders().add(TX_XID, xid); } return execution.execute(request, body); } })); return restTemplate; } }在下游服務(wù)中你需要一個(gè)類似的過濾器來接收并綁定XID。4.2 關(guān)鍵參數(shù)調(diào)優(yōu)指南SEATA的默認(rèn)配置適合測(cè)試生產(chǎn)環(huán)境需要根據(jù)壓力進(jìn)行調(diào)整??蛻舳伺渲?(application.yml)seata: client: rm: report-success-enable: false # 分支事務(wù)一階段成功是否立即上報(bào)TC默認(rèn)false異步上報(bào)即可。 report-retry-count: 5 # 分支事務(wù)狀態(tài)上報(bào)重試次數(shù) async-commit-buffer-limit: 10000 # 異步提交緩存隊(duì)列大小高并發(fā)可調(diào)大 lock: retry-interval: 10 # 獲取全局鎖重試間隔(ms) retry-times: 30 # 獲取全局鎖重試次數(shù) tm: commit-retry-count: 5 # 全局事務(wù)提交重試次數(shù) rollback-retry-count: 5 # 全局事務(wù)回滾重試次數(shù) service: disable-global-transaction: false # 緊急情況下可動(dòng)態(tài)關(guān)閉全局事務(wù)GlobalTransactional注解參數(shù)timeoutMills全局事務(wù)超時(shí)時(shí)間單位是毫秒。默認(rèn)60秒。這個(gè)時(shí)間要設(shè)置得比所有分支事務(wù)可能執(zhí)行時(shí)間的總和還要長(zhǎng)否則會(huì)超時(shí)回滾。例如你的下單流程涉及3個(gè)服務(wù)每個(gè)服務(wù)本地事務(wù)最多要5秒網(wǎng)絡(luò)調(diào)用可能2秒那么建議設(shè)置timeoutMills3000030秒以上。name給全局事務(wù)起個(gè)名字方便在控制臺(tái)查看和排查問題。rollbackFor/noRollbackFor指定哪些異常觸發(fā)回滾或不回滾。服務(wù)端配置 (server端 file.conf)store.mode生產(chǎn)環(huán)境強(qiáng)烈建議使用db模式。file模式性能差且服務(wù)器重啟后事務(wù)日志會(huì)丟失可能導(dǎo)致狀態(tài)不一致。配置db模式需要指定數(shù)據(jù)庫(kù)連接信息。session.reload.read_sizeTC從存儲(chǔ)中讀取會(huì)話的批次大小高并發(fā)可調(diào)大。注意事項(xiàng)timeoutMills設(shè)置過小是新手常踩的坑。一個(gè)復(fù)雜的業(yè)務(wù)流程如果包含了外部API調(diào)用、復(fù)雜的數(shù)據(jù)庫(kù)操作30秒可能根本不夠。一旦超時(shí)整個(gè)全局事務(wù)會(huì)回滾但業(yè)務(wù)可能已經(jīng)部分完成了造成數(shù)據(jù)不一致假象。建議根據(jù)監(jiān)控?cái)?shù)據(jù)如APM鏈路追蹤來設(shè)定一個(gè)合理的值并留出充足余量。5. 常見問題排查與實(shí)戰(zhàn)避坑指南理論很美好現(xiàn)實(shí)常踩坑。下面是我在多次實(shí)踐中總結(jié)的典型問題及排查思路。5.1 問題排查清單問題現(xiàn)象可能原因排查步驟與解決方案啟動(dòng)報(bào)錯(cuò)no available server to connect1. TC服務(wù)未啟動(dòng)或端口不對(duì)。2. 客戶端配置的seata.service.grouplist地址錯(cuò)誤。3. 網(wǎng)絡(luò)不通防火墻、安全組。4. 事務(wù)組名tx-service-group與TC的vgroupMapping不匹配。1. 檢查TC服務(wù)進(jìn)程和日志 (docker logs seata-server)。2. 在客戶端服務(wù)器上用telnet TC_IP 8091測(cè)試連通性。3. 核對(duì)客戶端yml中g(shù)rouplist的IP和端口。4. 核對(duì)tx-service-group和vgroup-mapping的映射關(guān)系。全局事務(wù)不生效注解加了但沒開啟事務(wù)1. 啟動(dòng)類上忘了加EnableAutoDataSourceProxy舊版或數(shù)據(jù)源代理模式未正確配置。2. 調(diào)用GlobalTransactional方法的方式不對(duì)如類內(nèi)部調(diào)用繞過了AOP代理。1. 確認(rèn)seata.data-source-proxy-modeAT已配置。2.確保是通過Spring代理對(duì)象調(diào)用的方法。在同一個(gè)Service類中方法A調(diào)用方法BB上有注解事務(wù)不會(huì)生效。應(yīng)將該方法放到另一個(gè)Service中或使用AopContext.currentProxy()??刂婆_(tái)看到全局事務(wù)一直處于Begin狀態(tài)不結(jié)束1. 分支事務(wù)執(zhí)行時(shí)間過長(zhǎng)超過timeoutMills。2. 某個(gè)分支事務(wù)卡住如死鎖導(dǎo)致TC無(wú)法收到二階段報(bào)告。3. 網(wǎng)絡(luò)問題RM上報(bào)狀態(tài)失敗。1. 檢查TC日志和業(yè)務(wù)日志看是否有超時(shí)或錯(cuò)誤。2. 在控制臺(tái)查看該全局事務(wù)的詳細(xì)分支列表定位是哪個(gè)分支卡住。3. 檢查數(shù)據(jù)庫(kù)鎖情況。適當(dāng)調(diào)大timeoutMills和鎖重試參數(shù)。數(shù)據(jù)回滾失敗undo_log表有數(shù)據(jù)但業(yè)務(wù)數(shù)據(jù)沒恢復(fù)1. 回滾日志rollback_info字段異常如鏡像數(shù)據(jù)不完整。2. 回滾時(shí)執(zhí)行的補(bǔ)償SQL因約束如唯一鍵沖突失敗。3. 業(yè)務(wù)表結(jié)構(gòu)在事務(wù)執(zhí)行后發(fā)生了變更。1. 查看undo_log表中對(duì)應(yīng)記錄的rollback_info需解碼檢查前鏡像數(shù)據(jù)是否正確。2.這是AT模式的硬傷。確保補(bǔ)償操作回滾一定是冪等的且能成功執(zhí)行。對(duì)于有嚴(yán)格約束的場(chǎng)景要格外小心。3. 嚴(yán)禁在事務(wù)進(jìn)行中變更表結(jié)構(gòu)。報(bào)錯(cuò)Could not retrieve transaction info常見于使用GlobalTransactional和Transactional混用且傳播行為設(shè)置不當(dāng)。避免在標(biāo)記了GlobalTransactional的方法內(nèi)部再使用Transactional(propagation Propagation.REQUIRES_NEW)等會(huì)開啟獨(dú)立事務(wù)的傳播行為。這會(huì)導(dǎo)致連接上下文混亂。建議在全局事務(wù)內(nèi)分支事務(wù)使用默認(rèn)的Propagation.REQUIRED。5.2 實(shí)戰(zhàn)避坑經(jīng)驗(yàn)SQL編寫規(guī)范AT模式依賴SQL解析。盡量使用簡(jiǎn)單的、標(biāo)準(zhǔn)的SQL語(yǔ)句。避免使用數(shù)據(jù)庫(kù)特有的函數(shù)、復(fù)雜的子查詢更新、多表關(guān)聯(lián)更新update a,b set a.x1 where a.idb.id。對(duì)于復(fù)雜更新可以拆分為多個(gè)簡(jiǎn)單SQL或者考慮使用TCC模式。undo_log表維護(hù)這張表會(huì)隨著事務(wù)增長(zhǎng)。需要建立定期清理機(jī)制如只保留7天的日志避免表過大影響性能。可以在業(yè)務(wù)低峰期執(zhí)行delete from undo_log where log_created DATE_SUB(NOW(), INTERVAL 7 DAY)。異常處理要干凈在GlobalTransactional注解的方法內(nèi)如果捕獲了異常但沒有重新拋出SEATA會(huì)認(rèn)為業(yè)務(wù)執(zhí)行成功不會(huì)觸發(fā)回滾。確保需要回滾的異常一定要拋出去。做好監(jiān)控與告警集成SEATA控制臺(tái)并關(guān)注其監(jiān)控指標(biāo)。更佳實(shí)踐是將SEATA的事務(wù)狀態(tài)如超時(shí)事務(wù)數(shù)、回滾失敗數(shù)接入公司的APM如SkyWalking、Pinpoint或監(jiān)控系統(tǒng)PrometheusGrafana設(shè)置告警規(guī)則。預(yù)備降級(jí)方案分布式事務(wù)增加了系統(tǒng)復(fù)雜性。在設(shè)計(jì)時(shí)就要考慮“如果SEATA掛了怎么辦”??梢酝ㄟ^配置seata.service.disable-global-transaction: true快速切換到“無(wú)分布式事務(wù)”的降級(jí)模式此時(shí)GlobalTransactional注解失效僅剩本地事務(wù)并通過其他手段如對(duì)賬、補(bǔ)償job來保證最終一致性。SEATA的AT模式是一個(gè)強(qiáng)大的工具它用較低的代碼侵入成本解決了大多數(shù)中小型分布式系統(tǒng)的數(shù)據(jù)一致性問題。但它不是銀彈理解其原理、明確其邊界、做好配置和監(jiān)控才能讓它真正為你的系統(tǒng)穩(wěn)定性保駕護(hù)航而不是成為新的故障源。從簡(jiǎn)單的服務(wù)開始嘗試逐步積累經(jīng)驗(yàn)?zāi)銜?huì)發(fā)現(xiàn)在微服務(wù)的世界里管理數(shù)據(jù)一致性并沒有想象中那么可怕。

相關(guān)新聞

密碼安全進(jìn)階:鹽與胡椒在加密存儲(chǔ)中的關(guān)鍵作用

密碼安全進(jìn)階:鹽與胡椒在加密存儲(chǔ)中的關(guān)鍵作用

1. 密碼安全的核心要素解析當(dāng)我們?cè)谟懻撁艽a安全時(shí),大多數(shù)人第一反應(yīng)就是"加密"——這確實(shí)沒錯(cuò),但遠(yuǎn)遠(yuǎn)不夠。就像做一道好菜,光有主料不行,還需要調(diào)味料來提升風(fēng)味。在密碼學(xué)領(lǐng)域,"鹽"(Salt)和&qu…

2026/7/29 4:36:03 閱讀更多
uni-app路由跳轉(zhuǎn)全解析:六種方式、實(shí)戰(zhàn)場(chǎng)景與性能優(yōu)化

uni-app路由跳轉(zhuǎn)全解析:六種方式、實(shí)戰(zhàn)場(chǎng)景與性能優(yōu)化

1. 項(xiàng)目概述:為什么uni-app的路由跳轉(zhuǎn)值得深挖?在uni-app的開發(fā)日常里,頁(yè)面跳轉(zhuǎn)是比呼吸還頻繁的操作。從最簡(jiǎn)單的商品列表到詳情頁(yè),到復(fù)雜的多級(jí)表單流程,再到需要登錄攔截的權(quán)限控制,路由跳轉(zhuǎn)是串聯(lián)起整個(gè)…

2026/7/29 4:36:03 閱讀更多
python爬取貝殼中二手房的數(shù)據(jù)

python爬取貝殼中二手房的數(shù)據(jù)

前言:通過代碼爬取貝殼中二手房的數(shù)據(jù),以此給更多需要了解爬蟲或者二手房信息的人提供便利。 第一部分:爬取地址 1.1貝殼首頁(yè)地址 jiujiang.ke.com 第二部分:爬取數(shù)據(jù) 2.1輸入要爬多少頁(yè) int(input(輸入一共要多少頁(yè)&#xf…

2026/7/29 4:26:03 閱讀更多
軟件設(shè)計(jì)師備考全攻略:從知識(shí)體系構(gòu)建到實(shí)戰(zhàn)應(yīng)試技巧

軟件設(shè)計(jì)師備考全攻略:從知識(shí)體系構(gòu)建到實(shí)戰(zhàn)應(yīng)試技巧

1. 備考緣起與核心價(jià)值最近幾年,身邊不少朋友和同事都在討論“軟考”,尤其是中級(jí)資格的《軟件設(shè)計(jì)師》。有人是為了職稱評(píng)定,有人是為了積分落戶,也有人純粹是想系統(tǒng)梳理一下自己的知識(shí)體系,給職業(yè)生涯加個(gè)“官方認(rèn)證”…

2026/7/29 5:36:05 閱讀更多
跨境支付系統(tǒng)架構(gòu)演進(jìn):從SWIFT到本地化清算通道

跨境支付系統(tǒng)架構(gòu)演進(jìn):從SWIFT到本地化清算通道

背景:跨境支付為什么這么慢?做過跨境支付系統(tǒng)開發(fā)的工程師應(yīng)該都有體會(huì)——一筆從國(guó)內(nèi)到海外的資金,動(dòng)輒3到5個(gè)工作日才能到賬。問題不出在銀行系統(tǒng)慢,出在底層架構(gòu)上。傳統(tǒng)跨境支付走的是SWIFT網(wǎng)絡(luò)。資金從匯款行出發(fā)&#xff0c…

2026/7/29 5:36:05 閱讀更多
C++編程思維構(gòu)建:從std::array遍歷到遞歸逆序打印的深度解析

C++編程思維構(gòu)建:從std::array遍歷到遞歸逆序打印的深度解析

1. 項(xiàng)目概述:從“打印”與“遞歸”窺探C編程思維的構(gòu)建拿到這個(gè)標(biāo)題,我仿佛回到了當(dāng)年啃教材、一行行調(diào)試代碼的時(shí)光。“C大學(xué)教程(第九版)7.30 打印array對(duì)象 7.31 逆序打印字符串(遞歸練習(xí)題)”&#xff…

2026/7/29 5:36:05 閱讀更多
Pandas數(shù)據(jù)處理實(shí)戰(zhàn):從Series與DataFrame基礎(chǔ)到完整工作流

Pandas數(shù)據(jù)處理實(shí)戰(zhàn):從Series與DataFrame基礎(chǔ)到完整工作流

1. 項(xiàng)目概述:從闖關(guān)實(shí)驗(yàn)看數(shù)據(jù)處理核心技能最近在“頭歌”平臺(tái)上帶學(xué)生過Python數(shù)據(jù)處理實(shí)驗(yàn),發(fā)現(xiàn)很多新手卡在了數(shù)據(jù)框和序列的基本操作上。這其實(shí)是個(gè)挺普遍的現(xiàn)象:大家學(xué)Python數(shù)據(jù)分析,一上來就被pandas庫(kù)的DataFrame和Series…

2026/7/29 5:36:05 閱讀更多
智能Bot產(chǎn)品核心價(jià)值定位與實(shí)戰(zhàn)框架

智能Bot產(chǎn)品核心價(jià)值定位與實(shí)戰(zhàn)框架

1. Clawdbot的啟示:智能Bot產(chǎn)品的核心價(jià)值定位第一次接觸Clawdbot時(shí),最讓我驚訝的是它解決實(shí)際業(yè)務(wù)痛點(diǎn)的精準(zhǔn)度。這個(gè)智能Bot沒有堆砌花哨的AI功能,而是聚焦于企業(yè)決策層的核心需求——通過自動(dòng)化數(shù)據(jù)抓取和智能分析,將分散在各系…

2026/7/29 5:26:04 閱讀更多
面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個(gè)月,我在重構(gòu) AlgoMooc 網(wǎng)站過程中,發(fā)現(xiàn)一個(gè)問題:在 Claude Code 里把一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,結(jié)果可能比 1 個(gè) agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過來的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂應(yīng)用,模擬了真實(shí)擲骰子的過程。應(yīng)用投擲兩個(gè)骰子(六面標(biāo)準(zhǔn)骰),使用 Unicode 骰面符號(hào)直觀展示每個(gè)骰子的點(diǎn)數(shù),并伴有快速滾動(dòng)的動(dòng)畫效果?!?/p>

2026/7/29 0:15:24 閱讀更多