数据库分库分表后跨库查询该怎么解决
分库分表后跨库查询的解决思路只有一条主脉络:能不让它跨库,就别让它跨库——优先用冗余字段、广播表、异构索引把查询收敛到单库,其次才是中间件归并,最后才是异步汇总和离线宽表。没有银弹,选哪条路取决于你的查询实时性要求和能接受的改造量。
分库分表后跨库查询为什么难?
结论:难在"路由不到就退化成全库扫描 + 内存归并",数据量和 offset 一大就直接把应用或中间件打爆。
单库时一条 JOIN 或 ORDER BY ... LIMIT 由 MySQL 执行器自己搞定。分库后,比如按 user_id 分成 32 个库、每库 32 张表共 1024 个分片,一条带 ORDER BY create_time LIMIT 100000, 20 的查询会被改写成 1024 条 SQL 分别下发,各返回 100020 条记录,在中间件内存里归并排序后再切掉前 100000 条。1024 × 100020 行的中间结果集,几百 MB 内存起步,深分页场景下这就是事故现场。
更糟的是"笛卡尔积":如果 A 表按 user_id 分片、B 表按 order_id 分片,两者 Join 时没有任何一条分片键能对上,中间件只能做 32×32=1024 种组合的交叉匹配。
跨库查询有哪些主流方案?
结论:按"改造代价从小到大"排序是——广播表 → 字段冗余 → 异构索引(ES / ClickHouse)→ 中间件归并 → 异步数仓,90% 的场景在前三步就能解决。
方案一:广播表(每个库都存一份全量)。 适合数据量小、变更少的字典表,比如城市表、类目表,几万行以内,用 ShardingSphere 的 broadcast-tables 配置即可,Join 时直接下推到单库执行。缺点是写放大,写一次要同步 N 个库。
方案二:字段冗余。 下单时把 user_name、shop_name 直接写进订单表,查询不用回表 Join。代价是数据一致性靠业务保证,适合"读多写少、允许最终一致"的场景,比如订单列表展示用户名,延迟几秒可接受。
方案三:异构索引。 把需要多维检索的数据用 Canal 订阅 MySQL binlog,同步到 Elasticsearch 或 ClickHouse,跨维度查询全部走它们。ES 单索引几亿文档、ClickHouse 单表十亿行级别都扛得住,这是目前"既要实时又要多维度"最主流的答案。同步延迟通常在 1 秒以内,Canal + Kafka 链路可以做到百毫秒级。
方案四:中间件归并。 ShardingSphere-Proxy 5.x 支持跨库 Join 和聚合,前提是配置了绑定表(Binding Table)——指分片规则完全一致的主表和子表,比如 t_order 和 t_order_item 都按 order_id 分片,Join 时能保证落到同一个库同一张表,不做笛卡尔积。绑定表配对了,跨库 Join 性能接近单库;没配对,就是前面说的 1024 种组合。
跨库分页和聚合怎么优化?
结论:深分页不要用 LIMIT offset, size,改用"游标分页 + 二次查询法",聚合尽量下推到 ES/ClickHouse。
游标分页(也叫 Keyset Pagination)是把 LIMIT 100000, 20 改写成 WHERE create_time > '上次最后一条的时间' ORDER BY create_time LIMIT 20,每页查询代价恒定,不受页码影响。代价是不能跳页,只能上一页/下一页。
如果必须支持跳页,用二次查询法:第一页查询时各分片只取 offset/N + size 条,拿到全局最小值后,第二轮再补查比它小的数据。这样内存开销从 O(offset × 分片数) 降到 O(offset + size × 分片数)。
聚合类查询(COUNT、SUM、GROUP BY)同理,能在 ES 里预聚合的就别回 MySQL 算。
落地时应该按什么顺序选?
结论:先问"这个查询能不能改需求",再问"能不能异步",最后才上中间件归并。
- 查询是 C 端主链路、QPS 高、实时性要求毫秒级 → 冗余字段 + 异构索引,别犹豫。
- 查询是运营后台、QPS 个位数、能接受秒级延迟 → 直接查 ES 或走离线数仓,最省事。
- 查询是内部对账、日结 → 用 Flink/Spark 每天凌晨跑批,落到汇总表。
- 只有"实时性要求高、维度固定、且分片键能对上"的查询,才考虑 ShardingSphere Proxy 跨库 Join。
最后提醒一句:跨库查询的根因通常是"分片键选错了"。如果业务上高频查询是按 merchant_id,那分片键就不该选 user_id,或者干脆做双写双分片(一份按 user、一份按 merchant)。选型阶段多花两天,能省掉后面半年的补丁。
原文链接:https://www.gj0.com/thread-192.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。