# 基础配置实战
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
代码参考:[https://github.com/YangYingmeng/learning_shardingJDBC)
# 一、插入数据
# 1.插入绑定表(主表 + 明细表)
/**
* 插入 订单 + 订单项(绑定表)
*/
@Test
public void testSaveProductOrderAndItem() {
Random random = new Random();
for (int i = 0; i < 20; i++) {
// 1️⃣ 先插入订单主表
Long userId = (long) random.nextInt(50);
ProductOrderDO order = new ProductOrderDO();
order.setCreateTime(new Date());
order.setNickname("自定义水平分库分表 i=" + i);
order.setOutTradeNo(UUID.randomUUID().toString().replace("-", ""));
order.setPayAmount(100.00);
order.setState("PAY");
order.setUserId(userId);
// 这里不要自己 setId,让 ShardingSphere 生成
productOrderMapper.insert(order);
// 拿到 ShardingSphere 生成的订单ID
Long orderId = order.getId();
System.out.println("生成订单ID = " + orderId + ",用户ID = " + userId);
// 2️⃣ 再插入订单项(绑定表)
for (int j = 0; j < 3; j++) {
ProductOrderItemDO item = new ProductOrderItemDO();
item.setProductOrderId(orderId); // 关键:绑定用这个字段分片
item.setProductId(1000L + j);
item.setProductName("测试商品-" + j);
item.setBuyNum(1 + j);
item.setUserId(userId); // 必须和订单 userId 一致(用于分库)
productOrderItemMapper.insert(item);
}
}
}
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
# 分片规则回顾
当前分片规则为:
- 分库策略:
ds$->{user_id % 2} - 订单分表:
product_order_$->{id % 2} - 订单项分表:
product_order_item_$->{product_order_id % 2}
假设某一次循环中:
userId = 41
orderId = 1996197413044035585
2
则路由过程如下:
分库计算
41 % 2 = 1 → 路由到 ds11订单分表
1996197413044035585 % 2 = 1 → product_order_11订单项分表
product_order_id = 1996197413044035585 1996197413044035585 % 2 = 1 → product_order_item_11
2
最终结果:
- 订单主表:
ds1.product_order_1 - 订单项表:
ds1.product_order_item_1
绑定表的核心价值: 绑定后多张表使用同一套分库分表策略,同一笔业务数据(订单 + 明细)一定落在同一个库、同一个分片上,从而可以高效 JOIN,不需要跨库跨表 JOIN。
# 逻辑 SQL / 真实 SQL 对比
逻辑 SQL(代码中看到的):
INSERT INTO product_order
( id, out_trade_no, state, create_time, pay_amount, nickname, user_id )
VALUES ( ?, ?, ?, ?, ?, ?, ? )
2
3
开启日志(sql.show: true)后,打印的真实 SQL:
ds1 :::
INSERT INTO product_order_1
( id, out_trade_no, state, create_time, pay_amount, nickname, user_id )
VALUES
(1996197413044035585, 7256fa9d3d5b4bcfb9198bfdf7011eb4, PAY, 2025-12-03 20:38:45.433, 100.0, 自定义水平分库分表 i=0, 41)
2
3
4
5
可以验证三件事:
- 逻辑表
product_order→ 已改写为物理表product_order_1 - 数据源正确路由到了
ds1 - Snowflake 生成的 ID 已回填到实体,并参与了分表计算
# 2. 插入广播表
/**
* 插入广播表 ad_config
*/
@Test
public void testSaveAdConfig() {
for (int i = 0; i < 5; i++) {
AdConfigDO config = new AdConfigDO();
config.setConfigKey("config_key_" + i);
config.setConfigValue("config_value_" + i);
config.setType("SYSTEM");
adConfigMapper.insert(config);
System.out.println("生成 ad_config ID = " + config.getId());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 结果说明
广播表的特点是:每个库都有一份,结构和数据必须保持一致。
所以这段代码逻辑上只插入一次,但 ShardingSphere 会自动向:
ds0.ad_configds1.ad_config
各插入一条完全相同的数据。
你只维护一份逻辑数据,框架自动帮你做“全库同步”。
# 逻辑 SQL / 真实 SQL 对比
逻辑 SQL:
INSERT INTO ad_config
( id, config_key, config_value, type)
VALUES ( ?, ?, ?, ?)
2
3
真实 SQL:
ds0 ::: INSERT INTO ad_config ( id,config_key,config_value,type )
VALUES (?, ?, ?, ?) ::: [1996211802992246785, config_key_0, config_value_0, SYSTEM]
ds1 ::: INSERT INTO ad_config ( id,config_key,config_value,type )
VALUES (?, ?, ?, ?) ::: [1996211802992246785, config_key_0, config_value_0, SYSTEM]
2
3
4
5
# 二、查询数据
# 1. 广播表查询
测试案例
/**
* 查询广播表 ad_config
*/
@Test
public void testListAdConfig() {
System.out.println(adConfigMapper.listAdConfig());
}
2
3
4
5
6
7
8
# 逻辑 / 真实SQL对比
Logic SQL: select * from ad_config
Actual SQL: ds1 ::: select * from ad_config
2
说明:
- 广播表的数据在各个库中是完全一致的;
- 对于简单的单表查询,ShardingSphere 会采用 Unicast Route(单播路由):
- 只从其中一个数据源(这里是
ds1)查询一次即可 - 无需对所有库做 UNION 合并
- 只从其中一个数据源(这里是
只有在广播表参与分片表的 JOIN 等复杂场景时,才会根据主表的路由一起参与本库查询,而不是每个库都查一遍。
# 2. 绑定表查询
# 单表查询绑定表的主表
测试案例
/**
* 查询绑定表单张 product_order
*/
@Test
public void testListProductOrder() {
System.out.println(productOrderMapper.listProductOrder());
}
2
3
4
5
6
7
8
# 逻辑 / 真实SQL对比
Logic SQL: select * from product_order
Actual SQL: ds0 ::: select * from product_order_0
Actual SQL: ds0 ::: select * from product_order_1
Actual SQL: ds1 ::: select * from product_order_0
Actual SQL: ds1 ::: select * from product_order_1
2
3
4
5
6
这里要特别强调一个点:
❗️并不是因为是绑定表才全库全表查,而是因为这条 SQL 没有带任何分片条件(比如 user_id / id),无法做精准路由,所以退化成了“全路由(全库 + 全分片)”。
也就是说:
- 如果你写的是:
select * from product_order(没有 where 条件)- 那么 ShardingSphere 不知道要查哪个库、哪张分表
- 于是只好把所有库、所有分片都查一遍后再做结果合并
- 如果你写的是:
select * from product_order where user_id = ?- 那就会按照
user_id精准路由,只查 一个库 两张分表(按 id 分表决定)
- 那就会按照
绑定表只解决“多表路由一致性问题”,不影响单表是否全路由。
# 关联查询
测试案例
/**
* 查询绑定表 product_order product_order_item
*/
@Test
public void testListProductOrderDetail() {
System.out.println(productOrderMapper.listProductOrderDetail());
}
2
3
4
5
6
7
8
# 逻辑 / 真实SQL对比
Logic SQL: select * from product_order o
left join product_order_item i on o.id=i.product_order_id
Actual SQL: ds0 ::: select * from product_order_0 o
left join product_order_item_0 i on o.id=i.product_order_id
Actual SQL: ds0 ::: select * from product_order_1 o
left join product_order_item_1 i on o.id=i.product_order_id
Actual SQL: ds1 ::: select * from product_order_0 o
left join product_order_item_0 i on o.id=i.product_order_id
Actual SQL: ds1 ::: select * from product_order_1 o
left join product_order_item_1 i on o.id=i.product_order_id
2
3
4
5
6
7
8
9
10
11
12
说明两点:
- 依然因为没有带分片键条件(如
user_id、id),所以是 全库全分片路由; - 由于
product_order与product_order_item是绑定表:- 在同一个库中,
product_order_0只会跟product_order_item_0JOIN product_order_1只会跟product_order_item_1JOIN- 避免了跨分片 JOIN,这是绑定表的价值所在。
- 在同一个库中,
✅ 推荐写法: 尽量在 WHERE 中带上分片键,例如:
select * from product_order o left join product_order_item i on o.id = i.product_order_id where o.user_id = 41;1
2
3
4这样就可以精准路由到单库单分片,避免全库扫描。
# 三、修改 / 删除数据的建议
ShardingSphere 对多表 UPDATE / DELETE 支持有限,尤其是 MySQL 的
UPDATE ... JOIN ...这种 Multiple-Table 语法,在很多版本中是直接不支持的。
建议:
- 尽量使用单表 UPDATE / DELETE
- 复杂场景改为“两步走”:先查询 ID 列表,再按 ID 单表更新 / 删除。
- WHERE 条件一定要带真实分片键
- 比如:
user_id、order_id、product_order_id、id等 - 否则很容易退化为全库全分片广播更新 / 删除,存在严重风险。
- 比如:
- 绑定表可以保证:
- 当你对主表做精准路由时,
- JOIN 的从表也能路由到同一库、同一分片
- 但绑定表本身并不能“补齐分片键”,WHERE 条件里仍然需要显式带上分片键。
✅ 总结一句话:
- 查询/修改绑定表时:一定要带分片键,走精准路由;
- 修改/删除时:尽量拆成单表操作,避免使用
UPDATE ... JOIN ...这种多表 DML。
← 基础配置 核心流程与多种分片策略 →