ShardingSphere-JDBC 入门教程之五

大纲

前言

学习资源

ShardingSphere-JDBC 使用

水平分库使用案例

准备工作

版本说明

本案例所使用的组件版本如下表所示:

组件版本说明
JDK11
MySQL8.0.29
SpringBoot2.7.18
MyBatis-Plus3.3.1
ShardingSphere-JDBC5.1.1
数据库规划

本案例是在两个 MySQL 数据库上实现的,数据库的规划如下图所示:

数据库服务器 IP 端口库的名称表的名称
订单数据库服务器一(server-order0192.168.2.1913310db_ordert_order0t_order1t_order_item0t_order_item1
订单数据库服务器二(server-order1192.168.2.1913311db_ordert_order0t_order1t_order_item0t_order_item1
数据库部署
  • 部署订单数据库服务器一(server-order0
1
2
3
4
5
6
7
8
9
10
# 创建并启动 MySQL 容器(Docker 会自动在宿主机上创建不存在的目录)
docker run -d \
-p 3310:3306 \
-v /data/server/order0/conf:/etc/mysql/conf.d \
-v /data/server/order0/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--name server-order0 \
--restart always \
mysql:8.0.29
  • 更改订单数据库服务器一(server-order0)的默认密码校验方式
1
2
3
4
5
6
7
8
# 进入容器内,其中环境变量 "env LANG=C.UTF-8" 用于避免容器内显示中文乱码的问题
docker exec -it server-order0 env LANG=C.UTF-8 /bin/bash

# 在容器内执行 MySQL 命令行
mysql -uroot -p

# 修改 MySQL 超级管理员用户的默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

  • 部署订单数据库服务器二(server-order1
1
2
3
4
5
6
7
8
9
10
# 创建并启动 MySQL 容器(Docker 会自动在宿主机上创建不存在的目录)
docker run -d \
-p 3311:3306 \
-v /data/server/order1/conf:/etc/mysql/conf.d \
-v /data/server/order1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--name server-order1 \
--restart always \
mysql:8.0.29
  • 更改订单数据库服务器二(server-order1)的默认密码校验方式
1
2
3
4
5
6
7
8
# 进入容器内,其中环境变量 "env LANG=C.UTF-8" 用于避免容器内显示中文乱码的问题
docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash

# 在容器内执行 MySQL 命令行
mysql -uroot -p

# 修改 MySQL 超级管理员用户的默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
数据库初始化
  • 在订单数据库服务器一(server-order0)中,执行以下 SQL 语句:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-- 创建数据库
CREATE DATABASE db_order;

-- 切换数据库
USE db_order;

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order0 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order1 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order_item0(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order_item1(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);
  • 在订单数据库服务器二(server-order1)中,执行以下 SQL 语句:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-- 创建数据库
CREATE DATABASE db_order;

-- 切换数据库
USE db_order;

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order0 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order1 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order_item0(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);

-- 创建表(水平分库时不能使用数据库的自增主键,否则不同库不同表之间会发生主键冲突)
CREATE TABLE t_order_item1(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);

多表关联使用案例

提示

本节将演示 SpringBoot + MyBatis-Plus 如何整合 ShardingSphere-JDBC,并在 水平分库 的基础上实现多表关联数据插入。水平分库会根据某个字段(或几个字段)通过特定规则,将数据分散到多个库或表中,每个分片只存储部分数据。例如,可按主键分片:偶数主键记录放入 0 库(或表),奇数主键记录放入 1 库(或表),如图所示水平分库通常是在水平分表的基础上进一步进行,将原本分散到多张表的数据继续分布到多台数据库服务器上,以提升整体的并发处理能力。完整的案例代码可以直接从 GitHub 下载对应章节 shardingsphere-jdbc-lesson-04

核心概念

多表关联(JOIN)能力并非 ShardingSphere-Proxy 额外提供的数据库功能,而是基于数据库本身的设计与约束。ShardingSphere-Proxy 只是对符合其分片规则与绑定表规则的 JOIN SQL 进行解析、路由与结果归并;是否能够高效甚至正确地执行多表关联,核心取决于表结构设计、分片键选择以及是否配置了绑定表,而非 ShardingSphere-Proxy 本身创造了新的关联能力。

案例代码
添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<properties>
<spring-boot.version>2.7.18</spring-boot.version>
<shardingsphere.version>5.1.1</shardingsphere.version>
<mysql-connector.version>8.2.0</mysql-connector.version>
<mybatis-plus.version>3.3.1</mybatis-plus.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- SpringBoot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- ShardingSphere-Jdbc -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
创建实体类
  • 订单实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

@TableName("t_order") // 逻辑表名
@Data
public class Order {

/**
* 当项目中配置了 ShardingSphere-JDBC 的分布式序列策略时,会自动使用 ShardingSphere-JDBC 的分布式序列策略
* 当项目中没有配置 ShardingSphere-JDBC 的分布式序列策略时,自动依赖数据库的主键自增策略
*/
@TableId(type = IdType.AUTO)
private Long id;

private String orderNo;

private Long userId;

private BigDecimal amount;

}
  • 订单详情实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

@TableName("t_order_item") // 逻辑表名
@Data
public class OrderItem {

/**
* 当项目中配置了 ShardingSphere-JDBC 的分布式序列策略时,会自动使用 ShardingSphere-JDBC 的分布式序列策略
* 当项目中没有配置 ShardingSphere-JDBC 的分布式序列策略时,自动依赖数据库的主键自增策略
*/
@TableId(type = IdType.AUTO)
private Long id;

private String orderNo;

private Long userId;

private BigDecimal price;

private Integer count;

}
创建 Mapper
  • 订单 Mapper
1
2
3
4
5
6
7
8
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.clay.shardingjdbc.entity.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}
  • 订单详情 Mapper
1
2
3
4
5
6
7
8
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.clay.shardingjdbc.entity.OrderItem;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {

}
配置数据分片

这里的配置以水平分库为例子,演示如何使用 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# ----------基础配置----------

# 应用名称
spring.application.name=sharging-jdbc-demo
# 开发环境设置
spring.profiles.active=dev
# 模式配置(可选值:Memory、Standalone、Cluster)
spring.shardingsphere.mode.type=Memory
# 打印SQL语句
spring.shardingsphere.props.sql-show=true

# ----------数据源配置----------

# 配置真实数据源
spring.shardingsphere.datasource.names=server-order0,server-order1

# 配置第 1 个数据源
spring.shardingsphere.datasource.server-order0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.server-order0.jdbc-url=jdbc:mysql://192.168.2.191:3310/db_order
spring.shardingsphere.datasource.server-order0.username=root
spring.shardingsphere.datasource.server-order0.password=123456

# 配置第 2 个数据源
spring.shardingsphere.datasource.server-order1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.server-order1.jdbc-url=jdbc:mysql://192.168.2.191:3311/db_order
spring.shardingsphere.datasource.server-order1.username=root
spring.shardingsphere.datasource.server-order1.password=123456

# ----------标椎分片表配置----------

# 标准分片表配置(数据节点),这里有两个库(server-order0、server-order1),每个库有四张表(t_order0、t_order1、t_order_item0、t_order_item1)
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔;多个表以逗号分隔,支持行表达式
# <table-name>:逻辑表名
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}
spring.shardingsphere.rules.sharding.tables.t_order_item.actual-data-nodes=server-order$->{0..1}.t_order_item$->{0..1}

# ----------分库策略配置----------

# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-column=user_id
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-algorithm-name=alg_mod
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-algorithm-name=alg_mod

# ----------分表策略配置----------

# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_no
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-column=order_no
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=alg_hash_mod
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-algorithm-name=alg_hash_mod

# ----------分片算法配置----------

# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.type=MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.props.sharding-count=2

# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.type=HASH_MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.props.sharding-count=2

# ----------分布式序列策略配置----------

# 分布式序列列名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=id
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.column=id
# 分布式序列算法名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=alg_snowflake
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.key-generator-name=alg_snowflake

# ----------分布式序列算法配置----------

# 分布式序列算法类型
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.type=SNOWFLAKE
# 分布式序列算法属性配置
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.max-vibration-offset=1
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.max-tolerate-time-difference-milliseconds=10
测试代码
多表关联数据插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@SpringBootTest
class ShardingTest {

@Autowired
OrderMapper orderMapper;

@Autowired
OrderItemMapper orderItemMapper;

/**
* 水平分库之多表关联:多表关联数据插入测试 <p>
* 同一个用户的订单表和订单详情表中的数据会插入到同一个数据源中,这样可以避免跨库关联
*/
@Test
public void testInsertOrder() {
for (int i = 1; i <= 3; i++) {
Order order = new Order();
order.setOrderNo("000" + i);
order.setUserId((long) i);
orderMapper.insert(order);

for (int j = 1; j <= 2; j++) {
OrderItem orderItem = new OrderItem();
orderItem.setOrderNo("000" + i);
orderItem.setUserId((long) i);
orderItem.setPrice(new BigDecimal(10));
orderItem.setCount(2);
orderItemMapper.insert(orderItem);
}
}
}

}

测试代码运行后的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2022-09-10 22:42:23.973  INFO 38270 --- [           main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order ( order_no, user_id ) VALUES ( ?, ? )
2022-09-10 22:42:23.974 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:23.974 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order1 ( order_no, user_id , id) VALUES (?, ?, ?) ::: [0001, 1, 1206248879498985472]

2022-09-10 22:42:24.073 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.074 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.074 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order_item1 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0001, 1, 10, 2, 1206248880253960193]

2022-09-10 22:42:24.091 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.091 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.092 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order_item1 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0001, 1, 10, 2, 1206248880329457664]

2022-09-10 22:42:24.103 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order ( order_no, user_id ) VALUES ( ?, ? )
2022-09-10 22:42:24.103 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.103 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: INSERT INTO t_order0 ( order_no, user_id , id) VALUES (?, ?, ?) ::: [0002, 2, 1206248880375595009]

2022-09-10 22:42:24.118 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.118 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.118 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: INSERT INTO t_order_item0 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0002, 2, 10, 2, 1206248880438509568]

2022-09-10 22:42:24.131 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.131 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.132 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order0 ::: INSERT INTO t_order_item0 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0002, 2, 10, 2, 1206248880497229825]

