透明之下不透明
上一篇文章里那句「业务代码一行没变」,是 ShardingSphere 这类中间件最大的卖点,也是最大的误会来源。应用看到的透明,是中间件在底下做了一大堆改写换来的。一条 SQL 从进来到结果返回,实际经过五个工位:解析、路由、改写、执行、归并。这篇把这五个工位各拆开看一眼——不要求你会造轮子,但要求你能在出问题时知道去哪儿查。
工位一二三:解析、路由、改写
解析把 SQL 文本变成语法树,认出表名、WHERE 条件、ORDER BY、LIMIT 各在哪儿;路由拿分片键的值代入规则,算出这条 SQL 该去哪些分片——带 user_id = 9527 的查询被精确路由到一个库一张表;不带分片键的,路由结果是「全分片」(广播)。改写最容易出幺蛾子:逻辑 SQL 要变成物理可执行的语句,除了把 t_order 换成 t_order_2,还有两类隐蔽改写:
逻辑: select * from t_order where user_id=9527 limit 100000,10
改写: select * from t_order_2 where user_id=9527 limit 0,100010
// limit 起点被抹平:跨片分页必须重算,第 10 篇展开
逻辑: select avg(amount) from t_order_2 ...
改写: select sum(amount) as AVG_DERIVED_0, count(amount) as ...
// avg 不能跨分片求平均,先带出 sum 与 count看到执行日志里凭空多出的 AVG_DERIVED、被改大的 limit,不要慌——那是改写工位的正常输出。
工位四:并发执行
改写后的物理 SQL 发往各目标分片。精确路由只有一条 SQL、一个连接,快得像没分过片;广播路由则是几十条 SQL 并发打到几十张表——每个分片各自利用索引是快的,但整体耗时取决于最慢的那个分片,而且连接数、内存占用瞬间翻几十倍。这也是为什么「尽量带分片键」是分片系统的第一铁律:它不只是快慢问题,是资源占用方式的问题。
工位五:结果归并
各分片的返回集要拼成一个结果集还给应用,归并策略按场景分三种。遍历归并:单分片结果直接拼接,最便宜;排序归并:ORDER BY 的查询,各分片返回的是各自有序的流,用多路归并(每次从各流头取最小)拼出全局有序,内存友好;聚合归并:count 与 sum 把各分片的值相加,max 与 min 在流里选极值,avg 则靠改写阶段带出的 sum 与 count 在归并时重新相除——直接对各分片的 avg 求平均是错的,这就是改写阶段那个诡计的原因。分组聚合(GROUP BY)最贵:要先按分组键归并再聚合,分片多了内存与耗时都显著上涨。
理解流水线的实用价值
把五步流水线装进脑子,排查问题就有了地图:数据写错了地方——查路由(分片键取值与规则);查询莫名变慢——查路由结果是不是广播了;聚合结果不对——查改写与归并(尤其 avg);分页越翻越慢——那是 limit 改写与归并的固有代价,第 10 篇给解法。中间件的透明是承诺,不是魔法——知道玻璃后面在干活的人是谁,玻璃碎了才知道找谁。
流水线的两个工位——聚合归并与分页归并——代价最重,值得各花一篇展开。下一篇先讲跨分片聚合与排序的归并账本。
评论 (0)