)
SEATA Sharding-JDBC 分庫分表分布式事務(wù)完整實戰(zhàn)原理場景代碼優(yōu)缺點一、業(yè)務(wù)背景在海量數(shù)據(jù)高并發(fā)微服務(wù)場景中單庫單表會出現(xiàn)數(shù)據(jù)量大、查詢慢、寫入瓶頸、索引失效等問題行業(yè)普遍采用Sharding-JDBC做分庫分表、讀寫分離解決存儲與性能瓶頸。但分庫分表后會引入新的核心問題跨分片分布式事務(wù)。Sharding-JDBC 僅負(fù)責(zé) SQL 路由、分片規(guī)則、多數(shù)據(jù)源管理不具備分布式事務(wù)一致性能力。當(dāng)一次業(yè)務(wù)操作涉及多個分庫、多個分片表寫入/更新時本地事務(wù)只能保證單庫一致性必然出現(xiàn)部分分片成功、部分分片失敗的數(shù)據(jù)臟寫問題。因此生產(chǎn)環(huán)境必須引入SEATA AT 模式結(jié)合 Sharding-JDBC解決分庫分表場景下的跨分片分布式事務(wù)一致性問題實現(xiàn)分表擴容事務(wù)可控的高可用架構(gòu)。二、核心適用場景SEATA Sharding-JDBC 組合方案并非所有項目都需要使用精準(zhǔn)適配以下業(yè)務(wù)場景海量數(shù)據(jù)業(yè)務(wù)訂單、支付、流水、用戶行為數(shù)據(jù)單表數(shù)據(jù)量突破千萬/億級需要水平分表擴容高并發(fā)寫入場景秒殺、下單、流水記錄單庫寫入壓力過大需要分庫分擔(dān)IO壓力跨分片事務(wù)場景一次業(yè)務(wù)操作需要同時修改多個分片數(shù)據(jù)必須保證多分片同時成功/同時回滾高性能、低侵入訴求不想使用TCC硬編碼事務(wù)希望通過框架自動實現(xiàn)分布式事務(wù)讀寫分離分庫分表混合架構(gòu)多數(shù)據(jù)源、多分片復(fù)雜架構(gòu)下的數(shù)據(jù)一致性保障。不適用場景數(shù)據(jù)量小、并發(fā)低、單庫完全夠用的項目過度架構(gòu)增加維護成本金融級強一致性核心賬務(wù)優(yōu)先Sharding-TCC模式。三、整合核心原理3.1 組件分工Sharding-JDBCJDBC層中間件負(fù)責(zé)攔截SQL、根據(jù)分片算法路由至對應(yīng)分庫分表、管理多數(shù)據(jù)源、合并查詢結(jié)果SEATA AT模式負(fù)責(zé)跨分片分布式事務(wù)管控一階段記錄undo鏡像、二階段統(tǒng)一提交/回滾保證多分片數(shù)據(jù)最終一致。3.2 關(guān)鍵整合機制核心難點原生 SEATA 會通過DataSourceProxy代理數(shù)據(jù)源實現(xiàn)事務(wù)管控Sharding-JDBC 內(nèi)部擁有自己的數(shù)據(jù)源分片代理邏輯。兩者直接整合會出現(xiàn)數(shù)據(jù)源代理沖突、事務(wù)失效。標(biāo)準(zhǔn)解決方案SEATA 嵌套代理 Sharding 數(shù)據(jù)源執(zhí)行鏈路業(yè)務(wù)SQL → SEATA數(shù)據(jù)源代理 → Sharding-JDBC分片路由 → 真實分庫數(shù)據(jù)源 → 執(zhí)行SQL并生成undo鏡像3.3 分片事務(wù)執(zhí)行流程TM開啟全局事務(wù)業(yè)務(wù)方法添加GlobalTransactional生成全局XIDSQL分片路由Sharding-JDBC 根據(jù)分片鍵路由到一個或多個分片庫表一階段預(yù)提交所有分片分支執(zhí)行SQLSEATA 生成每個分片的undo_log鏡像本地事務(wù)提交、釋放鎖二階段統(tǒng)一處理全局成功批量刪除所有分片undo_log事務(wù)正常結(jié)束全局異常根據(jù)各分片undo鏡像反向回滾所有分片數(shù)據(jù)保證多分片數(shù)據(jù)一致。四、方案優(yōu)缺點分析4.1 核心優(yōu)點零代碼侵入基于SEATA AT模式無需手動編寫Try/Confirm/Cancel僅注解開啟事務(wù)適配快速開發(fā)解決分片事務(wù)痛點徹底解決跨庫、跨分片部分成功、部分失敗的數(shù)據(jù)一致性問題高性能高并發(fā)一階段提交、短事務(wù)、無長鎖相比XA、TCC性能更高適配海量分片場景架構(gòu)解耦分片擴容由Sharding管控事務(wù)一致性由SEATA管控職責(zé)清晰、擴展靈活兼容主流架構(gòu)完美適配SpringCloud、MyBatis、MyBatis-Plus生態(tài)。4.2 現(xiàn)存缺點與生產(chǎn)局限存在臟讀風(fēng)險AT模式一階段釋鎖高并發(fā)跨分片場景存在短暫臟讀、數(shù)據(jù)覆蓋風(fēng)險極端場景會觸發(fā)鏡像校驗失敗、事務(wù)重試卡死不支持強隔離級別無法做到串行化隔離金融核心賬務(wù)場景不適用整合配置復(fù)雜多數(shù)據(jù)源、分片規(guī)則、事務(wù)分組配置不當(dāng)極易出現(xiàn)事務(wù)不生效、代理沖突大事務(wù)風(fēng)險放大跨多分片大事務(wù)一旦超時會出現(xiàn)多分片事務(wù)狀態(tài)割裂修復(fù)成本極高undo日志膨脹分片越多事務(wù)日志越多增加數(shù)據(jù)庫存儲與清理壓力。五、入門實戰(zhàn)SEATA Sharding-JDBC 完整整合案例5.0 整合核心常用注解及適配業(yè)務(wù)場景SEATA 結(jié)合 Sharding-JDBC 實現(xiàn)跨分片分布式事務(wù)核心依靠兩類注解實現(xiàn)事務(wù)管控與業(yè)務(wù)適配不同注解對應(yīng)不同分片業(yè)務(wù)場景是入門開發(fā)與生產(chǎn)落地的核心重點下面結(jié)合分庫分表場景逐一說明。5.0.1 GlobalTransactionalSEATA 全局事務(wù)注解注解作用開啟跨庫、跨分片全局分布式事務(wù)是 SEATASharding-JDBC 整合的核心注解??山y(tǒng)一管控多個分片數(shù)據(jù)源的分支事務(wù)保證所有分片數(shù)據(jù)要么全部提交、要么全部回滾徹底解決分庫分表「部分分片成功、部分分片失敗」的一致性問題。核心屬性適配分片場景rollbackFor Exception.class捕獲所有業(yè)務(wù)異常任意分片報錯觸發(fā)全分片回滾適配跨多分片復(fù)雜業(yè)務(wù)timeout自定義全局事務(wù)超時時間解決跨多庫分片事務(wù)耗時過長導(dǎo)致的強制回滾失效問題propagation支持事務(wù)傳播配置適配多服務(wù)嵌套分片事務(wù)場景。貼合使用場景一次業(yè)務(wù)操作路由多個分庫、多個分片表的讀寫場景如用戶下單根據(jù)user_id分庫、order_id分表單次下單跨分片寫入跨分片數(shù)據(jù)聯(lián)動更新場景如下單同步扣減分片庫存、生成分片流水記錄高并發(fā)分片寫入場景需要嚴(yán)格保證多分片數(shù)據(jù)一致性禁止臟數(shù)據(jù)殘留。5.0.2 TransactionalSpring本地事務(wù)注解注解作用在 SEATASharding 整合架構(gòu)中僅用于單分片本地事務(wù)兜底負(fù)責(zé)單個數(shù)據(jù)源內(nèi)部的SQL事務(wù)控制不具備跨分片事務(wù)能力必須配合 GlobalTransactional 使用。貼合使用場景單個分片內(nèi)多條SQL批量執(zhí)行保證單庫數(shù)據(jù)本地一致性作為全局事務(wù)的分支兜底輔助SEATA完成單分片事務(wù)提交與回滾。重要規(guī)范分庫分表跨分片事務(wù)必須以 GlobalTransactional 為入口僅使用 Transactional 會導(dǎo)致跨分片事務(wù)完全失效。5.0.3 注解組合使用規(guī)范生產(chǎn)標(biāo)準(zhǔn)分片分布式事務(wù)標(biāo)準(zhǔn)寫法入口方法加 GlobalTransactional分支業(yè)務(wù)方法加 Transactional形成「全局事務(wù)統(tǒng)籌本地事務(wù)兜底」的雙層事務(wù)架構(gòu)。5.1 項目依賴5.1 項目依賴核心引入Sharding-JDBC分片依賴、SEATA事務(wù)依賴版本適配SpringCloud Alibaba生態(tài)。!-- sharding-jdbc 分庫分表核心依賴 -- dependency groupIdorg.apache.shardingsphere/groupId artifactIdsharding-jdbc-spring-boot-starter/artifactId version4.1.1/version /dependency !-- seata 分布式事務(wù) -- dependency groupIdio.seata/groupId artifactIdseata-spring-boot-starter/artifactId version1.5.2/version /dependency5.2 數(shù)據(jù)庫準(zhǔn)備模擬分庫場景創(chuàng)建2個分庫order_db_0、order_db_1每個庫包含訂單分片表t_order_0 ~ t_order_1所有分片庫必須初始化undo_log表。CREATE TABLE IF NOT EXISTS undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 自增主鍵, branch_id BIGINT(20) NOT NULL COMMENT 分支事務(wù)ID, xid VARCHAR(128) NOT NULL COMMENT 全局事務(wù)ID, context VARCHAR(128) NOT NULL COMMENT 上下文, rollback_info LONGBLOB NOT NULL COMMENT 回滾鏡像信息, log_status INT(11) NOT NULL COMMENT 0正常1已回滾, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid,branch_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENTSEATA事務(wù)回滾日志表;5.3 核心差異化配置整合專屬與單獨使用差異對比SEATA、Sharding-JDBC單獨使用時無需特殊適配但二者整合場景存在數(shù)據(jù)源代理、事務(wù)托管沖突必須添加專屬差異化配置也是整合事務(wù)生效的核心關(guān)鍵僅列舉差異配置常規(guī)分片、SEATA基礎(chǔ)配置不再贅述。# SEATA Sharding 整合差異化專屬配置 spring: shardingsphere: # 核心差異1關(guān)閉Sharding原生事務(wù)避免與SEATA事務(wù)沖突 # 單獨用Sharding默認(rèn)LOCAL整合SEATA必須改為NONE交由SEATA統(tǒng)一托管 transaction: type: NONE # 其余常規(guī)分片規(guī)則、數(shù)據(jù)源配置、SEATA注冊配置與單獨使用保持一致無需改動配套Java差異化配置唯一代碼差異單獨使用 Sharding/SEATA 無需手動代理數(shù)據(jù)源整合場景必須手動用 SEATA 數(shù)據(jù)源代理包裹 Sharding 數(shù)據(jù)源解決多層代理沖突否則跨分片事務(wù)完全不生效。Configuration public class SeataShardingConfig { Bean Primary public DataSource dataSource(ShardingDataSource shardingDataSource) { // 整合專屬差異SEATA嵌套代理Sharding數(shù)據(jù)源 return new DataSourceProxy(shardingDataSource); } }全局事務(wù)模式兜底差異生產(chǎn)環(huán)境統(tǒng)一兜底配置spring.shardingsphere.transaction.typeBASE適配SEATA AT模式禁止XA/LOCAL模式混用規(guī)避事務(wù)協(xié)議沖突。重點關(guān)閉Sharding自帶事務(wù)、適配SEATA事務(wù)代理保證分布式事務(wù)生效。spring: # 關(guān)閉sharding默認(rèn)本地事務(wù)交給seata托管 shardingsphere: props: sql-show: true allow-range-query-without-sharding-key: true # 關(guān)閉sharding原生事務(wù) transaction: type: NONE # 分庫分表規(guī)則配置 datasource: names: ds0,ds1 ds0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/order_db_0?useSSLfalseserverTimezoneGMT%2B8 username: root password: 123456 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/order_db_1?useSSLfalseserverTimezoneGMT%2B8 username: root password: 123456 sharding: tables: t_order: actual-data-nodes: ds$-{0..1}.t_order_$-{0..1} database-strategy: inline: sharding-column: user_id algorithm-expression: ds$-{user_id % 2} table-strategy: inline: sharding-column: order_id algorithm-expression: t_order_$-{order_id % 2} # SEATA 分布式事務(wù)配置 seata: enabled: true tx-service-group: default_tx_group service: vgroup-mapping: default_tx_group: default registry: type: nacos nacos: server-addr: 127.0.0.1:8848 config: type: nacos nacos: server-addr: 127.0.0.1:8848手動注入SEATA代理數(shù)據(jù)源覆蓋Sharding默認(rèn)數(shù)據(jù)源解決事務(wù)不生效、代理沖突核心問題。Configuration public class SeataShardingConfig { Bean Primary public DataSource dataSource(ShardingDataSource shardingDataSource) { // SEATA代理Sharding數(shù)據(jù)源實現(xiàn)分片事務(wù)管控 return new DataSourceProxy(shardingDataSource); } }5.5 業(yè)務(wù)實戰(zhàn)代碼跨分片分布式事務(wù)模擬一次下單跨分片寫入手動制造異常測試多分片事務(wù)一致性。Service public class OrderServiceImpl implements OrderService { Resource private OrderMapper orderMapper; // 開啟跨分片分布式事務(wù) Override GlobalTransactional(rollbackFor Exception.class) public void createOrder(Order order) { // 1.插入訂單自動根據(jù)分片鍵路由至不同分庫分表 orderMapper.insert(order); // 2.模擬業(yè)務(wù)異常觸發(fā)全分片回滾 int i 1 / 0; } }5.6 效果驗證正常無異常所有分片數(shù)據(jù)寫入成功undo_log自動清除代碼拋出異常所有已寫入的分片數(shù)據(jù)全部回滾無任何臟數(shù)據(jù)殘留徹底解決原生Sharding-JDBC跨分片部分成功、部分失敗的事務(wù)問題。5.7 分片場景注解搭配高頻坑點匯總生產(chǎn)致命問題SEATA Sharding-JDBC 整合架構(gòu)中注解使用不規(guī)范是跨分片事務(wù)失效、臟讀、回滾重試卡死、數(shù)據(jù)不一致的第一大成因。下面匯總生產(chǎn) 7 類最高頻、最隱蔽的注解搭配錯誤明確錯誤場景、故障原理與標(biāo)準(zhǔn)規(guī)范。5.7.1 坑點一只寫 GlobalTransactional不加 ShardingSphereTransactionType(BASE)錯誤場景跨多分庫、多分片業(yè)務(wù)僅使用 SEATA 全局事務(wù)注解未手動指定 Sharding 事務(wù)類型。故障原理Sharding-JDBC 默認(rèn)自帶本地事務(wù)管控若未強制指定BASE適配 SEATA AT 模式分片底層會走原生本地事務(wù)邏輯??绶制霈F(xiàn)異常時Sharding 本地事務(wù)無法聯(lián)動 SEATA 全局事務(wù)部分分片本地提交成功、SEATA 全局回滾指令無法覆蓋分片數(shù)據(jù)最終導(dǎo)致分庫數(shù)據(jù)回滾失效、分片數(shù)據(jù)不一致。正確規(guī)范SEATA AT 整合分片場景統(tǒng)一配置spring.shardingsphere.transaction.typeBASE全局適配 SEATA 事務(wù)托管杜絕原生事務(wù)沖突。5.7.2 坑點二GlobalLock 缺少 Transactional / for update錯誤場景分片高并發(fā)讀寫場景僅使用 SEATA 全局鎖注解GlobalLock未搭配本地事務(wù)或行鎖。故障原理GlobalLock 僅控制全局分布式鎖不替代本地事務(wù)與數(shù)據(jù)庫行鎖。缺少本地事務(wù)包裹時數(shù)據(jù)庫語句自動提交、鎖瞬間釋放全局鎖約束失效。高并發(fā)下依然存在跨分片臟讀、數(shù)據(jù)覆蓋問題最終觸發(fā) SEATAundo 鏡像校驗失敗、事務(wù)無限重試卡死。正確規(guī)范GlobalLock 必須嵌套 Transactional 本地事務(wù)熱點數(shù)據(jù)查詢更新搭配 for update 行鎖雙重杜絕臟讀。5.7.3 坑點三分片場景 GlobalLock lockKey 未攜帶分片字段錯誤場景跨庫分表場景全局鎖 lockKey 僅使用業(yè)務(wù)ID未攜帶分片字段user_id/分庫標(biāo)識。故障原理Sharding 分片數(shù)據(jù)分散在不同庫、不同表中若鎖 Key 不攜帶分片字段鎖作用域會錯亂。不同分片的不同業(yè)務(wù)數(shù)據(jù)會共用同一把鎖或本該互斥的分片數(shù)據(jù)無法加鎖跨分片互相臟讀、數(shù)據(jù)覆蓋引發(fā)回滾校驗失敗。正確規(guī)范分片場景 lockKey 必須包含「分片鍵唯一業(yè)務(wù)ID」保證鎖粒度精準(zhǔn)綁定單分片單行數(shù)據(jù)隔離跨分片并發(fā)讀寫。5.7.4 坑點四DDL 語句放在 GlobalTransactional 方法內(nèi)錯誤場景全局事務(wù)方法中執(zhí)行建表、改表、加索引等 DDL 語句。故障原理MySQL 對 DDL 語句會強制觸發(fā)自動提交本地事務(wù)會直接打斷 SEATA 一階段鏡像生成流程導(dǎo)致當(dāng)前分支 undo 日志丟失、無回滾快照。全局事務(wù)異常時SEATA 無數(shù)據(jù)可還原事務(wù)回滾徹底失效產(chǎn)生永久性分片臟數(shù)據(jù)。正確規(guī)范所有 DDL 語句禁止納入 SEATA 全局事務(wù)單獨執(zhí)行、避開事務(wù)切面。5.7.5 坑點五遺漏內(nèi)層 Transactional 本地事務(wù)錯誤場景外層僅添加 GlobalTransactional內(nèi)層分片業(yè)務(wù)方法無任何本地事務(wù)注解。故障原理SEATA 分支 RM 生成 undo_log 鏡像的前置條件是本地事務(wù)上下文生效。內(nèi)層無 Transactional 時分片 SQL 自動提交、無法被 SEATA 切面攔截RM 分支無 undo 快照生成。二階段全局回滾時無鏡像可還原直接回滾失敗。正確規(guī)范嚴(yán)格遵循「外層 GlobalTransactional 內(nèi)層分片業(yè)務(wù) Transactional」雙層事務(wù)規(guī)范。5.7.6 坑點六混用 XA 與 BASE 事務(wù)類型錯誤場景項目同時存在 XA、BASE 兩種事務(wù)模式或分片配置與 SEATA 模式不匹配。故障原理XA 強一致性事務(wù)與 BASE 最終一致性事務(wù)的分支注冊、日志機制、鎖機制完全不兼容?;煊煤笫聞?wù)協(xié)議沖突SEATA TC 無法正常識別、注冊分片分支事務(wù)導(dǎo)致全局事務(wù)創(chuàng)建失敗、分片事務(wù)不生效、提交回滾異常。正確規(guī)范一個項目統(tǒng)一一種事務(wù)模式分片整合 SEATA AT 必須固定 typeBASE禁止多模式混用。5.7.7 坑點七單純依賴 GlobalTransactional完全放棄 Sharding 事務(wù)管控錯誤場景認(rèn)為開啟 SEATA 全局事務(wù)即可隨意關(guān)閉、修改 Sharding 事務(wù)配置。故障原理Sharding-JDBC 承擔(dān)多分片 SQL 路由、多數(shù)據(jù)源調(diào)度若完全取消 Sharding 事務(wù)適配分片多數(shù)據(jù)源執(zhí)行時序錯亂SEATA 無法統(tǒng)一接管多分片分支出現(xiàn)部分分片未納入全局事務(wù)、回滾遺漏的問題。正確規(guī)范必須配置spring.shardingsphere.transaction.typeBASE適配 SEATA不關(guān)閉分片事務(wù)適配能力實現(xiàn)框架分層托管。六、生產(chǎn)踩坑與解決方案6.1 坑點1整合后分布式事務(wù)不生效原因Sharding-JDBC原生事務(wù)與SEATA數(shù)據(jù)源代理沖突未關(guān)閉Sharding默認(rèn)事務(wù)。解決配置spring.shardingsphere.transaction.typeNONE強制交給SEATA托管。6.2 坑點2跨分片回滾觸發(fā)data validation failed原因高并發(fā)下多分片數(shù)據(jù)被外部線程篡改鏡像校驗失敗。解決熱點分片數(shù)據(jù)增加分布式鎖、禁止并發(fā)修改核心業(yè)務(wù)拆分小事務(wù)。6.3 坑點3多分片大事務(wù)超時回滾失敗原因跨多庫多分片事務(wù)耗時過長TC強制終止分片狀態(tài)割裂。解決禁止大事務(wù)、拆分分片業(yè)務(wù)調(diào)優(yōu)SEATA超時時間增加定時對賬兜底。七、全文總結(jié)Sharding-JDBC 解決了海量數(shù)據(jù)的存儲擴容、讀寫性能問題但缺失分布式事務(wù)能力SEATA AT 模式解決了跨分片數(shù)據(jù)一致性問題。兩者結(jié)合是目前互聯(lián)網(wǎng)海量數(shù)據(jù)高并發(fā)項目的標(biāo)準(zhǔn)生產(chǎn)架構(gòu)。該方案具備零侵入、高性能、易擴展的優(yōu)勢適配絕大多數(shù)電商、流水、賬單、用戶數(shù)據(jù)業(yè)務(wù)但存在AT模式天生的隔離性缺陷金融級強一致場景需要升級為Sharding SEATA TCC模式。同時生產(chǎn)必須遵循小事務(wù)規(guī)范、配合兜底對賬保證分片事務(wù)百分百可靠。