2022-09-10 22:42:24.142 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order ( order_no, user_id ) VALUES ( ?, ? )
2022-09-10 22:42:24.142 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.142 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order1 ( order_no, user_id , id) VALUES (?, ?, ?) ::: [0003, 3, 1206248880543367168]

2022-09-10 22:42:24.153 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.153 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.153 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order_item1 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0003, 3, 10, 2, 1206248880585310209]

2022-09-10 22:42:24.165 INFO 38270 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO t_order_item ( order_no, user_id, price, count ) VALUES ( ?, ?, ?, ? )
2022-09-10 22:42:24.165 INFO 38270 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
2022-09-10 22:42:24.165 INFO 38270 --- [ main] ShardingSphere-SQL : Actual SQL: server-order1 ::: INSERT INTO t_order_item1 ( order_no, user_id, price, count , id) VALUES (?, ?, ?, ?, ?) ::: [0003, 3, 10, 2, 1206248880635641856]

绑定表使用案例

提示

本节将演示 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
      7
      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);
    • 在配置绑定表关系,并且使用 order_id 进行关联后,路由的 SQL 应该为 2 条:
      1
      2
      3
      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 表由于指定了分片条件,ShardingSphere 将会以它作为整个绑定表的主表。所有路由计算将会只使用主表的策略,那么 t_order_item 表的分片计算将会使用 t_order 的条件。
  • 总结说明:
    • 绑定表的核心目标是保证 ShardingSphere-JDBC 在执行多表关联查询时,能够计算出相同的路由目标,避免:
      • 不必要的跨库 JOIN
      • 笛卡尔积路由
      • 扫描全部数据源
    • 绑定表要求多张有关联的表在分库规则和分表规则上完全一致;否则 ShardingSphere-JDBC 无法在 JOIN 时计算出相同的路由,导致不能被视为绑定表
