大家好,我是工藤学编程 | 一个正在努力学习的小博主,期待你的关注 |
---|---|
实战代码系列最新文章 | C++实现图书管理系统(Qt C++ GUI界面版) |
SpringBoot实战系列 | 【SpringBoot实战系列】Sharding-Jdbc实现分库分表到分布式ID生成器Snowflake自定义wrokId实战 |
环境搭建大集合 | 环境搭建大集合(持续更新) |
分库分表 | 分库分表之实战-sharding-JDBC绑定表配置实战 |
前情摘要:
1、数据库性能优化
2、分库分表之优缺点分析
3、分库分表之数据库分片分类
4、分库分表之策略
5、分库分表技术栈讲解-Sharding-JDBC
6、分库分表下的 ID 冲突问题与雪花算法讲解
7、分库分表之实战-sharding-JDBC
8、分库分表之实战-sharding-JDBC广播表
9、分库分表之实战-sharding-JDBC水平分库+水平分表配置实战
10、分库分表之实战-sharding-JDBC绑定表配置实战
【亲测宝藏】发现一个让 AI 学习秒变轻松的神站!不用啃高数、不用怕编程,高中生都能看懂的人工智能教程来啦!
点击跳转,和 thousands of 小伙伴一起用快乐学习法征服 AI,说不定下一个开发出爆款 AI 程序的就是你!
在水平分库+分表架构中,CRUD操作的路由逻辑直接影响系统性能与数据准确性。本文聚焦查询和删除操作,通过实战案例解析「有分片键」和「无分片键」场景下的路由规则,结合Sharding-Jdbc的实现原理,总结高效操作的最佳实践。
先明确本文的分库分表规则(以订单表product_order
为例):
user_id
取模(2个库:ds0
、ds1
)id
取模(每个库分2张表:t0
、t1
)ds${user_id % 2}.product_order_${id % 2}
核心概念:
user_id
为分库键,order_id
为分表键,统称「分片键」)查询操作的路由逻辑是分库分表中最常接触的场景,也是性能优化的关键。
当查询条件中包含分库键或分表键时,Sharding-Jdbc会通过「标准路由」精准定位目标库表,避免全扫描。
在DbTest中新增测试函数
@Test
public void testPartitionKeyQuery(){
List<ProductOrderDO> list = productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id", 1942559776326320130L));
}
结果只会发出两条sql
路由过程:
id=1942559776326320130 % 2 = 0
→ 目标表为product_order_t0
user_id
未指定,需扫描所有分库的product_order_t0
ds0.product_order_t0
、ds1.product_order_t0
各查1次在DbTest中新增测试函数(同时包含分库键
user_id
和分表键order_id
):
@Test
public void testTwoPartitionKeyQuery(){
List<ProductOrderDO> list = productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id", 1942559776326320130L)
.eq("user_id", 204402002533403893L));
}
结果只会发出1条sql
路由过程:
user_id=204402002533403893 % 2 = 1
→ 目标库ds1
→ 目标表
product_order_t0`ds1.product_order_t0
(1次操作,最优路由)在DbTest中新增测试函数(分表键
id
用IN
查询):
@Test
public void testPartitionKeyInQuery(){
List<ProductOrderDO> list = productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().in("id", Arrays.asList(1942559776326320130L, 1942559779014868995L))
);
}
执行结果
路由过程:
order_id=1942559776326320130 % 2 = 0
→ 表product_order_t0
order_id=1942559779014868995 % 2 = 1
→ 表product_order_t1
user_id
未指定,需扫描所有分库的t0
和t1
ds0.t0
、ds0.t1
、ds1.t0
、ds1.t1
(4次操作,按分表键拆分后路由)当查询条件中不包含任何分片键时,Sharding-Jdbc会执行「全库表路由」,即扫描所有分库分表。
在DbTest中新增测试函数(仅按非分片键
out_trade_no
查询):
@Test
public void testNoPartitionKeyQuery(){
List<ProductOrderDO> list = productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", "70bd5220-1c9c-4e3e-b418-85481f60")
);
}
路由过程:
user_id
和id
,无法定位分库分表ds0.t0
、ds0.t1
、ds1.t0
、ds1.t1
),并合并结果问题与风险:
out_trade_no=70bd5220-1c9c-4e3e-b418-85481f60
的记录),需业务层保证逻辑唯一删除操作的路由逻辑与查询基本一致,但需额外注意数据安全(尤其是无分片键时)。
在DbTest中新增测试函数(按分库键+分表键删除):
@Test
public void testTwoPartitionKeyDelete(){
productOrderMapper.delete(new QueryWrapper<ProductOrderDO>().eq("id", 1942559776326320130L)
.eq("user_id", 204402002533403893L));
}
路由过程:
ds1.product_order_t0
(1次删除,精准高效)在DbTest中新增测试函数(按分表键批量删除):
@Test
public void testPartitionKeyInDelete(){
productOrderMapper.delete(new QueryWrapper<ProductOrderDO>().in("id", Arrays.asList(1942559776326320130L, 1942559779014868995L))
);
}
路由过程:
1942559776326320130→t0
、1942559779014868995→t1
,但无分库键user_id
ds0.t0
、ds0.t1
、ds1.t0
、ds1.t1
中符合条件的记录(4次操作)在DbTest中新增测试函数:
@Test
public void testNoPartitionKeyDelete(){
productOrderMapper.delete(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", "70bd5220-1c9c-4e3e-b418-85481f60")
);
}
路由过程:
LIMIT
限制条数)Sharding-Jdbc的路由逻辑基于「分片键提取→分片算法计算→目标库表定位」三步:
WHERE
条件中解析分库键(如user_id
)和分表键(如order_id
)IN
条件IN
中的值不宜过多(建议≤10个),否则会触发多表路由,性能接近全扫描SELECT COUNT(*)
预估数据量,确认后再删除LIMIT
限制单次删除条数:DELETE FROM product_order WHERE status=3 LIMIT 1000;
id
),避免单表内全扫描WHERE user_id=1 AND out_trade_no=1
(先定位库,再查订单号)user_id
批量删除)水平分库分表后的查询和删除操作,核心是对分片键的依赖程度:
实际开发中,应通过业务设计强制分片键的使用(如API接口必须传入user_id
或order_id
),同时结合监控工具(如Sharding-Jdbc的SQL日志)跟踪路由情况,及时优化不合理的操作。
觉得有用请点赞收藏!
如果有相关问题,欢迎评论区留言讨论~