分库分表查询优化的核心是避免全库扫描、减少跨节点通信、让查询落在单分片上;需带分片键等值条件精准路由,规避跨库join,聚合查询应下推计算并归并,分页改用游标方式。

分库分表后查询变慢、跨库关联困难、聚合统计不准——这些问题不是架构设计的终点,而是查询优化的起点。核心在于:避免全库扫描、减少跨节点通信、让查询尽可能落在单分片上。
精准路由:让sql命中目标分片
分库分表失效的最常见原因,是SQL中缺失分片键(如 user_id、order_no),导致中间件无法定位数据,只能广播到所有库表执行,性能断崖式下跌。
- 查询必须带分片键条件,且为等值查询(支持:WHERE user_id = 123;不支持:WHERE user_id > 100 OR LIKE ‘%abc’)
- 联合分片时(如按 user_id + order_date),WHERE 条件需覆盖最左分片键,否则无法路由
- 应用层生成SQL前,先根据业务参数计算目标分片(如用雪花ID解析出分库分表号),再拼接具体库表名直连查询,绕过代理层开销
规避跨库JOIN:用冗余、异步或应用层组装
ShardingSphere 或 MyCat 等中间件对跨库 JOIN 支持有限,性能差且易出错。生产环境应主动规避。
- 字段冗余:将高频关联字段(如用户昵称、商品类目)冗余到订单表,用空间换时间
- 应用层两段查询:先查主表获取分片键集合(如一批 order_id),再根据分片规则分组,批量并行查关联表,最后在内存合并
- 异步写宽表:通过 Binlog 订阅+实时计算(flink/Canal),将订单+用户+商品信息写入 elasticsearch 或 Doris 宽表,查报表、搜索类需求走宽表
聚合类查询优化:下推计算 + 分布式归并
count/SUM/AVG/GROUP BY 在分库分表下不能直接使用,需拆解为“分片执行 → 中间件归并”两阶段。
- 简单 COUNT(*):中间件自动下发 COUNT 到各分片,汇总结果(注意:无 WHERE 条件时仍需扫全部分片)
- 带 GROUP BY 的聚合:确保分组字段是分片键(如 GROUP BY user_id),否则中间件需拉取全部数据在内存归并,内存和网络压力大
- 高基数聚合(如按日期统计):提前建好按天/月的汇总表(如 order_daily_stat),写入时实时更新,查时直取,避免现场计算
实战案例:电商订单分页查询卡顿优化
某平台订单表按 user_id 分 8 库 16 表,原接口按 create_time 倒序分页(PageHelper),响应超 5s。
- 问题定位:create_time 非分片键,分页需跨全部 128 张表排序取 LIMIT,产生大量临时表和网络传输
- 改造方案: ① 改用基于游标的分页:前端传上一页最后的 create_time + order_id 作为查询条件,WHERE create_time ② 建立二级索引表:单独一张 time_order_index 表,按 create_time 分片,记录 (create_time, order_id, user_id),查最新N条时先查索引表获 user_id 列表,再按 user_id 并行查主表
③ 冷热分离:近3个月订单留分库分表,历史订单归档至单库只读表,大幅缩小查询范围