知识卡片

分区裁剪只认分区列本身,遇到表达式就失效

普通读书笔记卡

内容

分区表对用户呈现为一张逻辑表,底层由多个物理子表构成,查询效率提升的 关键机制是”分区裁剪”(partition pruning):优化器根据WHERE条件判断 只需要扫描哪几个分区,跳过其余分区,等效于一种粗粒度的索引。但分区 裁剪只在WHERE条件直接比较分区列本身时才生效,一旦条件比较的是分区 列的某个表达式(哪怕这个表达式恰好就是建分区时用的那个函数),优化器 也无法据此裁剪——例如按YEAR(day)分区后,WHERE YEAR(day)=2010反而 不会被裁剪,必须改写成对day本身的等价范围条件才行;这和索引不能用在 “字段套了函数”的表达式上是同一类限制。除此之外还有一个更隐蔽的坑: 按范围分区时,任何NULL或非法值(含日期函数对非法输入返回NULL的情况) 都会被塞进第一个分区,这意味着一个覆盖某个具体时间段的查询,优化器 除了裁剪出目标分区,还会顺带扫描第一个分区以防漏掉NULL/异常值,如果 第一个分区本身很大,这个”顺带扫描”的代价可能不小(缓解办法是刻意留一个 空的”哨兵”首分区)。这些限制说明”分区能让查询更快”是一个有条件的承诺, 条件是WHERE子句写法要精确匹配分区裁剪能识别的形式,否则分区退化成 “逻辑上分好了、但查询照样要扫全部子表”的摆设。

参考来源

- 位置:《高性能MySQL:第3版》第7章"MySQL高级特性"7.1.4节"什么情况下 会出问题"、7.1.5节"查询优化"(源文件:_epub-src/OEBPS/Text/part0014.xhtml) - 结论依据:原文明确"MySQL只能在使用分区函数的列本身进行比较时才能 过滤分区,而不能根据表达式的值去过滤分区,即使这个表达式就是分区 函数也不行……假设按照PARTITION BY RANGE YEAR(order_date)分区, 那么所有order_date为NULL或者是一个非法值的时候,记录都会被存放到 第一个分区",直接说明分区裁剪只认列本身及NULL值导致额外扫描第一 分区的机制。 - 原始内容:MySQL只能在使用分区函数的列本身进行比较时才能过滤分区, 而不能根据表达式的值去过滤分区,即使这个表达式就是分区函数也不行。