1 為什么要有服務端分庫分表?
-
ShardingSphere-Proxy 是 ShardingSphere 提供的服務端分庫分表工具,定位是“透明化的數據庫代理”。
-
它模擬 MySQL 或 PostgreSQL 的數據庫服務,應用程序(Application)只需像訪問單個數據庫一樣訪問 ShardingSphere-Proxy;
-
實際的分庫分表邏輯,由 ShardingSphere-Proxy 在代理層完成,對應用程序完全透明;
-
-
ShardingSphere-JDBC 已經能實現分庫分表,為什么還需要 ShardingSphere-Proxy?
-
對 ORM 框架更友好
-
ShardingSphere-JDBC 的問題:需要在業務代碼中直接寫分庫分表邏輯(比如代碼里寫:數據該路由到哪個庫/表)。如果項目用了 ORM 框架(如 MyBatis、Hibernate),這種代碼侵入式的邏輯容易和 ORM 沖突;
-
ShardingSphere-Proxy 的解決:作為第三方服務端代理,分庫分表邏輯在代理層(ShardingSphere-Proxy)處理,應用程序完全不用關心。應用只需按訪問單庫的方式寫代碼,無縫對接 ORM 框架;
-
-
對 DBA(數據庫管理員)更友好
-
ShardingSphere-JDBC 的問題:DBA 要管理分庫分表后的數據庫,得先了解 ShardingSphere 的專屬功能和 API,學習成本高;
-
ShardingSphere-Proxy 的解決:對 DBA 完全透明。DBA 可以直接操作原始數據庫(像平時管理 MySQL/PostgreSQL 一樣),無需了解 ShardingSphere 的技術細節,簡化了 DBA 的工作;
-
-
避免業務代碼侵入分庫分表邏輯
-
ShardingSphere-JDBC 的問題:分庫分表的規則配置(比如“按用戶 ID 分庫”的規則)要寫在業務代碼里,會讓業務代碼變得臃腫。如果規則變更(比如從“按用戶 ID 分”改成“按訂單 ID 分”),要修改大量業務代碼,維護成本高;
-
ShardingSphere-Proxy 的解決:通過外部配置文件管理分庫分表規則,業務代碼完全不用關心這些邏輯。規則變更時,只需改配置,不影響業務代碼;
-
-
實現“無中心化”數據治理
-
ShardingSphere-JDBC 的局限:多數據源的治理(比如跨庫監控、報警、統一管理)難以統一;
-
ShardingSphere-Proxy 的解決:多個數據源可以注冊到同一個 ShardingSphere-Proxy 代理服務中。基于代理,能實現跨數據源的數據治理(比如統一監控、報警、數據管理),特別適合大規模微服務系統的運維。
-
-
2 基本使用
2.1 部署 ShardingSphere-Proxy
-
下載并解壓(注意不要有中文,此處選擇的版本:Apache Archive Distribution Directory);
-
ShardingSphere-Proxy 是一個典型的 Java 應用程序,解壓后目錄結構如下:
-
解壓完成后,如果要連接 MySQL 數據庫,那么需要手動將 JDBC 的驅動包
mysql-connector-java-8.0.20.jar
復制到 ShardingSphere-Proxy 的lib
目錄下(ShardingSphere-Proxy 默認只附帶了 PostgreSQL 的 JDBC 驅動包,沒有包含 MySQL 的 JDBC 驅動包); -
打開
conf
目錄,在這個目錄下就有 ShardingSphere-Proxy 的所有配置文件(不同版本的會不一樣): -
打開
server.yaml
,把其中的rules
部分和props
部分的注釋取消:rules:# 權限控制規則配置- !AUTHORITYusers:# 定義用戶權限:root用戶可從任何主機(%)訪問,擁有root角色;sharding用戶可從任何主機訪問,擁有sharding角色- root@%:root- sharding@:shardingprovider:# 權限提供者類型:ALL_PERMITTED表示允許所有操作(無權限限制)type: ALL_PERMITTED# 事務管理配置- !TRANSACTION# 默認事務類型:XA分布式事務defaultType: XA# 事務管理器提供者:Atomikos(一種XA事務管理器實現)providerType: Atomikos# SQL解析器配置- !SQL_PARSER# 啟用SQL注釋解析功能sqlCommentParseEnabled: truesqlStatementCache:# SQL語句緩存初始容量initialCapacity: 2000# SQL語句緩存最大容量maximumSize: 65535parseTreeCache:# 解析樹緩存初始容量initialCapacity: 128# 解析樹緩存最大容量maximumSize: 1024# 屬性配置 props:# 每個查詢允許的最大連接數max-connections-size-per-query: 1# 內核線程池大小(默認無限制)kernel-executor-size: 16# 代理前端刷新閾值(網絡數據包刷新條件)proxy-frontend-flush-threshold: 128# 是否啟用Hint功能(強制路由提示)proxy-hint-enabled: false# 是否在日志中顯示SQL語句sql-show: false# 是否啟用表元數據一致性檢查check-table-metadata-enabled: false# 代理后端查詢獲取大小:-1表示使用不同JDBC驅動程序的最小值proxy-backend-query-fetch-size: -1# 代理前端執行線程數:0表示由Netty自動決定proxy-frontend-executor-size: # 代理后端執行模式:OLAP(聯機分析處理)或OLTP(聯機事務處理)proxy-backend-executor-suitable: OLAP# 代理前端最大連接數:0或負數表示無限制proxy-frontend-max-connections: 0# SQL聯邦查詢類型:NONE(禁用),ORIGINAL(原始),ADVANCED(高級)sql-federation-type: NONE# 代理后端驅動類型:JDBC(標準)或ExperimentalVertx(實驗性Vert.x)proxy-backend-driver-type: JDBC# 默認MySQL版本號(當缺少schema信息時使用)proxy-mysql-default-version: 8.0.20# 代理服務默認端口proxy-default-port: 3307# Netty網絡連接后備隊列長度proxy-netty-backlog: 1024
- 注意:為了與 MySQL JDBC 的驅動包
mysql-connector-java-8.0.20.jar
兼容,將proxy-mysql-default-version
修改為8.0.20
;
- 注意:為了與 MySQL JDBC 的驅動包
-
啟動:
-
然后就可以使用 MySQL 的客戶端(比如 DataGrip)去連接 ShardingSphere-Proxy 了,且可以像用 MySQL 一樣去使用 ShardingSphere-Proxy:
mysql> show databases; +--------------------+ | schema_name | +--------------------+ | shardingsphere | | information_schema | | performance_schema | | mysql | | sys | +--------------------+ 5 rows in set (0.01 sec)mysql> use shardingsphere Database changed mysql> show tables; +---------------------------+------------+ | Tables_in_shardingsphere | Table_type | +---------------------------+------------+ | sharding_table_statistics | BASE TABLE | +---------------------------+------------+ 1 row in set (0.01 sec)mysql> select * from sharding_table_statistics; Empty set (1.25 sec)
-
不過要注意,此時 ShardingSphere-Proxy 只是一個虛擬庫,所以并不能像 MySQL 一樣去隨意建表或修改數據,比如下面就建表失敗:
mysql> CREATE TABLE test (id varchar(255) NOT NULL); Query OK, 0 rows affected (0.00 sec)mysql> select * from test; 30000 - Unknown exception: At line 0, column 0: Object 'test' not found mysql> show tables; +---------------------------+------------+ | Tables_in_shardingsphere | Table_type | +---------------------------+------------+ | sharding_table_statistics | BASE TABLE | +---------------------------+------------+ 1 row in set (0.01 sec)
2.2 常用的分庫分表配置策略
-
打開
config-sharding.yaml
,配置邏輯表course
:# 邏輯數據庫名稱(客戶端連接時使用的虛擬數據庫名) databaseName: sharding_db# 數據源配置(定義實際的后端數據庫連接) dataSources:# 第一個數據源,命名為m0m0:# MySQL數據庫連接URL,指向192.168.65.212服務器的3306端口,數據庫名為shardingdb1url: jdbc:mysql://192.168.65.212:3306/shardingdb1?serverTimezone=UTC&useSSL=falseusername: rootpassword: root# 連接超時時間(毫秒)connectionTimeoutMilliseconds: 30000# 連接空閑超時時間(毫秒)idleTimeoutMilliseconds: 60000# 連接最大生命周期(毫秒)maxLifetimeMilliseconds: 1800000# 連接池最大大小maxPoolSize: 50# 連接池最小大小minPoolSize: 1# 第二個數據源,命名為m1m1:url: jdbc:mysql://192.168.65.212:3306/shardingdb2?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1# 分片規則配置 rules: - !SHARDING # 分片規則標識tables:# 配置course表的分片規則course:# 實際數據節點:分布在m0和m1數據庫的course_1和course_2表中actualDataNodes: m${0..1}.course_${1..2}# 數據庫分片策略databaseStrategy:standard: # 標準分片策略shardingColumn: cid # 分片列(根據此字段的值決定數據路由到哪個數據庫)shardingAlgorithmName: course_db_alg # 使用的分片算法名稱# 表分片策略tableStrategy:standard: # 標準分片策略shardingColumn: cid # 分片列(根據此字段的值決定數據路由到哪個表)shardingAlgorithmName: course_tbl_alg # 使用的分片算法名稱# 主鍵生成策略keyGenerateStrategy:column: cid # 需要生成主鍵的列名keyGeneratorName: alg_snowflake # 使用的主鍵生成器名稱# 分片算法定義shardingAlgorithms:# 數據庫分片算法:取模算法course_db_alg:type: MOD # 取模分片算法props:sharding-count: 2 # 分片數量(2個數據庫)# 表分片算法:行表達式算法course_tbl_alg:type: INLINE # 行表達式分片算法props:# 算法表達式:根據cid字段的值取模2再加1,得到表后綴(1或2)algorithm-expression: course_${cid%2+1}# 主鍵生成器定義keyGenerators:# 雪花算法生成器,用于生成分布式唯一IDalg_snowflake:type: SNOWFLAKE # 使用雪花算法
-
重新啟動 ShardingSphere-Proxy 服務:
mysql> show databases; +--------------------+ | schema_name | +--------------------+ | information_schema | | performance_schema | | sys | | shardingsphere | | sharding_db | | mysql | +--------------------+ 6 rows in set (0.02 sec)mysql> use sharding_db; Database changed mysql> show tables; +-----------------------+------------+ | Tables_in_sharding_db | Table_type | +-----------------------+------------+ | course | BASE TABLE | | user_2 | BASE TABLE | | user | BASE TABLE | | user_1 | BASE TABLE | +-----------------------+------------+ 4 rows in set (0.02 sec)mysql> select * from course; +---------------------+-------+---------+---------+ | cid | cname | user_id | cstatus | +---------------------+-------+---------+---------+ | 1017125767709982720 | java | 1001 | 1 | | 1017125769383510016 | java | 1001 | 1 |mysql> select * from course_1; +---------------------+-------+---------+---------+ | cid | cname | user_id | cstatus | +---------------------+-------+---------+---------+ | 1017125767709982720 | java | 1001 | 1 | | 1017125769383510016 | java | 1001 | 1 |
- 可以看到多了一個
sharding_db
庫。
- 可以看到多了一個
3 ShardingSphere-Proxy 中的分布式事務機制
3.1 為什么需要分布式事務?
-
XA 事務 :: ShardingSphere;
-
在
2.1 部署 ShardingSphere-Proxy
中配置server.yaml
時,有一個TRANSACTION
配置項:rules: - !TRANSACTIONdefaultType: XAproviderType: Atomikos
-
為什么 ShardingSphere-Proxy 需要分布式事務?
-
ShardingSphere(尤其是 ShardingSphere-Proxy)要操作分布式的數據庫集群(多個庫、多個節點)。而數據庫的本地事務只能保證單個數據庫內的事務安全,無法覆蓋跨多個數據庫的場景;
-
因此,必須引入分布式事務管理機制,才能保證 ShardingSphere-Proxy 中 SQL 執行的原子性。開啟分布式事務后,開發者不用再手動考慮跨庫 SQL 的事務問題。
-
3.2 什么是 XA 事務?
-
XA 是由 X/Open Group 組織定義的分布式事務標準,主流關系型數據庫(如 MySQL、Oracle 等)都實現了 XA 協議(比如 MySQL 5.0.3+ 的 InnoDB 引擎支持 XA);
-
XA 事務的核心是兩階段提交(2PC),通過**準備(Prepare)和提交(Commit)**兩個階段,保證跨多個數據庫的事務一致性;
-
XA 分布式事務操作示例:
-- 1. 開啟XA事務,'test'是事務標識符(XID),將事務狀態設置為ACTIVE(活躍) -- 事務標識符用于在分布式環境中唯一標識一個事務 XA START 'test';-- 2. 在ACTIVE狀態的XA事務中執行業務SQL語句 -- 這些操作是事務的一部分,但尚未最終提交 INSERT INTO dict VALUES(1, 't', 'test');-- 3. 結束XA事務,將事務狀態從ACTIVE改為IDLE(空閑) -- 表示事務內的所有操作已完成,準備進入準備階段 XA END 'test';-- 4. 準備提交事務,將事務狀態從IDLE改為PREPARED(已準備) -- 此階段事務管理器會確保所有資源管理器都已準備好提交 XA PREPARE 'test';-- 5. 列出所有處于PREPARED狀態的XA事務 -- 返回的信息包含:gtrid(全局事務ID)、bqual(分支限定符)、formatID(格式ID)和data(數據) XA RECOVER;-- 6. 提交已準備的XA事務,使所有更改永久生效 -- 這是兩階段提交的第二階段(提交階段) XA COMMIT 'test';-- 6. 替代方案:回滾已準備的XA事務,撤銷所有更改 -- 在PREPARED狀態下也可以選擇回滾而不是提交 XA ROLLBACK 'test';-- 注意:也可以使用單條命令直接提交,將預備和提交合并操作 -- XA COMMIT 'test' ONE PHASE;
-
ShardingSphere 集成了多個 XA 實現框架(Atomikos、Bitronix、Narayana)。在 ShardingSphere-Proxy 中,默認只集成了 Atomikos(所以配置里
providerType: Atomikos
)。
3.3 實戰理解 XA 事務
-
引入 Maven 依賴:
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-transaction-xa-core</artifactId><version>5.2.1</version><exclusions><exclusion><artifactId>transactions-jdbc</artifactId><groupId>com.atomikos</groupId></exclusion><exclusion><artifactId>transactions-jta</artifactId><groupId>com.atomikos</groupId></exclusion></exclusions> </dependency> <!-- 版本滯后了 --> <dependency><artifactId>transactions-jdbc</artifactId><groupId>com.atomikos</groupId><version>5.0.8</version> </dependency> <dependency><artifactId>transactions-jta</artifactId><groupId>com.atomikos</groupId><version>5.0.8</version> </dependency> <!-- 使用XA事務時,可以引入其他幾種事務管理器 --> <!-- <dependency>--> <!-- <groupId>org.apache.shardingsphere</groupId>--> <!-- <artifactId>shardingsphere-transaction-xa-bitronix</artifactId>--> <!-- <version>5.2.1</version>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>org.apache.shardingsphere</groupId>--> <!-- <artifactId>shardingsphere-transaction-xa-narayana</artifactId>--> <!-- <version>5.2.1</version>--> <!-- </dependency>-->
-
配置事務管理器:
@Configuration @EnableTransactionManagement public class TransactionConfiguration {@Beanpublic PlatformTransactionManager txManager(final DataSource dataSource) {return new DataSourceTransactionManager(dataSource);} }
-
測試案例:
public class MySQLXAConnectionTest {public static void main(String[] args) throws SQLException {// 是否打印XA相關命令,用于調試目的boolean logXaCommands = true;// ============== 初始化階段:建立數據庫連接 ==============// 獲取第一個數據庫連接(資源管理器RM1)Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/coursedb?serverTimezone=UTC", "root", "root");// 創建XA連接包裝器,用于支持分布式事務XAConnection xaConn1 = new MysqlXAConnection((com.mysql.cj.jdbc.JdbcConnection) conn1, logXaCommands);// 獲取XA資源接口,用于控制分布式事務XAResource rm1 = xaConn1.getXAResource();// 獲取第二個數據庫連接(資源管理器RM2)Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/coursedb2?serverTimezone=UTC", "root", "root");// 創建第二個XA連接包裝器XAConnection xaConn2 = new MysqlXAConnection((com.mysql.cj.jdbc.JdbcConnection) conn2, logXaCommands);// 獲取第二個XA資源接口XAResource rm2 = xaConn2.getXAResource();// 生成全局事務ID(GTrid,是全局事務標識符,在整個事務生命周期中唯一)byte[] gtrid = "g12345".getBytes();int formatId = 1; // 格式標識符,通常為1,表示標準XID格式try {// ============== 分別執行RM1和RM2上的事務分支 ====================// 為第一個資源管理器生成分支事務ID(BQual)byte[] bqual1 = "b00001".getBytes();// 創建完整的事務ID(XID),包含全局ID和分支IDXid xid1 = new MysqlXid(gtrid, bqual1, formatId);// 開始第一個分支事務,TMNOFLAGS表示新建一個事務(不是加入或恢復現有事務)rm1.start(xid1, XAResource.TMNOFLAGS);// 在第一個分支事務中執行SQL操作PreparedStatement ps1 = conn1.prepareStatement("INSERT INTO `dict` VALUES (1, 'T', '測試1');");ps1.execute();// 結束第一個分支事務,TMSUCCESS表示事務執行成功rm1.end(xid1, XAResource.TMSUCCESS);// 為第二個資源管理器生成分支事務IDbyte[] bqual2 = "b00002".getBytes();// 創建第二個事務IDXid xid2 = new MysqlXid(gtrid, bqual2, formatId);// 開始第二個分支事務rm2.start(xid2, XAResource.TMNOFLAGS);// 在第二個分支事務中執行SQL操作PreparedStatement ps2 = conn2.prepareStatement("INSERT INTO `dict` VALUES (2, 'F', '測試2');");ps2.execute();// 結束第二個分支事務rm2.end(xid2, XAResource.TMSUCCESS);// =================== 兩階段提交流程 ============================// 第一階段:準備階段 - 詢問所有資源管理器是否準備好提交事務int rm1_prepare = rm1.prepare(xid1);int rm2_prepare = rm2.prepare(xid2);// 第二階段:提交階段 - 根據準備階段的結果決定提交或回滾boolean onePhase = false; // 設置為false表示使用標準的兩階段提交// 檢查所有資源管理器是否都準備成功if (rm1_prepare == XAResource.XA_OK&& rm2_prepare == XAResource.XA_OK) {// 所有分支都準備成功,提交所有事務rm1.commit(xid1, onePhase);rm2.commit(xid2, onePhase);} else {// 如果有任何一個分支準備失敗,回滾所有事務rm1.rollback(xid1);rm2.rollback(xid2);}} catch (XAException e) {// 處理XA異常,打印異常信息e.printStackTrace();}} }
-
XA標準規范了事務XID的格式,它由三個部分組成:
gtrid [, bqual [, formatID ]]
,具體含義如下:-
gtrid(全局事務標識符):
- global transaction identifier,是用于標識一個全局事務的唯一值 。在分布式事務場景下,多個數據庫節點參與同一個事務,通過這個全局事務標識符可以將這些操作關聯到一起,明確它們屬于同一個事務;
- 比如在一個涉及電商系統多個數據庫(訂單庫、庫存庫、支付庫)的分布式事務中,通過相同的gtrid就能確認這些數據庫上的操作都屬于同一次購物流程對應的事務;
-
bqual(分支限定符):
- branch qualifier ,用來進一步限定事務分支。如果沒有提供,會使用默認值即一個空字符串;
- 例如,在一個分布式事務中有多個數據庫節點,每個節點上的操作可以看作事務的一個分支,bqual 就可以用來區分和標識這些不同的分支,以便更精確地管理事務在各個節點上的執行情況;
-
formatID(格式標識符):
- 是一個數字,用于標記gtrid和bqual值的格式,是一個正整數,最小為0,默認值是1 ;
- 它主要用于在不同的系統或數據庫之間進行交互時,明確全局事務標識符和分支限定符的格式規則,以確保各方能夠正確解析和處理事務標識信息。
-
-
使用XA事務時的注意事項
-
無法自動提交:與本地事務在滿足一定條件下可以自動提交不同,XA事務需要開發者顯式地調用提交指令(如
XA COMMIT
) 。這就要求開發者在編寫代碼時,要準確把控事務提交的時機,避免因疏忽未提交事務而導致數據不一致等問題。例如在編寫一個涉及多個數據庫操作的業務邏輯時,開發者需要在所有操作都成功執行后,手動調用提交語句來完成事務,否則事務會一直處于未提交狀態; -
效率低下:XA事務執行效率遠低于本地事務,通常耗時能達到本地事務的10倍。這是因為在XA事務中,全局事務的狀態都需要持久化,涉及到多個數據庫節點之間的協調和通信,比如在兩階段提交過程中,準備階段各個節點要記錄事務狀態等信息,提交階段又需要進行多次確認和交互,這些額外的操作增加了系統開銷,降低了事務處理的速度。這就意味著在對性能要求極高的場景中,使用XA事務可能并不合適,需要謹慎評估;
-
故障隔離困難:在XA事務提交前如果出現故障(如某個數據庫節點宕機、網絡中斷等),很難將問題隔離開 。由于XA事務涉及多個數據庫節點的協同工作,一個節點出現故障可能會影響到整個事務的執行,而且故障排查和恢復相對復雜。例如在一個跨地域的分布式數據庫系統中,如果某個地區的數據庫節點在XA事務準備階段出現故障,不僅要處理該節點自身的數據恢復問題,還要協調其他節點來處理事務狀態,以保證整個系統的數據一致性。
-
3.4 如何在 ShardingSphere-Proxy 中使用其他事務管理器?
-
ShardingSphere-Proxy 支持多種分布式事務管理器(如 Atomikos、Narayana、Bitronix)。默認集成的是 Atomikos,若要改用其它兩種事務管理器,需手動配置;
-
具體操作(以 Narayana 為例)
-
添加 Narayana 的依賴包:
- 將 Narayana 事務集成的 Jar 包(如
shardingsphere-transaction-xa-narayana-5.2.1.jar
),放到 ShardingSphere-Proxy 的lib
目錄下; - 該 Jar 包可通過 Maven 依賴下載(需要在項目的 Maven 配置中引入對應依賴,構建后獲取 Jar 包);
- 將 Narayana 事務集成的 Jar 包(如
-
在
server.yaml
的rules
部分,將事務的providerType
配置為Narayana
。配置示例:rules:- !TRANSACTIONdefaultType: XAproviderType: Narayana
-
-
注意事項:ShardingSphere 版本更新快,部分依賴可能未及時維護,存在組件版本沖突的風險。使用前需確認依賴包與 ShardingSphere-Proxy 版本的兼容性。
4 ShardingSphere-Proxy 集群化部署
4.1 ShardingSphere-Proxy 的運行模式
-
在
server.yaml
文件中,有一段被注釋的mode
配置。這段配置的作用是指定 ShardingSphere 的運行模式,決定 ShardingSphere 如何管理復雜的配置信息:#mode: # type: Cluster # repository: # type: ZooKeeper # props: # namespace: governance_ds # server-lists: localhost:2181 # retryIntervalMilliseconds: 500 # timeToLiveSeconds: 60 # maxRetries: 3 # operationTimeoutMilliseconds: 500
-
ShardingSphere 支持 Standalone(獨立模式) 和 Cluster(集群模式) 兩種運行模式,核心差異如下:
-
Standalone(獨立模式)
-
ShardingSphere 不需要考慮其他實例的影響,直接在內存中管理核心配置規則;
-
是 ShardingSphere 默認的運行模式,配置簡單,無需依賴第三方組件。
-
適用場景:小型項目,或對性能要求較高的場景(因為沒有額外的配置同步開銷)。通常配合 ShardingJDBC 使用(ShardingJDBC 是客戶端分庫分表,更側重性能);
-
-
Cluster(集群模式)
-
ShardingSphere 不僅要管理自身配置,還要與集群中其他實例同步配置規則。因此需要引入第三方配置中心(如 Zookeeper、etcd、Nacos 等)來實現配置同步;
-
推薦的配置中心:在分庫分表場景下,配置信息變動少、訪問頻率低,因此基于 CP 架構的 Zookeeper 最推薦(CP 架構保證數據一致性,適合配置這類強一致需求的場景);
-
優先級:如果應用本地和 Zookeeper 都有配置,ShardingSphere 以 Zookeeper 中的配置為準(保證集群配置統一);
-
適用場景:大規模集群環境,需要多實例協同工作,通常配合 ShardingSphere-Proxy 使用;
-
-
-
模式選擇建議
-
Standalone:小型項目、性能敏感場景 → 配合 ShardingJDBC;
-
Cluster:大規模集群環境 → 配合 ShardingSphere-Proxy,且推薦用 Zookeeper 作為配置中心。
-
4.2 使用 Zookeeper 進行集群部署
-
接下來基于 Zookeeper 部署一下 ShardingSphere-Proxy 集群;
-
首先在本地部署一個 Zookeeper,然后將
server.yaml
中的mode
部分解除注釋:# ShardingSphere 運行模式配置 mode:# 運行模式類型:Cluster 表示集群模式(支持多個Proxy實例組成集群)type: Cluster# 集群元數據存儲倉庫配置repository:# 倉庫類型:ZooKeeper(使用ZooKeeper作為分布式協調服務)type: ZooKeeper# ZooKeeper連接配置屬性props:# ZooKeeper命名空間:用于在ZooKeeper中隔離不同環境的配置namespace: governance_ds# ZooKeeper服務器地址列表:支持多個服務器地址,用逗號分隔server-lists: 192.168.65.212:2181# 重試間隔時間(毫秒):連接失敗后重試的等待時間retryIntervalMilliseconds: 500# 節點存活時間(秒):在ZooKeeper中創建的臨時節點的存活時間timeToLiveSeconds: 60# 最大重試次數:連接ZooKeeper失敗時的最大重試次數maxRetries: 3# 操作超時時間(毫秒):ZooKeeper操作(如創建、讀取節點)的超時時間operationTimeoutMilliseconds: 500
-
啟動 ShardingSphere-Proxy 服務,在 Zookeeper 注冊中心可以看到 ShardingSphere 集群模式下的節點結構如下:
# ShardingSphere 集群模式下的 ZooKeeper 節點結構詳解# 根命名空間(用于環境隔離,避免不同環境配置相互干擾) namespace: governance_ds # 全局配置節點 ├── rules # 存儲全局規則配置(如分片規則、加密規則等) ├── props # 存儲全局屬性配置(如SQL顯示開關、執行模式等) # 元數據管理節點 ├── metadata # 元數據配置根目錄 │ ├── ${databaseName} # 邏輯數據庫名稱(如:sharding_db) │ │ ├── schemas # 邏輯Schema列表 │ │ │ ├── ${schemaName} # 邏輯Schema名稱 │ │ │ │ ├── tables # 表結構配置 │ │ │ │ │ ├── ${tableName} # 具體表的結構定義 │ │ │ │ │ └── ... # 其他表 │ │ │ │ └── views # 視圖結構配置 │ │ │ │ ├── ${viewName} # 具體視圖的定義 │ │ │ │ └── ... # 其他視圖 │ │ │ └── ... # 其他Schema │ │ └── versions # 元數據版本管理 │ │ ├── ${versionNumber} # 具體版本號(如:v1, v2) │ │ │ ├── dataSources # 該版本的數據源配置 │ │ │ └── rules # 該版本的規則配置 │ │ ├── active_version # 當前激活的元數據版本號 │ │ └── ... # 其他版本 # 計算節點管理(Proxy和JDBC實例管理) ├── nodes │ ├── compute_nodes # 計算節點管理根目錄 │ │ ├── online # 在線實例列表 │ │ │ ├── proxy # Proxy模式實例 │ │ │ │ ├── UUID # 每個Proxy實例的唯一標識(如:生成的實際UUID) │ │ │ │ └── ... # 其他Proxy實例 │ │ │ └── jdbc # JDBC模式實例 │ │ │ ├── UUID # 每個JDBC實例的唯一標識 │ │ │ └── ... # 其他JDBC實例 │ │ ├── status # 實例狀態信息 │ │ │ ├── UUID # 具體實例的狀態數據 │ │ │ └── ... # 其他實例狀態 │ │ ├── worker_id # 工作節點ID分配 │ │ │ ├── UUID # 實例分配的工作ID │ │ │ └── ... # 其他實例的工作ID │ │ ├── process_trigger # 進程觸發管理 │ │ │ ├── process_list_id:UUID # 進程列表與實例的關聯 │ │ │ └── ... # 其他進程觸發信息 │ │ └── labels # 實例標簽管理 │ │ ├── UUID # 具體實例的標簽配置 │ │ └── ... # 其他實例標簽 # 存儲節點管理(實際數據源管理) │ └── storage_nodes # 存儲節點管理根目錄 │ ├── ${databaseName.groupName.ds} # 數據源節點命名格式:邏輯庫名.組名.數據源名 │ └── ${databaseName.groupName.ds} # 另一個數據源節點
-
server.yaml
中的rules
部分同2.1 部署 ShardingSphere-Proxy
; -
分庫分表配置同
2.2 常用的分庫分表配置策略
。
4.3 統一 JDBC 和 Proxy 配置信息
- ShardingSphere-JDBC 也能像 ShardingSphere-Proxy 一樣,通過 Zookeeper 同步配置信息,從而實現兩者配置的統一管理(減少配置分散,便于集群化維護)。
4.3.1 通過注冊中心同步配置
-
在
application.properties
中,刪除之前 ShardingSphere-JDBC 相關的分庫分表規則等配置,只配置Zookeeper 的連接和集群模式。示例配置:# 指定運行模式為“集群模式(Cluster)” spring.shardingsphere.mode.type=Cluster # 指定配置中心類型為 Zookeeper spring.shardingsphere.mode.repository.type=Zookeeper # Zookeeper 的命名空間(用于隔離不同配置,自定義即可) spring.shardingsphere.mode.repository.props.namespace=governance_ds # Zookeeper 的地址和端口 spring.shardingsphere.mode.repository.props.server-lists=localhost:2181 # 重試間隔(毫秒) spring.shardingsphere.mode.repository.props.retryIntervalMilliseconds=600 # 存活時間(秒) spring.shardingsphere.mode.repository.props.timeToLiveSeconds=60 # 最大重試次數 spring.shardingsphere.mode.repository.props.maxRetries=3 # 操作超時時間(毫秒) spring.shardingsphere.mode.repository.props.operationTimeoutMilliseconds=500
-
配置完成后,可繼續驗證對表(如
course
表)的分庫分表操作,此時 ShardingSphere-JDBC 會從 Zookeeper 中拉取配置信息來執行分庫分表邏輯; -
注意:
- 如果在 ShardingSphere-JDBC 中讀取配置中心(Zookeeper)的配置,需要用
spring.shardingsphere.database.name
指定對應的虛擬庫; - 若不配置該參數,默認值為
logic_db
; - 這個虛擬庫是 ShardingSphere 中邏輯上的數據庫概念,用于統一管理分庫分表后的邏輯結構。
- 如果在 ShardingSphere-JDBC 中讀取配置中心(Zookeeper)的配置,需要用
4.3.2 使用ShardingSphere-Proxy提供的JDBC驅動讀取配置文件
-
ShardingSphere 過去是通過兼容 MySQL 或 PostgreSQL 服務的方式,提供分庫分表功能:
-
應用端需要用 MySQL/PostgreSQL 的 JDBC 驅動,去訪問 ShardingSphere 封裝的數據源(
ShardingSphereDataSource
); -
這種方式相當于模擬成 MySQL/PostgreSQL 數據庫,讓應用無感知地使用分庫分表能力,但依賴的是第三方數據庫的 JDBC 驅動;
-
-
在當前版本中,ShardingSphere 直接提供了自己的 JDBC 驅動:
-
這意味著應用可以不再依賴 MySQL/PostgreSQL 的 JDBC 驅動,直接用 ShardingSphere 專屬的驅動來連接和操作數據;
-
優勢是更原生、更直接地支持 ShardingSphere 的特性(分庫分表、分布式事務等),減少對第三方驅動的依賴,也能更靈活地擴展功能;
-
-
比如在 ShardingSphere-JDBC 中,可在類路徑(classpath)下增加
config.yaml
文件,將之前 ShardingSphere-Proxy中的關鍵配置整合到這個文件中。這樣 ShardingSphere-JDBC 就能通過自身的 JDBC 驅動,讀取config.yaml
里的配置(分庫分表規則、數據源等),實現和 ShardingSphere-Proxy 類似的分庫分表邏輯:rules:- !AUTHORITYusers:- root@%:root- sharding@:shardingprovider:type: ALL_PERMITTED- !TRANSACTIONdefaultType: XAproviderType: Atomikos- !SQL_PARSERsqlCommentParseEnabled: truesqlStatementCache:initialCapacity: 2000maximumSize: 65535parseTreeCache:initialCapacity: 128maximumSize: 1024- !SHARDINGtables:course:actualDataNodes: m${0..1}.course_${1..2}databaseStrategy:standard:shardingColumn: cidshardingAlgorithmName: course_db_algtableStrategy:standard:shardingColumn: cidshardingAlgorithmName: course_tbl_algkeyGenerateStrategy:column: cidkeyGeneratorName: alg_snowflakeshardingAlgorithms:course_db_alg:type: MODprops:sharding-count: 2course_tbl_alg:type: INLINEprops:algorithm-expression: course_$->{cid%2+1}keyGenerators:alg_snowflake:type: SNOWFLAKEprops:max-connections-size-per-query: 1kernel-executor-size: 16proxy-frontend-flush-threshold: 128proxy-hint-enabled: falsesql-show: falsecheck-table-metadata-enabled: falseproxy-backend-query-fetch-size: -1proxy-frontend-executor-size: 0proxy-backend-executor-suitable: OLAPproxy-frontend-max-connections: 0 sql-federation-type: NONEproxy-backend-driver-type: JDBCproxy-mysql-default-version: 8.0.20proxy-default-port: 3307proxy-netty-backlog: 1024databaseName: sharding_db dataSources:m0:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://127.0.0.1:3306/coursedb?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1m1:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://127.0.0.1:3306/coursedb2?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1
-
然后,可以直接用 JDBC 的方式訪問帶有分庫分表的虛擬庫:
public class ShardingJDBCDriverTest {@Testpublic void test() throws ClassNotFoundException, SQLException {// 定義ShardingSphere JDBC驅動類全限定名// 這是ShardingSphere提供的專用JDBC驅動,用于攔截和路由SQL請求String jdbcDriver = "org.apache.shardingsphere.driver.ShardingSphereDriver";// JDBC連接URL,指定使用ShardingSphere驅動和配置文件路徑// classpath:config.yaml 表示配置文件位于類路徑下的config.yaml文件String jdbcUrl = "jdbc:shardingsphere:classpath:config.yaml";// 要執行的SQL查詢語句// 這里查詢的是邏輯數據庫sharding_db中的course表// ShardingSphere會根據配置將查詢路由到實際的物理表String sql = "select * from sharding_db.course";// 加載ShardingSphere JDBC驅動類// 這是使用JDBC驅動的標準方式,驅動會自動注冊到DriverManagerClass.forName(jdbcDriver);// 使用try-with-resources語句確保連接正確關閉try(Connection connection = DriverManager.getConnection(jdbcUrl);) {// 創建Statement對象用于執行SQL語句Statement statement = connection.createStatement();// 執行SQL查詢并返回ResultSet結果集// ShardingSphere會在此處攔截SQL,根據分片規則路由到正確的數據節點ResultSet resultSet = statement.executeQuery(sql);// 遍歷結果集,處理查詢結果while (resultSet.next()){// 從結果集中獲取cid字段的值并輸出// ShardingSphere會自動合并來自多個分片的結果System.out.println("course cid= "+resultSet.getLong("cid"));}// try-with-resources會自動關閉statement和resultSet}// try-with-resources會自動關閉connection,釋放數據庫連接資源} }
-
官方的說明是 ShardingSphereDriver 讀取
config.yaml
時, 這個config.yaml
配置信息與 ShardingSphere-Proxy 中的配置文件完全是相同的,甚至可以直接將 ShardingSphere-Proxy 中的配置文件拿過來用。但是從目前版本來看,還是有不少小問題的,靜待后續版本跟蹤吧; -
到這里,對于之前講解過的 ShardingSphere 的混合架構,有沒有更新的了解?
5 ShardingSphere-Proxy功能擴展
-
ShardingSphere-Proxy 支持通過 SPI(Service Provider Interface)機制 進行自定義擴展,只要將自定義的擴展功能(如自定義算法、策略等)按照 SPI 規范打成 Jar 包,放入 ShardingSphere-Proxy 的
lib
目錄,就能被 ShardingSphere-Proxy 加載并使用; -
以自定義主鍵生成器為例:
- 實現
KeyGeneratorAlgorithm
接口,用于生成自定義格式的主鍵; - 生成主鍵時,結合當前時間戳(格式化為
yyyyMMddHHmmssSSS
)和自增原子變量,保證主鍵的唯一性和有序性;
public class MyKeyGeneratorAlgorithm implements KeyGeneratorAlgorithm {private AtomicLong atom = new AtomicLong(0);private Properties props;@Overridepublic Comparable<?> generateKey() {LocalDateTime ldt = LocalDateTime.now();// 格式化時間為 “年月日時分秒毫秒” 格式String timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(ldt);// 結合自增數生成主鍵(時間戳 + 自增數)return Long.parseLong(timestamp + atom.incrementAndGet());}@Overridepublic Properties getProps() {return this.props;}@Overridepublic String getType() {// 自定義算法的類型標識,配置時會用到return "MYKEY";}@Overridepublic void init(Properties props) {this.props = props;} }
- 實現
-
要將自定義類和對應的 SPI 文件打成 Jar 包,需在
pom.xml
中配置maven-jar-plugin
:- 指定要打包的自定義類(
com/your/package/algorithm/**
)和 SPI 配置文件(META-INF/services/*
),生成包含擴展功能的 Jar 包;
<build><plugins><!-- 將 SPI 擴展功能單獨打成 Jar 包 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><executions><execution><id>shardingJDBDemo</id><phase>package</phase><goals><goal>jar</goal></goals><configuration><classifier>spi-extention</classifier><includes><!-- 包含自定義算法的包路徑 --><include>com/your/package/algorithm/**</include><!-- 包含 SPI 配置文件(META-INF/services/ 下的文件) --><include>META-INF/services/*</include></includes></configuration></execution></executions></plugin></plugins> </build>
- 指定要打包的自定義類(
-
將打包好的 Jar 包,放入 ShardingProxy 的
lib
目錄。之后在 ShardingProxy 的配置中,就可以像使用內置組件一樣,引用這個自定義的主鍵生成算法(如配置keyGeneratorName: MYKEY
)。
6 分庫分表數據遷移方案
-
分庫分表場景下,數據遷移面臨兩大難題:
-
海量數據遷移難度大:舊項目無分庫分表,或需調整分片規則時,數據量龐大,遷移工程浩大;
-
業務無感知難度高:遷移過程中要保證業務正常運行,不能因遷移導致服務中斷;
-
-
為平衡數據遷移和業務連續性(在數據轉移的過程中,保證不影響業務正常進行),采用冷熱數據分離遷移:
-
冷數據:
- 即歷史存量數據(數據量大,無法一次性遷移);
- 策略:通過定時任務逐步遷移,減少對業務的瞬間壓力;
-
熱數據:
- 即業務實時產生的新數據;
- 策略:保證雙寫(同時寫入舊庫和新庫),確保遷移過程中數據實時同步,業務不受影響;
-
-
基于 ShardingSphere 的遷移方案
-
熱數據雙寫(ShardingJDBC 數據雙寫庫)
-
核心邏輯:用
ShardingSphereDataSource
替換舊的DataSource
,配置雙寫規則(核心業務表同時寫入舊庫和新庫); -
實現方式:在 ShardingJDBC 雙寫庫中,針對需遷移的核心表配置分片規則,通過定制分片算法實現一份數據寫兩份庫;
-
-
新庫分片寫入(ShardingJDBC 數據寫入庫)
-
核心邏輯:針對新數據庫集群,配置專門的 ShardingJDBC 寫入庫,完整實現新的分片規則(即分庫分表的最終邏輯);
-
關聯方式:將這個寫入庫配置到之前的雙寫庫中,保證新數據按新規則寫入新庫;
-
-
冷數據增量遷移(定時任務 + ShardingProxy)
-
冷數據特點:量大、遷移慢,需分批增量遷移;
-
實現方式:
- 用
ElasticJob
等定時任務框架,分批讀取舊庫冷數據,按新分片規則寫入新庫; - 借助
ShardingProxy
服務,簡化新庫的分片邏輯(定時任務只需從舊庫讀、往新庫寫,分片規則由 ShardingProxy 統一管理); - 規則同步:通過
ShardingSphere Governance Center
(如 Zookeeper),保證 ShardingProxy 和 ShardingJDBC 寫入庫的分片規則一致;
- 用
-
-
遷移完成后切換
-
核心邏輯:冷數據遷移完成后,刪除雙寫庫和舊庫依賴,只保留 ShardingJDBC 寫入庫(新庫的分片邏輯);
-
業務影響:應用層只需訪問一個
DataSource
(底層由 ShardingSphere 切換為新庫邏輯),業務代碼幾乎無需修改;
-
-
-
注意:遷移過程中,需梳理業務 SQL,確保 SQL 符合 ShardingSphere(尤其是 ShardingSphere-JDBC)的支持范圍,避免使用不兼容的 SQL 語法導致遷移或業務異常。