连载中 9/20

跨分片查询(上):聚合与排序的归并代价

2026-07-22 · 3537 阅读 · 0 评论 · 0 赞

带与不带,是两种人生

分片系统里同一张逻辑表的两个查询,命运天差地别。where user_id=9527 走精确路由,一个库一张表,单表索引伺候,快得像从没分过片;where status=0 order by create_time desc 不带分片键,被广播到 8 库 32 表,每个分片各自执行、各自排序,再由中间件把 32 份结果归并成一份。聚合与排序,是归并开销最重的两类查询——这篇把它们的账本逐项算清。

count 与 sum:最老实的归并

count(*)sum(amount) 的归并语义是可分解的:全局计数等于各分片计数之和,全局求和等于各分片求和之和。中间件把逻辑 SQL 广播下去,每个分片返回一个数字,加法归并完事。正确性无忧,但代价仍在:32 个分片都要完整扫描自己的数据(或索引),整体耗时取决于最慢的分片。count 类查询的优化老三样在分片下依旧成立——按条件建索引、避免无谓的全表 count,外加一条分片专属的:精确的全局 count 高频使用时,别实时算,用计数器或统计表异步维护

avg:归并里的头号陷阱

avg 是唯一「数学上不可直接分解」的常用聚合:全局平均值不等于各分片平均值的平均。分片 A 一亿行均值 100,分片 B 十行均值 1000,直接对两个 avg 求平均得 550,而真实的全局均值约等于 100——差出五倍。正确做法是改写阶段就把 avg 拆成 sum 与 count,归并时拿全局 sum 除以全局 count(第 8 篇的改写日志里那两个 AVG_DERIVED 就是干这个的)。成熟中间件会自动处理,要警惕的是自己手写的「分片循环加求平均」——那是真实的线上 bug 温床

ORDER BY:全局有序怎么拼

广播查询里带 ORDER BY,中间件不会把 32 份结果拉回内存重新排——那样内存与耗时都爆炸。它的做法是多路归并:每个分片返回的本来就是各自有序的结果流(单表有索引时甚至有序读盘),归并器同时盯着 32 个流的队头,每次挑出全局最小的输出,流式推进直到凑够结果。内存友好,但有一个隐性代价:32 个连接、32 个游标要同时打开并维持到归并结束——连接池瞬间被占满的「连接风暴」,很多就是跨片排序查询堆出来的。带分片键的查询没有这个问题,这再次把铁律钉牢。

GROUP BY 与工程取舍

GROUP BY 是最重的一档:先按分组键把各分片的行重新组织,再在归并层聚合——分组基数越大,归并的内存与耗时越失控。分片数据库不适合做实时 OLAP,大范围 group by、多维统计、报表,请走另一条路:T+1 离线汇总到数仓,或把数据同步进专门的检索/分析引擎(ES、ClickHouse 这类)。工程上的取舍口诀:在线查询尽量带分片键;跨片聚合能异步就异步,能预计算就预计算;实在要实时的,让专门的引擎去扛

聚合与排序讲完,归并账本还剩最贵的一页——分页与 JOIN。翻到第 100 页为什么慢出天际?三张表怎么 JOIN 才不散架?下一篇收掉跨分片查询的下半场。

503

10 年全栈工程师 · 503咖啡馆主理人

#跨分片查询#聚合归并#avg陷阱#多路归并#GROUP BY

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