案例代码
添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<properties>
<spring-boot.version>2.7.18</spring-boot.version>
<shardingsphere.version>5.1.1</shardingsphere.version>
<mysql-connector.version>8.2.0</mysql-connector.version>
<mybatis-plus.version>3.3.1</mybatis-plus.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- SpringBoot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- ShardingSphere-Jdbc -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
创建实体类
  • 订单实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

@TableName("t_order") // 逻辑表名
@Data
public class Order {

/**
* 当项目中配置了 ShardingSphere-JDBC 的分布式序列策略时,会自动使用 ShardingSphere-JDBC 的分布式序列策略
* 当项目中没有配置 ShardingSphere-JDBC 的分布式序列策略时,自动依赖数据库的主键自增策略
*/
@TableId(type = IdType.AUTO)
private Long id;

private String orderNo;

private Long userId;

private BigDecimal amount;

}
  • 订单详情实体类(特别注意:分库分表场景下不能使用数据库的自增主键,必须采用分布式全局唯一 ID,否则不同库不同表之间会发生主键冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

@TableName("t_order_item") // 逻辑表名
@Data
public class OrderItem {

/**
* 当项目中配置了 ShardingSphere-JDBC 的分布式序列策略时,会自动使用 ShardingSphere-JDBC 的分布式序列策略
* 当项目中没有配置 ShardingSphere-JDBC 的分布式序列策略时,自动依赖数据库的主键自增策略
*/
@TableId(type = IdType.AUTO)
private Long id;

private String orderNo;

private Long userId;

private BigDecimal price;

private Integer count;

}
创建 VO 类
1
2
3
4
5
6
7
8
9
10
11
import lombok.Data;
import java.math.BigDecimal;

@Data
public class OrderVo {

private String orderNo;

private BigDecimal amount;

}
创建 Mapper
  • 订单 Mapper(特别注意:由于后面配置了 ShardingSphere-JDBC 的绑定表,因此这里执行多表关联查询时,必须使用分片键(order_no)进行关联(join),否则会出现笛卡尔积关联或跨库关联,从而严重影响查询效率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.clay.shardingjdbc.entity.Order;
import com.clay.shardingjdbc.vo.OrderVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

/**
* 查询每个订单的订单号和总订单金额 <p>
* 由于项目中配置了 ShardingSphere-JDBC 的绑定表,因此这里执行多表关联查询时,必须使用分片键("order_no")进行关联,否则会出现笛卡尔积关联或跨库关联,从而严重影响查询效率
*/
@Select({"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"})
List<OrderVo> getOrderAmount();

}
  • 订单详情 Mapper
1
2
3
4
5
6
7
8
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.clay.shardingjdbc.entity.OrderItem;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {

}
配置数据分片

这里的配置以水平分库为例子,演示如何使用 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# ----------基础配置----------

# 应用名称
spring.application.name=sharging-jdbc-demo
# 开发环境设置
spring.profiles.active=dev
# 模式配置(可选值:Memory、Standalone、Cluster)
spring.shardingsphere.mode.type=Memory
# 打印SQL语句
spring.shardingsphere.props.sql-show=true

# ----------数据源配置----------

# 配置真实数据源
spring.shardingsphere.datasource.names=server-order0,server-order1

# 配置第 1 个数据源
spring.shardingsphere.datasource.server-order0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.server-order0.jdbc-url=jdbc:mysql://192.168.2.191:3310/db_order
spring.shardingsphere.datasource.server-order0.username=root
spring.shardingsphere.datasource.server-order0.password=123456

# 配置第 2 个数据源
spring.shardingsphere.datasource.server-order1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.server-order1.jdbc-url=jdbc:mysql://192.168.2.191:3311/db_order
spring.shardingsphere.datasource.server-order1.username=root
spring.shardingsphere.datasource.server-order1.password=123456

# ----------标椎分片表配置----------

# 标准分片表配置(数据节点),这里有两个库(server-order0、server-order1),每个库有四张表(t_order0、t_order1、t_order_item0、t_order_item1)
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔;多个表以逗号分隔,支持行表达式
# <table-name>:逻辑表名
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}
spring.shardingsphere.rules.sharding.tables.t_order_item.actual-data-nodes=server-order$->{0..1}.t_order_item$->{0..1}

