连载中 9/20

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

2026-07-22 · 3542 阅读 · 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)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