一、簡介
官網:https://shardingsphere.apache.org/index_zh.html
文檔:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/overview/
ShardingSphere-JDBC 是 Apache ShardingSphere 的第一個產品,也是 Apache ShardingSphere 的前身。 定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。 它使用客戶端直連數據庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。
- 適用於任何基於 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
- 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
- 支持任意實現 JDBC 規范的數據庫,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標准的數據庫。
二、對比
ShardingSphere-JDBC | ShardingSphere-Proxy | ShardingSphere-Sidecar | |
---|---|---|---|
數據庫 | 任意 |
MySQL/PostgreSQL | MySQL/PostgreSQL |
連接消耗數 | 高 |
低 | 高 |
異構語言 | 僅Java |
任意 | 任意 |
性能 | 損耗低 |
損耗略高 | 損耗低 |
無中心化 | 是 |
否 | 是 |
靜態入口 | 無 |
有 | 無 |
ShardingSphere-JDBC 的優勢在於對 Java 應用的友好度。
三、核心概念
邏輯表
水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:訂單數據根據主鍵尾數拆分為 10 張表,分別是 t_order_0
到 t_order_9
,他們的邏輯表名為 t_order
。
真實表
在分片的數據庫中真實存在的物理表。即上個示例中的 t_order_0
到 t_order_9
。
數據節點
數據分片的最小單元。由數據源名稱和數據表組成,例:ds_0.t_order_0
。
綁定表
指分片規則一致的主表和子表。例如:t_order
表和 t_order_item
表,均按照 order_id
分片,則此兩張表互為綁定表關系。綁定表之間的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。舉例說明,如果 SQL 為:
SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
在不配置綁定表關系時,假設分片鍵 order_id
將數值 10 路由至第 0 片,將數值 11 路由至第 1 片,那么路由后的 SQL 應該為 4 條,它們呈現為笛卡爾積:
SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
在配置綁定表關系后,路由的 SQL 應該為 2 條:
SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
其中 t_order
在 FROM 的最左側,ShardingSphere 將會以它作為整個綁定表的主表。 所有路由計算將會只使用主表的策略,那么 t_order_item
表的分片計算將會使用 t_order
的條件。故綁定表之間的分區鍵要完全相同。
廣播表
指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用於數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表。
單表
指所有的分片數據源中只存在唯一一張的表。適用於數據量不大且不需要做任何分片操作的場景。
分片鍵
用於分片的數據庫字段,是將數據庫(表)水平拆分的關鍵字段。例:將訂單表中的訂單主鍵的尾數取模分片,則訂單主鍵為分片字段。 SQL 中如果無分片字段,將執行全路由,性能較差。 除了對單分片字段的支持,Apache ShardingSphere 也支持根據多個字段進行分片。
分片算法
通過分片算法將數據分片,支持通過 =
、>=
、<=
、>
、<
、BETWEEN
和 IN
分片。 分片算法需要應用方開發者自行實現,可實現的靈活度非常高。
目前提供 3 種分片算法。 由於分片算法和業務實現緊密相關,因此並未提供內置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,並提供接口讓應用開發者自行實現分片算法。
- 標准分片算法
對應 StandardShardingAlgorithm,用於處理使用單一鍵作為分片鍵的 =
、IN
、BETWEEN AND
、>
、<
、>=
、<=
進行分片的場景。需要配合 StandardShardingStrategy 使用。
- 復合分片算法
對應 ComplexKeysShardingAlgorithm,用於處理使用多鍵作為分片鍵進行分片的場景,包含多個分片鍵的邏輯較復雜,需要應用開發者自行處理其中的復雜度。需要配合 ComplexShardingStrategy 使用。
- Hint分片算法
對應 HintShardingAlgorithm,用於處理使用 Hint
行分片的場景。需要配合 HintShardingStrategy 使用。
分片策略
包含分片鍵和分片算法,由於分片算法的獨立性,將其獨立抽離。真正可用於分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供 4 種分片策略。
- 標准分片策略
對應 StandardShardingStrategy。提供對 SQL 語句中的 =
, >
, <
, >=
, <=
, IN
和 BETWEEN AND
的分片操作支持。 StandardShardingStrategy 只支持單分片鍵,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 兩個分片算法。 PreciseShardingAlgorithm 是必選的,用於處理 =
和 IN
的分片。 RangeShardingAlgorithm 是可選的,用於處理 BETWEEN AND
, >
, <
, >=
, <=
分片,如果不配置 RangeShardingAlgorithm,SQL 中的 BETWEEN AND
將按照全庫路由處理。
- 復合分片策略
對應 ComplexShardingStrategy。復合分片策略。提供對 SQL 語句中的 =
, >
, <
, >=
, <=
, IN
和 BETWEEN AND
的分片操作支持。 ComplexShardingStrategy 支持多分片鍵,由於多分片鍵之間的關系復雜,因此並未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。
- Hint分片策略
對應 HintShardingStrategy。通過 Hint 指定分片值而非從 SQL 中提取分片值的方式進行分片的策略。
- 不分片策略
對應 NoneShardingStrategy。不分片的策略。
SQL Hint
對於分片字段非 SQL 決定,而由其他外置條件決定的場景,可使用 SQL Hint 靈活的注入分片字段。 例:內部系統,按照員工登錄主鍵分庫,而數據庫中並無此字段。SQL Hint 支持通過 Java API 和 SQL 注釋(待實現)兩種方式使用。 詳情請參見強制分片路由。
四、數據分片使用
1、在mysql中,新建2個數據庫 test-shardingsphere1 與 test-shardingsphere2
1 CREATE TABLE IF NOT EXISTS t_order_0 (order_id INT NOT NULL, user_id INT NOT NULL, status VARCHAR(45) NULL, PRIMARY KEY (order_id)); 2 CREATE TABLE IF NOT EXISTS t_order_1 (order_id INT NOT NULL, user_id INT NOT NULL, status VARCHAR(45) NULL, PRIMARY KEY (order_id)); 3 CREATE TABLE IF NOT EXISTS t_order_item_0 (item_id INT NOT NULL, order_id INT NOT NULL, user_id INT NOT NULL, status VARCHAR(45) NULL, PRIMARY KEY (item_id)); 4 CREATE TABLE IF NOT EXISTS t_order_item_1 (item_id INT NOT NULL, order_id INT NOT NULL, user_id INT NOT NULL, status VARCHAR(45) NULL, PRIMARY KEY (item_id));
1 INSERT INTO t_order VALUES(1000, 10, 'init'); 2 INSERT INTO t_order VALUES(1001, 10, 'init'); 3 INSERT INTO t_order VALUES(1100, 11, 'init'); 4 INSERT INTO t_order VALUES(1101, 11, 'init'); 5 INSERT INTO t_order_item VALUES(100000, 1000, 10, 'init'); 6 INSERT INTO t_order_item VALUES(100001, 1000, 10, 'init'); 7 INSERT INTO t_order_item VALUES(100100, 1001, 10, 'init'); 8 INSERT INTO t_order_item VALUES(100101, 1001, 10, 'init'); 9 INSERT INTO t_order_item VALUES(110000, 1100, 11, 'init'); 10 INSERT INTO t_order_item VALUES(110001, 1100, 11, 'init'); 11 INSERT INTO t_order_item VALUES(110100, 1101, 11, 'init'); 12 INSERT INTO t_order_item VALUES(110101, 1101, 11, 'init');
2、新建maven項目,引入依賴
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.test.sharding</groupId> 8 <artifactId>test-sharding-jdbc</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 11 <properties> 12 <maven.compiler.source>8</maven.compiler.source> 13 <maven.compiler.target>8</maven.compiler.target> 14 </properties> 15 16 <dependencies> 17 <!-- shardingsphere-jdbc --> 18 <dependency> 19 <groupId>org.apache.shardingsphere</groupId> 20 <artifactId>shardingsphere-jdbc-core</artifactId> 21 <version>5.0.0-beta</version> 22 </dependency> 23 24 <!-- mysql --> 25 <dependency> 26 <groupId>mysql</groupId> 27 <artifactId>mysql-connector-java</artifactId> 28 <version>8.0.12</version> 29 </dependency> 30 31 32 <!-- 日志 --> 33 <dependency> 34 <groupId>org.slf4j</groupId> 35 <artifactId>slf4j-simple</artifactId> 36 <version>1.7.25</version> 37 <scope>compile</scope> 38 </dependency> 39 </dependencies> 40 41 </project>
3、Java代碼如下:
1 /** 2 * 水平分庫分表 3 */ 4 public class ShardingJDBCTest { 5 6 public static void main(String[] args) throws SQLException { 7 // ShardingJDBCTest.insert(); 8 ShardingJDBCTest.select(); 9 } 10 11 12 13 public static void insert() throws SQLException { 14 // 獲取數據源 15 DataSource dataSource = getDataSource(); 16 17 String insertSql1 = "INSERT INTO t_order VALUES(1000, 10, 'init');"; 18 String insertSql2 = "INSERT INTO t_order VALUES(1001, 10, 'init');"; 19 String insertSql3 = "INSERT INTO t_order VALUES(1100, 11, 'init');"; 20 String insertSql4 = "INSERT INTO t_order VALUES(1101, 11, 'init');"; 21 22 try ( 23 Connection conn = dataSource.getConnection(); 24 PreparedStatement ps = conn.prepareStatement(insertSql3)) { 25 int num = ps.executeUpdate(); 26 System.out.println("num = " + num); 27 } 28 29 } 30 31 public static void select() throws SQLException { 32 // 獲取數據源 33 DataSource dataSource = getDataSource(); 34 35 // String sql = "select * from t_order order"; 36 String sql = "select * from t_order order by user_id desc"; 37 // String sql = "select * from t_order order by order_id desc limit 2"; 38 39 try ( 40 Connection conn = dataSource.getConnection(); 41 PreparedStatement ps = conn.prepareStatement(sql)) { 42 try (ResultSet rs = ps.executeQuery()) { 43 while(rs.next()) { 44 int order_id = rs.getInt(1); 45 int user_id = rs.getInt(2); 46 String status = rs.getString(3); 47 System.out.println(order_id + "\t" + user_id + "\t" + status); 48 } 49 } 50 } 51 } 52 53 public static DataSource getDataSource() throws SQLException { 54 // 配置真實數據源 55 Map<String, DataSource> dataSourceMap = new HashMap<>(); 56 57 // 配置第 1 個數據源 58 HikariDataSource dataSource1 = new HikariDataSource(); 59 dataSource1.setDriverClassName("com.mysql.cj.jdbc.Driver"); 60 dataSource1.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test-shardingjdbc1?allowPublicKeyRetrieval=true&useSSL=true"); 61 dataSource1.setUsername("root"); 62 dataSource1.setPassword("123456"); 63 dataSourceMap.put("ds0", dataSource1); 64 65 // 配置第 2 個數據源 66 HikariDataSource dataSource2 = new HikariDataSource(); 67 dataSource2.setDriverClassName("com.mysql.cj.jdbc.Driver"); 68 dataSource2.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test-shardingjdbc2?allowPublicKeyRetrieval=true&useSSL=true"); 69 dataSource2.setUsername("root"); 70 dataSource2.setPassword("123456"); 71 dataSourceMap.put("ds1", dataSource2); 72 73 // 配置 t_order 表規則 74 ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_order", "ds${0..1}.t_order_${0..1}"); 75 76 // 配置分庫策略 77 orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "dbShardingAlgorithm")); 78 79 // 配置分表策略 80 orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "tableShardingAlgorithm")); 81 82 // 省略配置 t_order_item 表規則... 83 // ... 84 // ShardingTableRuleConfiguration itemTableRuleConfig = new ShardingTableRuleConfiguration("t_order_item", "ds${0..1}.t_order_item_${0..1}"); 85 // itemTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "dbShardingAlgorithm")); 86 // itemTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "tableShardingAlgorithm")); 87 88 89 // 配置分片規則 90 ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); 91 shardingRuleConfig.getTables().add(orderTableRuleConfig); 92 93 // 配置分庫算法 94 Properties dbShardingAlgorithmrProps = new Properties(); 95 dbShardingAlgorithmrProps.setProperty("algorithm-expression", "ds${user_id % 2}"); 96 shardingRuleConfig.getShardingAlgorithms().put("dbShardingAlgorithm", 97 new ShardingSphereAlgorithmConfiguration("INLINE", dbShardingAlgorithmrProps)); 98 99 // 配置分表算法 100 Properties tableShardingAlgorithmrProps = new Properties(); 101 tableShardingAlgorithmrProps.setProperty("algorithm-expression", "t_order${order_id % 2}"); 102 shardingRuleConfig.getShardingAlgorithms().put("tableShardingAlgorithm", 103 new ShardingSphereAlgorithmConfiguration("INLINE", tableShardingAlgorithmrProps)); 104 105 // 創建 ShardingSphereDataSource 106 DataSource dataSource = ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), new Properties()); 107 108 return dataSource; 109 } 110 }
更多使用,請參考官網文檔