# ----------绑定表配置----------
spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item

# ----------分库策略配置----------

# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-column=user_id
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-algorithm-name=alg_mod
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-algorithm-name=alg_mod

# ----------分表策略配置----------

# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_no
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-column=order_no
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=alg_hash_mod
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-algorithm-name=alg_hash_mod

# ----------分片算法配置----------

# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.type=MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.props.sharding-count=2

# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.type=HASH_MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.props.sharding-count=2

# ----------分布式序列策略配置----------

# 分布式序列列名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=id
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.column=id
# 分布式序列算法名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=alg_snowflake
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.key-generator-name=alg_snowflake

# ----------分布式序列算法配置----------

# 分布式序列算法类型
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.type=SNOWFLAKE
# 分布式序列算法属性配置
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.max-vibration-offset=1
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.max-tolerate-time-difference-milliseconds=10
  • 由于订单表(t_order)与订单详情表(t_order_item)的分片规则(包括分库规则和分表规则)完全相同,为了提高多表关联查询的效率,上面的配置内容中添加了 ShardingSphere-JDBC 的绑定表配置,如下所示:
1
2
# ----------绑定表配置----------
spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item
测试代码
多表关联数据查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SpringBootTest
class ShardingTest {

@Autowired
OrderMapper orderMapper;

/**
* 水平分库之绑定表:多表关联数据查询测试 <p>
*/
@Test
public void testGetOrderAmount() {
List<OrderVo> orderAmountList = orderMapper.getOrderAmount();
}

}

测试代码运行后的输出结果如下:

1
2
3
4
5
6
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
2022-09-10 23:47:00.687 INFO 68567 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2022-09-10 23:47:00.687 INFO 68567 --- [ 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:47:00.687 INFO 68567 --- [ 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
2022-09-10 23:47:00.687 INFO 68567 --- [ 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:47:00.687 INFO 68567 --- [ 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

特别注意:

  • 如果配置绑定表:测试的结果最终为 4 条 SQL(如上所示), 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
  • 如果不配置绑定表:测试的结果最终为 8 条 SQL(如下所示),多表关联查询会出现笛卡尔积关联,关联查询效率将大大降低。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    2022-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