Sharding-JDBC從入門到精通(6)-- Sharding-JDBC 水平分庫 和 垂直分庫。
一、Sharding-JDBC 水平分庫-分片策略配置
1、分庫策略定義方式如下
# 分庫策略,如何將一個邏輯表映射到多個數據源
spring.shardingsphere.sharding.tables.<邏輯表名稱>.qatabase-strategy.<分片策略>.<分片策略屬性名>= #分片策略屬性值# 分表策略,如何將一個邏輯表映射為多個實際表
spring.shardingsphere.sharding.tables.<邏輯表名稱>.table-strategy.<分片策略>.<分片策略屬性名>= #分片策略屬性值# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}
2、分片規則修改:
由于數據庫需要拆分了兩個,這里需要配置兩個數據源。
分庫需要配置分庫的策略,和分表策略的意義類似,通過分庫策略實現數據操作針對分庫的數據庫進行操作。
# 配置 sharding-jdbc 分片規則(2024-6-29 分片規則修改)
# 定義數據源(定義 多個 數據源名為 m1, m2)
spring.shardingsphere.datasource.names = m1,m2spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311
3、在 sharding_jdbc_simple 子工程(子模塊)中,修改 application.properties 配置文件,添加配置分庫策略。
# dbsharding\sharding_jdbc_simple\src\main\resources\application.propertiesserver.port = 56081spring.application.name = sharding-jdbc-simple-demoserver.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = truespring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true# 配置 sharding-jdbc 分片規則(2024-6-29 分片規則修改)
# 定義數據源(定義 多個 數據源名為 m1, m2)
spring.shardingsphere.datasource.names = m1,m2spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2)
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}# 指定 t_order 表的主鍵生成策略為 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE# 指定 t_order 表的分片策略,分片策略包括分片鍵和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}# 打開 sql 輸出日志
spring.shardingsphere.props.sql.show = trueswagger.enable = truelogging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug
4、Sharding-JDBC 支持以下幾種分片策略:
不管理分庫還是分表,策略基本一樣。
-
standard: 標準分片策略,對應 Standardshardingstrategy。提供對 SQL 語句中的 =,IN 和 BETWEEN AND 的分片操作支持。StandardShardingStrategy 只支持單分片鍵,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 兩個分片算法。PreciseShardingAlgorithm 是必選的,用于處理 = 和 IN 的分片。RangeShardingAlgorithm 是可選的,用于處理 BETWEEN AND 分片,如果不配置 RangeshardingAlgorithm,SQL 中的 BETWEEN AND 將按照全庫路由處理。
-
complex: 符合分片策略,對應 ComplexshardingStrategy。復合分片策略。提供對 SQL 語句中的 =,IN 和 BETWEEN AND 的分片操作支持。ComplexshardingStrategy 支持多分片鍵,由于多分片鍵之間的關系復雜因此并未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。
-
inline: 行表達式分片策略,對應 InlineshardingStrategy。使用 Groovy 的表達式,提供對 SQL 語句中的 = 和 IN 的分片操作支持,只支持單分片鍵。對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的 Java 代碼開發,如:t_user_$->{u id % 8} 表示 t_user 表根據u_id 模8,而分成8張表,表名稱為 t_user_0 到 t_user_7。
-
hint: Hint 分片策略,對應 HintShardingStrategy。通過 Hint 而非 SQL 解析的方式分片的策略。對于分片字段非 SQL決 定,而由其他外置條件決定的場景,可使用 SQLHint 靈活的注入分片字段。例:內部系統,按照員工登錄主鍵分庫,而數據庫中并無此字段。SQL Hint 支持通過 Java API 和 SQL 注釋(待實現)兩種方式使用。
-
none : 不分片策略,對應 NoneShardingStrategy。不分片的策略。
5、創建兩個數據庫:order_db_1, order_db_2
CREATE DATABASE `order_db_1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
CREATE DATABASE `order_db_2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
6、在 order_db_1, order_db_2 中分別 創建 t_order_1 和 t_order_2 兩個表:
# 在 數據庫 order_db_1 中,創建兩張表。USE `order_db_1`;# 創建 t_order_1 表DROP TABLE IF EXISTS `t_order_1`;CREATE TABLE `t_order_1` (
`order_id` BIGINT(20) NOT NULL COMMENT '訂單id',
`price` DECIMAL(10,2) NOT NULL COMMENT '訂單價格',
`user_id` BIGINT(20) NOT NULL COMMENT '下單用戶id',
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '訂單狀態',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;# 創建 t_order_2 表DROP TABLE IF EXISTS `t_order_2`;CREATE TABLE `t_order_2` (
`order_id` BIGINT(20) NOT NULL COMMENT '訂單id',
`price` DECIMAL(10,2) NOT NULL COMMENT '訂單價格',
`user_id` BIGINT(20) NOT NULL COMMENT '下單用戶id',
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '訂單狀態',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
# 在 數據庫 order_db_2 中,創建兩張表。USE `order_db_2`;# 創建 t_order_1 表DROP TABLE IF EXISTS `t_order_1`;CREATE TABLE `t_order_1` (
`order_id` BIGINT(20) NOT NULL COMMENT '訂單id',
`price` DECIMAL(10,2) NOT NULL COMMENT '訂單價格',
`user_id` BIGINT(20) NOT NULL COMMENT '下單用戶id',
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '訂單狀態',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;# 創建 t_order_2 表DROP TABLE IF EXISTS `t_order_2`;CREATE TABLE `t_order_2` (
`order_id` BIGINT(20) NOT NULL COMMENT '訂單id',
`price` DECIMAL(10,2) NOT NULL COMMENT '訂單價格',
`user_id` BIGINT(20) NOT NULL COMMENT '下單用戶id',
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '訂單狀態',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
二、Sharding-JDBC 水平分庫-插入訂單
1、在 sharding_jdbc_simple 子工程(子模塊)中,修改 application.properties 配置文件,添加配置分庫策略。
# dbsharding\sharding_jdbc_simple\src\main\resources\application.propertiesserver.port = 56081spring.application.name = sharding-jdbc-simple-demoserver.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = truespring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true# 配置 sharding-jdbc 分片規則(2024-6-29 分片規則修改)
# 定義數據源(定義 多個 數據源名為 m1, m2)
spring.shardingsphere.datasource.names = m1,m2spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2)
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}# 指定 t_order 表的主鍵生成策略為 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE# 指定 t_order 表的分片策略,分片策略包括分片鍵和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}# 打開 sql 輸出日志
spring.shardingsphere.props.sql.show = trueswagger.enable = truelogging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug
2、在 sharding_jdbc_simple 子工程(子模塊)中,測試類 OrderDao 的測試類 OrderDaoTest.java 進行測試
/*** dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\OrderDaoTest.java** 2024-6-28 創建 接口 OrderDao 的測試類 OrderDaoTest.java 進行測試** 快速生成 接口 OrderDao 類的測試類:* 1)右鍵 接口 OrderDao 選擇 【Generate...】* 2)選擇【Test..】* 3)Testing library : JUnit4* Class name : OrderDaoTest* SUPERCLASS : 空* Destination package : djh.it.dbsharding.simple.dao* 4)點擊 OK。*/
package djh.it.dbsharding.simple.dao;import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class OrderDaoTest {@AutowiredOrderDao orderDao;@Test //查詢public void testSelectOrderByIds(){List<Long> ids = new ArrayList<>();ids.add(1013467489922711552L); //此order_id 在 mysql 數據庫的 t_order_1 表中,ids.add(1013465458055053313L); //此order_id 在 mysql 數據庫的 t_order_2 表中,List<Map> maps = orderDao.selectOrderByIds(ids);System.out.println(maps);}@Test //插入數據public void testInsertOrder(){
// // 1)此數據會插入到 m1 數據庫: 1L % 2 + 1 = 2 得到 M2,由此可得 向 order_db_2 數據庫中插入 20 條數據。
// orderDao.insertOrder(new BigDecimal(i ),1L, "success2");// 2)此數據會插入到 m1 數據庫: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 數據庫中插入 20 條數據。for(int i=1; i<20; i++){orderDao.insertOrder(new BigDecimal(i ),4L, "success2");}}
}
3、運行測試類 OrderDaoTest.java 插入數據 testInsertOrder 方法,進行測試。
1)1L % 2 + 1 = 2 得到 M2,由此可得 向 t_order_2 數據庫中插入 20 條數據。
2)此數據會插入到 m1 數據庫: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 數據庫中插入 10 條數據。
三、Sharding-JDBC 水平分庫-查詢訂單
1、在 sharding_jdbc_simple 子工程(子模塊)中,修改 application.properties 配置文件,添加配置分庫策略。
# dbsharding\sharding_jdbc_simple\src\main\resources\application.propertiesserver.port = 56081spring.application.name = sharding-jdbc-simple-demoserver.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = truespring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true# 配置 sharding-jdbc 分片規則(2024-6-29 分片規則修改)
# 定義數據源(定義 多個 數據源名為 m1, m2)
spring.shardingsphere.datasource.names = m1,m2spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2): 只能路由到 m1 數據庫
#spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}
# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2): 動態路由到 m1 數據庫 或 m2 數據庫。spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..2}# 指定 t_order 表的主鍵生成策略為 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE# 指定 t_order 表的分片策略,分片策略包括分片鍵和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}# 打開 sql 輸出日志
spring.shardingsphere.props.sql.show = trueswagger.enable = truelogging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug
2、在 sharding_jdbc_simple 子工程(子模塊)中,修改 dao 接口類 OrderDao.java 添加 //查詢數據:根據訂單ID 和 用戶 id 查詢訂單 方法。
/*** dbsharding\sharding_jdbc_simple\src\main\java\djh\it\dbsharding\simple\dao\OrderDao.java** 2024-5-28 創建 dao 接口類 OrderDao.java*/
package djh.it.dbsharding.simple.dao;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;import java.math.BigDecimal;
import java.util.List;
import java.util.Map;@Mapper
@Component
public interface OrderDao {//查詢數據:根據訂單ID 和 用戶 id 查詢訂單// ( SELECT * FROM t_order_1 WHERE user_id = 4 AND order_id IN (1014650592712196096, 1014650593467170816); )@Select( "<script>" +"select" +" * " +" from t_order t " +" where t.order_id in " +" <foreach collection=' orderIds' open='(' separator=',' close=')' item='id'>" +" #{id} " +" </foreach>" +" and user_id = ${userId}" +"</script>" )List<Map> selectOrderByIdsAndUserId(@Param("userId") Long userId, @Param("orderIds") List<Long> orderIds);//查詢數據:根據訂單ID ( SQL 語句:SELECT * FROM t_order_1 WHERE order_id IN (1013467489922711552, 1013467489960460288); )@Select( "<script>" +"select" +" * " +" from t_order t " +" where t.order_id in " +" <foreach collection=' orderIds' open='(' separator=',' close=')' item='id'>" +" #{id} " +" </foreach>" +"</script>" )List<Map> selectOrderByIds(@Param("orderIds") List<Long> orderIds);//插入數據@Insert("insert into t_order(price, user_id, status) values(#{price}, #{userId}, #{status})")int insertOrder(@Param("price") BigDecimal price, @Param("userId")Long userId, @Param("status")String status);
}
3、在 sharding_jdbc_simple 子工程(子模塊)中,修改 測試類 OrderDao 的測試類 查詢方法,進行多次查詢測試。添加 根據訂單ID 和 用戶 id 查詢訂單 的方法。
/*** dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\OrderDaoTest.java** 2024-6-28 創建 接口 OrderDao 的測試類 OrderDaoTest.java 進行測試** 快速生成 接口 OrderDao 類的測試類:* 1)右鍵 接口 OrderDao 選擇 【Generate...】* 2)選擇【Test..】* 3)Testing library : JUnit4* Class name : OrderDaoTest* SUPERCLASS : 空* Destination package : djh.it.dbsharding.simple.dao* 4)點擊 OK。*/
package djh.it.dbsharding.simple.dao;import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class OrderDaoTest {@AutowiredOrderDao orderDao;@Test //查詢--根據訂單ID 和 用戶 id 查詢訂單public void testSelectOrderByIdsAndUserId(){List<Long> ids = new ArrayList<>();// 水平分庫測試如下:List<Long> ids2 = new ArrayList<>();// //1)查詢不存在的 order_id(在 m1 或 m2 中,都不存在),添加了 userId , 會輸出真實的2條SQL語句。
// ids2.add(1013467489922711552L); //此 order_id 在 mysql 的 m1 和 m2 數據庫中 的 t_order_1 表 和 t_order_2 表中 都不存在
// ids2.add(1013465458055053313L); //此 order_id 在 mysql 的 m1 和 m2 數據庫中 的 t_order_1 表 和 t_order_2 表中 都不存在
// List<Map> maps = orderDao.selectOrderByIdsAndUserId(4L, ids2);
// System.out.println(maps);//2)查詢存在的 order_id(一條在 m1 數據庫,一條在 m2 數據庫中),添加了 userId , 會輸出真實的2條SQL語句。ids2.add(1014650592712196096L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_1 表 中。ids2.add(1014635737775079425L); //此 order_id 在 mysql 的 m2 數據庫中 t_order_2 表 中。List<Map> maps2 = orderDao.selectOrderByIdsAndUserId(4L, ids2);System.out.println(maps2);}@Test //查詢public void testSelectOrderByIds(){List<Long> ids = new ArrayList<>();// //查詢數據庫 order_db 中的數據 order_id(水平分表)
// ids.add(1013467489922711552L); //此order_id 在 mysql 數據庫的 t_order_1 表中,
// ids.add(1013465458055053313L); //此order_id 在 mysql 數據庫的 t_order_2 表中,
// List<Map> maps = orderDao.selectOrderByIds(ids);
// System.out.println(maps);// 水平分庫測試如下:List<Long> ids2 = new ArrayList<>();//1)查詢不存在的 order_id(在 m1 或 m2 中,都不存在),會輸出真實的4條SQL語句。ids2.add(1013467489922711552L); //此 order_id 在 mysql 的 m1 和 m2 數據庫中 的 t_order_1 表 和 t_order_2 表中 都不存在ids2.add(1013465458055053313L); //此 order_id 在 mysql 的 m1 和 m2 數據庫中 的 t_order_1 表 和 t_order_2 表中 都不存在List<Map> maps = orderDao.selectOrderByIds(ids2);System.out.println(maps);// //2)查詢存在的 order_id(一條在 m1 數據庫,一條在 m2 數據庫中),會輸出真實的4條SQL語句。
// ids2.add(1014650592712196096L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_1 表 中。
// ids2.add(1014635737775079425L); //此 order_id 在 mysql 的 m2 數據庫中 t_order_2 表 中。
// List<Map> maps2 = orderDao.selectOrderByIds(ids2);
// System.out.println(maps2);// //3)查詢存在的 order_id(2條都在 m1 數據庫,不同表中),會輸出真實的4條SQL語句。
// ids2.add(1014650592712196096L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_1 表 中。
// ids2.add(1014635737775079425L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_2 表 中。
// List<Map> maps2 = orderDao.selectOrderByIds(ids2);
// System.out.println(maps2);// //4)查詢存在的 order_id(2條都在 m1 數據庫,同一表中),會輸出真實的2條SQL語句。
// ids2.add(1014650592712196096L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_1 表 中。
// ids2.add(1014635742883741696L); //此 order_id 在 mysql 的 m1 數據庫中 t_order_1 表 中。
// List<Map> maps2 = orderDao.selectOrderByIds(ids2);
// System.out.println(maps2);// //5)查詢存在的 order_id(1條都在 m2 數據庫),會輸出真實的2條SQL語句。
// ids2.add(1014635741851942912L); //此 order_id 在 mysql 的 m2 數據庫中 t_order_1 表 中。
// List<Map> maps2 = orderDao.selectOrderByIds(ids2);
// System.out.println(maps2);}@Test //插入數據public void testInsertOrder(){//orderDao.insertOrder(new BigDecimal(11 ),1L, "SUCCESS");for(int i=1; i<10; i++){
// // 1)此數據會插入到 m1 數據庫: 1L % 2 + 1 = 2 得到 M2,由此可得 向 order_db_2 數據庫中插入 20 條數據。
// orderDao.insertOrder(new BigDecimal(i ),1L, "success2");// 2)此數據會插入到 m1 數據庫: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 數據庫中插入 10 條數據。orderDao.insertOrder(new BigDecimal(i ),4L, "success3");}}
}
4、運行測試類 OrderDaoTest.java 查詢數據 testSelectOrderByIds 方法,進行測試。
1)查詢不存在的 order_id(在 m1 或 m2 中,都不存在),會輸出真實的4條SQL語句。
2)查詢存在的 order_id(一條在 m1 數據庫,一條在 m2 數據庫中),會輸出真實的4條SQL語句。
3)查詢存在的 order_id(2條都在 m1 數據庫,不同表中),會輸出真實的4條SQL語句。
4)查詢存在的 order_id(2條都在 m1 數據庫 同一表中),會輸出真實的2條SQL語句。
5)查詢存在的 order_id(1條都在 m2 數據庫),會輸出真實的2條SQL語句。
6)查詢不存在的 order_id(在 m1 或 m2 中,都不存在),添加了 userId ,
7)查詢存在的 order_id(一條在 m1 數據庫,一條在 m2 數據庫中),添加了 userId , 會輸出真實的2條SQL語句。
四、Sharding-JDBC 垂直分庫-分片策略配置
1、創建數據庫:user_db
CREATE DATABASE `user_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
2、在 suer_db 中分別 創建 t_user 表:
# 在 數據庫 order_db_1 中,創建兩張表。USE `user_db`;# 創建 t_user 表DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` (
`user_id` BIGINT(20) NOT NULL COMMENT '用戶id',
`fullname` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用戶姓名',
`user_type` char(1) DEFAULT NULL COMMENT '用戶類型',
PRIMARY KEY(`user_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
3、在 application.properties 配置文件中,配置數據源,
spring.shardingsphere.datasource.names = m0,m1,m2spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true
spring.shardingsphere.datasource.m0.username = root
spring.shardingsphere.datasource.m0.password = 12311# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}# 配置 user_db 數據節點
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes = m0.t_user# 指定 t_user 表的分片策略,分片策略包括分片鍵和分片算法(未分庫分表也需要配置)
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression = t_user
五、Sharding-JDBC 垂直分庫-插入和查詢測試
1、在 sharding_jdbc_simple 子工程(子模塊)中,修改 application.properties 配置文件,添加 user_db 數據庫的 配置分庫分表策略。
# dbsharding\sharding_jdbc_simple\src\main\resources\application.propertiesserver.port = 56081spring.application.name = sharding-jdbc-simple-demoserver.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = truespring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true# 配置 sharding-jdbc 分片規則(2024-6-29 分片規則修改)
# 定義數據源(定義 多個 數據源名為 m1, m2)
spring.shardingsphere.datasource.names = m0,m1,m2spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true
spring.shardingsphere.datasource.m0.username = root
spring.shardingsphere.datasource.m0.password = 12311spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311# 分庫策略:以 user_id 為分片鍵,分片策略為 user_id % 2 + 1,user_id 為偶數操作 m1 數據源,否則操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2): 只能路由到 m1 數據庫
#spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}
# 指定 t_order 表的數據分布情況,配置數據節點(t_order 映射到 t_order_1 或者 t_order_2): 動態路由到 m1 數據庫 或 m2 數據庫。
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..2}
# 配置 user_db 數據節點
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes = m$->{0}.t_user# 指定 t_order 表的主鍵生成策略為 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE# 指定 t_order 表的分片策略,分片策略包括分片鍵和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}# 指定 t_user 表的分片策略,分片策略包括分片鍵和分片算法(未分庫分表也需要配置)
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression = t_user# 打開 sql 輸出日志
spring.shardingsphere.props.sql.show = trueswagger.enable = truelogging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug
2、在 sharding_jdbc_simple 子工程(子模塊)中,創建 接口類 UserDao.java
/*** dbsharding\sharding_jdbc_simple\src\main\java\djh\it\dbsharding\simple\dao\UserDao.java** 2024-7-1 創建 接口類 UserDao.java*/
package djh.it.dbsharding.simple.dao;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Map;@Mapper
@Component
public interface UserDao {//新增用戶@Insert("insert into t_user(user_id, fullname) value(#{userId}, #{fullname})")int insertUser(@Param("userId") Long userId, @Param("fullname") String fullname);//根據id列表查詢多個用戶@Select({ "<script>" ," select" ," * " ," from t_user t " ," where t.user_id in "," <foreach collection=' userIds' open='(' separator=',' close=')' item='id'>" +" #{id} " ," </foreach>" ,"</script>" })List<Map> selectUserId(@Param("userIds") List<Long> userIds);
}
3、在 sharding_jdbc_simple 子工程(子模塊)中,創建 接口類 UserDao.java 的測試類 UserDaoTest.java 進行插入數據 和 查詢數據測試。
/*** dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\UserDaoTest.java** 2024-7-1 創建 接口類 UserDao.java 的測試類 UserDaoTest.java 進行插入數據 和 查詢數據測試 UserDaoTest.java*/
package djh.it.dbsharding.simple.dao;import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class UserDaoTest {@AutowiredUserDao userDao;@Testpublic void testInsertUser() {for(int i=0; i<10; i++){Long id = i + 1L;userDao.insertUser(id, "姓名" + id);}}@Testpublic void testSelectUserByIds() {List<Long> userIds = new ArrayList<>();userIds.add(1L);userIds.add(2L);List<Map> users = userDao.selectUserId(userIds);System.out.println(users);}
}
4、運行測試類 UserDaoTest.java,查看結果。
Sharding-JDBC垂直分庫-插入和查詢測試.png
上一節關聯鏈接請點擊
# Sharding-JDBC從入門到精通(5)-- Sharding-JDBC 執行原理