ShardingSphere-JDBC 入门教程之五
大纲
- ShardingSphere-JDBC 入门教程之一、ShardingSphere-JDBC 入门教程之二、ShardingSphere-JDBC 入门教程之三
- ShardingSphere-JDBC 入门教程之四、ShardingSphere-JDBC 入门教程之五、ShardingSphere-JDBC 入门教程之六
- ShardingSphere-JDBC 入门教程之七、ShardingSphere-JDBC 入门教程之八、ShardingSphere-JDBC 入门教程之九
前言
学习资源
- ShardingSphere 官方项目(GitHub)
- ShardingSphere 官方网站(中文站点)
- ShardingSphere 官方文档(最新版本)
- ShardingSphere 官方文档(5.1.1 版本)
ShardingSphere-JDBC 使用
水平分库使用案例
准备工作
版本说明
本案例所使用的组件版本如下表所示:
| 组件 | 版本说明 |
|---|---|
| JDK | 11 |
| MySQL | 8.0.29 |
| SpringBoot | 2.7.18 |
| MyBatis-Plus | 3.3.1 |
| ShardingSphere-JDBC | 5.1.1 |
数据库规划
本案例是在两个 MySQL 数据库上实现的,数据库的规划如下图所示:

| 数据库服务器 | IP | 端口 | 库的名称 | 表的名称 |
|---|---|---|---|---|
订单数据库服务器一(server-order0) | 192.168.2.191 | 3310 | db_order | t_order0、t_order1、t_order_item0、t_order_item1 |
订单数据库服务器二(server-order1) | 192.168.2.191 | 3311 | db_order | t_order0、t_order1、t_order_item0、t_order_item1 |
数据库部署
- 部署订单数据库服务器一(
server-order0)
1 | # 创建并启动 MySQL 容器(Docker 会自动在宿主机上创建不存在的目录) |
- 更改订单数据库服务器一(
server-order0)的默认密码校验方式
1 | # 进入容器内,其中环境变量 "env LANG=C.UTF-8" 用于避免容器内显示中文乱码的问题 |
- 部署订单数据库服务器二(
server-order1)
1 | # 创建并启动 MySQL 容器(Docker 会自动在宿主机上创建不存在的目录) |
- 更改订单数据库服务器二(
server-order1)的默认密码校验方式
1 | # 进入容器内,其中环境变量 "env LANG=C.UTF-8" 用于避免容器内显示中文乱码的问题 |
数据库初始化
- 在订单数据库服务器一(
server-order0)中,执行以下 SQL 语句:
1 | -- 创建数据库 |
- 在订单数据库服务器二(
server-order1)中,执行以下 SQL 语句:
1 | -- 创建数据库 |
多表关联使用案例
提示
本节将演示 SpringBoot + MyBatis-Plus 如何整合 ShardingSphere-JDBC,并在 水平分库 的基础上实现多表关联数据插入。水平分库会根据某个字段(或几个字段)通过特定规则,将数据分散到多个库或表中,每个分片只存储部分数据。例如,可按主键分片:偶数主键记录放入 0 库(或表),奇数主键记录放入 1 库(或表),如图所示。水平分库通常是在水平分表的基础上进一步进行,将原本分散到多张表的数据继续分布到多台数据库服务器上,以提升整体的并发处理能力。完整的案例代码可以直接从 GitHub 下载对应章节 shardingsphere-jdbc-lesson-04。
核心概念
多表关联(JOIN)能力并非 ShardingSphere-Proxy 额外提供的数据库功能,而是基于数据库本身的设计与约束。ShardingSphere-Proxy 只是对符合其分片规则与绑定表规则的 JOIN SQL 进行解析、路由与结果归并;是否能够高效甚至正确地执行多表关联,核心取决于表结构设计、分片键选择以及是否配置了绑定表,而非 ShardingSphere-Proxy 本身创造了新的关联能力。
案例代码
添加依赖
1 | <properties> |
创建实体类
- 订单实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突)
1 | import com.baomidou.mybatisplus.annotation.IdType; |
- 订单详情实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突)
1 | import com.baomidou.mybatisplus.annotation.IdType; |
创建 Mapper
- 订单 Mapper
1 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
- 订单详情 Mapper
1 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
配置数据分片
这里的配置以水平分库为例子,演示如何使用 ShardingSphere-JDBC 提供的分片算法,其中分片规则如下:
分库规则:
- 订单表(
t_order):t_order逻辑表中user_id为偶数时,数据插入到订单数据库服务器一(server-order0);user_id为奇数时,数据插入到订单数据库服务器二(server-order1)。- 这样分库的好处是,同一个用户的订单数据,一定会被插入到同一台数据库服务器上,这样查询某个用户的所有订单时效率较高。
- 订单详情表(
t_order_item):t_order_item逻辑表中user_id为偶数时,数据插入到订单数据库服务器一(server-order0);user_id为奇数时,数据插入到订单数据库服务器二(server-order1)。- 这样分库的好处是,同一个用户的订单详情数据,一定会被插入到同一台数据库服务器上,这样查询某个用户的所有订单详情时效率较高。
- 订单表(
分表规则:
- 订单表(
t_order):t_order逻辑表中order_no的哈希值为偶数时,数据插入对应数据库服务器的t_order0表;order_no的哈希值为奇数时,数据插入对应数据库服务器的t_order1表。- 因为
order_no是字符串类型,所以不能直接取模,需要对其进行哈希计算再取模。
- 订单详情表(
t_order_item):t_order_item逻辑表中order_no的哈希值为偶数时,数据插入对应数据库服务器的t_order_item0表;order_no的哈希值为奇数时,数据插入对应数据库服务器的t_order_item1表。- 因为
order_no是字符串类型,所以不能直接取模,需要对其进行哈希计算再取模。
- 订单表(
数据库规划:

特别注意
这里希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联,因此订单表和订单详情表使用相同的分库规则。
- 创建配置文件(
application.properties),配置数据分片
1 | # ----------基础配置---------- |
官方文档
测试代码
多表关联数据插入
1 |
|
测试代码运行后的输出结果如下:
1 | 2022-09-10 22:42:23.973 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order ( order_no, user_id ) VALUES ( ?, ? ) |
绑定表使用案例
提示
本节将演示 SpringBoot + MyBatis-Plus 如何整合 ShardingSphere-JDBC,并在 水平分库 的基础上结合绑定表实现多表关联数据查询。绑定表是建立在 多表关联 的基础上,其作用是提高多表关联查询的效率。水平分库会根据某个字段(或几个字段)通过特定规则,将数据分散到多个库或表中,每个分片只存储部分数据。例如,可按主键分片:偶数主键记录放入 0 库(或表),奇数主键记录放入 1 库(或表),如图所示。水平分库通常是在水平分表的基础上进一步进行,将原本分散到多张表的数据继续分布到多台数据库服务器上,以提升整体的并发处理能力。完整的案例代码可以直接从 GitHub 下载对应章节 shardingsphere-jdbc-lesson-05。
核心概念
- 在 ShardingSphere-Proxy 中,绑定表是指分片规则(包括分库规则和分表规则)完全一致的一组分片表。使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而严重影响查询效率。
- 例如:
t_order表和t_order_item表,均按照order_id进行分片(包括分库和分表),并且使用order_id进行关联,则此两张表互为绑定表关系。 - 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
- 举例说明:
- 如果 SQL 为:
1
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 条,它们呈现为笛卡尔积:1
2
3
4
5
6
7SELECT 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); - 在配置绑定表关系,并且使用
order_id进行关联后,路由的 SQL 应该为 2 条:1
2
3SELECT 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表由于指定了分片条件,ShardingSphere 将会以它作为整个绑定表的主表。所有路由计算将会只使用主表的策略,那么t_order_item表的分片计算将会使用t_order的条件。
- 如果 SQL 为:
- 总结说明:
- 绑定表的核心目标是保证 ShardingSphere-JDBC 在执行多表关联查询时,能够计算出相同的路由目标,避免:
- 不必要的跨库 JOIN
- 笛卡尔积路由
- 扫描全部数据源
- 绑定表要求多张有关联的表在分库规则和分表规则上完全一致;否则 ShardingSphere-JDBC 无法在 JOIN 时计算出相同的路由,导致不能被视为绑定表。
- 绑定表的核心目标是保证 ShardingSphere-JDBC 在执行多表关联查询时,能够计算出相同的路由目标,避免:
案例代码
添加依赖
1 | <properties> |
创建实体类
- 订单实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突)
1 | import com.baomidou.mybatisplus.annotation.IdType; |
- 订单详情实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突)
1 | import com.baomidou.mybatisplus.annotation.IdType; |
创建 VO 类
1 | import lombok.Data; |
创建 Mapper
- 订单 Mapper(特别注意:由于后面配置了 ShardingSphere-JDBC 的绑定表,因此这里执行多表关联查询时,必须使用分片键(
order_no)进行关联(join),否则会出现笛卡尔积关联或跨库关联,从而严重影响查询效率)
1 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
- 订单详情 Mapper
1 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
配置数据分片
这里的配置以水平分库为例子,演示如何使用 ShardingSphere-JDBC 提供的分片算法,其中分片规则如下:
分库规则:
- 订单表(
t_order):t_order逻辑表中user_id为偶数时,数据插入到订单数据库服务器一(server-order0);user_id为奇数时,数据插入到订单数据库服务器二(server-order1)。- 这样分库的好处是,同一个用户的订单数据,一定会被插入到同一台数据库服务器上,这样查询某个用户的所有订单时效率较高。
- 订单详情表(
t_order_item):t_order_item逻辑表中user_id为偶数时,数据插入到订单数据库服务器一(server-order0);user_id为奇数时,数据插入到订单数据库服务器二(server-order1)。- 这样分库的好处是,同一个用户的订单详情数据,一定会被插入到同一台数据库服务器上,这样查询某个用户的所有订单详情时效率较高。
- 订单表(
分表规则:
- 订单表(
t_order):t_order逻辑表中order_no的哈希值为偶数时,数据插入对应数据库服务器的t_order0表;order_no的哈希值为奇数时,数据插入对应数据库服务器的t_order1表。- 因为
order_no是字符串类型,所以不能直接取模,需要对其进行哈希计算再取模。
- 订单详情表(
t_order_item):t_order_item逻辑表中order_no的哈希值为偶数时,数据插入对应数据库服务器的t_order_item0表;order_no的哈希值为奇数时,数据插入对应数据库服务器的t_order_item1表。- 因为
order_no是字符串类型,所以不能直接取模,需要对其进行哈希计算再取模。
- 订单表(
数据库规划:

特别注意
- 这里希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联,因此订单表和订单详情表使用相同的分库规则。
- 由于订单表与订单详情表的分片规则(包括分库规则和分表规则)完全相同,因此这两个表在 ShardingSphere-JDBC 中形成了绑定表关系。
- 创建配置文件(
application.properties),配置数据分片
1 | # ----------基础配置---------- |
- 由于订单表(
t_order)与订单详情表(t_order_item)的分片规则(包括分库规则和分表规则)完全相同,为了提高多表关联查询的效率,上面的配置内容中添加了 ShardingSphere-JDBC 的绑定表配置,如下所示:
1 | # ----------绑定表配置---------- |
官方文档
测试代码
多表关联数据查询
1 |
|
测试代码运行后的输出结果如下:
1 | 2022-09-10 23:47:00.687 INFO 68567 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no |
特别注意:
- 如果配置绑定表:测试的结果最终为 4 条 SQL(如上所示), 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
- 如果不配置绑定表:测试的结果最终为 8 条 SQL(如下所示),多表关联查询会出现笛卡尔积关联,关联查询效率将大大降低。
1
2
3
4
5
6
7
8
9
102022-09-10 23:49:28.377 INFO 69322 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2022-09-10 23:49:28.378 INFO 69322 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
