分布式查询规划负责把一条全局 SQL,拆成"能在各节点独立执行的局部子查询 + 一台汇总器归并结果",并估算每一步的网络成本。本节走一遍"全局→拆分→本地→汇总"的完整流程,讲清规划器的输出是什么,以及一个糟糕的拆分怎么毁掉性能。
阅读完本节,你应当能够:
用户一句"统计每个用户的订单总额 TOP 10"。若数据分布在一百台节点,这句 SQL 对分布式优化器来说是个大难题:订单按用户 id 哈希分布着,"每个用户的前十"意味着得把所有节点都问一遍、再全网归并出真正的 TOP 10。规划器要干的事,就是把这句"全局视角"的 SQL 翻译成一份"给每个节点的施工单",并安排一台设备把各家答案拼回完整结果。
全局计划是"面试时给考官看的大纲":结合数据分布(哪个分片在哪个节点)与元数据,把它拆成 ① 哪些子查询送到哪些节点 ② 谁来归并。局部计划是每个节点拿到自己的那块后的执行细节:本节点怎么扫、怎么过滤、怎么排序。规划器不是想当然拍板,它要基于"分片键、表大小、统计信息"来估算"每步要跨几次网络、搬多少数据",从而挑出最省的一种拆法。
假设我们要算"全网订单货值十佳用户"。规划器会这么拆:
1 给每个节点发子查询: 各节点只在本地算"本分片货值前10的用户" (因为全网前10一定躺在这些局部前10里,但局部前10不超过每个节点的top) 2 各节点把本地的 top10 上报给汇总节点 3 汇总节点把 N 个 top10 合并(至多 N×10 条),再做一次排序,取全球前10 ——注意这里没把全表搬上来,只搬了 N×10 条,这才是省的地方
这个示例里最值钱的优化是第 1 步的"先各算局部 top10"——它让网络只搬"候选的前十",而不是"每个节点的全部数据"。这正是查询规划里"能先在源头做掉的计算,就先在源头做掉"这条准则的体现。
识别烂拆分的标志,无非三条:一是把不必要的整片数据搬上网,本可本地过滤的先全网汇总——看计划里有没有"全表搬移"剧就基本亮了;二是把聚合放到全网才做,本应先在各节点预聚合的 topN/求和却全部拉回汇总器;三是随心所欲地广播,能按分区键定位点路由的不去定位,硬是把一条单点查询放大成全网格卷。如果你的分布式库某个慢查询,去 EXPLAIN 一下,多半能在这些"该本地却跨网"的点上找到病根。
当规划器在若干种拆法之间挑一个时,它其实是在对同一条 SQL 同时做三笔估算,而不是凭感觉。第一笔是搬运量——这条链路一共要跨几次节点、每次搬多少行多少列,单位通常按字节算;第二笔是本地成本——每个节点内部的扫描、排序、聚合大概要花多少 CPU 与 IO;第三笔是执行顺序——先过滤还是先联表,会不会因为顺序错导致中间结果暴涨。用一组假想数感受一下:一条"统计华南区近 30 天每人订单额前 20"的查询,若规划器把"华南区过滤"留在本地做,跨网上传的只有'命中区间的少数行',可能只有几百 KB;若它误把过滤放在全网汇总后才做,那全网订单(可能几个 GB)先被拉到汇总器才挡,无论是传输还是排序都是灾难级开销。同一个 SQL、两种拆法,性能差出两个数量级,差的完全不在 CPU,而在规划器有没有把"该算的账"算对。 这就是为什么凡是大厂分布式库,几乎都要维护一份尽量新鲜的统计信息(表大小、直方图、分片分布)喂给规划器——没有它,规划器就是瞎猜,算账也白算。
看完这些,你手上应该有一套能当场用的自查口诀,专治"这条 SQL 是不是没拆好":一问是否点路由能定——能按分区键直发的,别广播;二问过滤是否下推——WHERE 该在源头筛掉的,别等到全网才筛;三问聚合是否预聚合——topN 该各片先算一遍的,别全拉回汇总器才算;四问排序是否错位——要全局有序的,看各片那步是不是在拖不必要的量。把 EXPLAIN 输出的执行计划对着这四问过一遍,绝大多数慢查询的"拆错了"病根都能一眼揪出来。这也是你从"会看概念"走向"会调优"的一道关键台阶——看懂一份执行计划,比背十条优化口诀都顶用。
规划的内容不少,但真正值得你记住的其实就那么一句:"能先在源头算掉的,就先在源头算掉;能不搬的,就别搬。" 分布式查询规划的所有技巧,都是这句话的展开。
顺着想下去,规划器真正值钱的地方,不在它懂多少花哨算法,而在于它会拿着分片信息和统计,去"估"每一步要跨几次网、搬多少行——这套估算能力,才是好规划和烂规划差出数量级的根子。同样一句 SQL,规划器算出"A 拆法只搬几百 KB、B 拆法要搬几个 GB",于是它选 A,就这么简单。
作为会用分布式库的人,你能带走的行动,是把这句话送进每一次查看执行计划(EXPLAIN)的过程里:看到计划里哪一步"本可在源头做、却拖到了全网",就盯住它。这一盯,往往就把一条慢查询的病根抓住了——不是靠背参数,而是靠理解"规划在替你做搬与不搬的算术"。
规划定了"谁在哪儿算",可"到底搬多少、怎么让数据更少动"才是省钱的深水区。下一节 7.2 讲数据传输与下推优化——谓词下推和列裁剪,怎么在源头上就把该搬的卡住。