知识卡片
分区裁剪只认分区列本身,遇到表达式就失效
内容
分区表对用户呈现为一张逻辑表,底层由多个物理子表构成,查询效率提升的
关键机制是”分区裁剪”(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只能在使用分区函数的列本身进行比较时才能过滤分区,
而不能根据表达式的值去过滤分区,即使这个表达式就是分区函数也不行。