• 在除了前面介绍的常见index merge的案例(Index Merge Union Access Algorithm)之外,还有一类很少见也比较特殊的index merge,多个索引扫描后进行交集,即 Index Merge Intersection。这类执行计划比较少见(因为MySQL需要ROR的原因),但是,在合适的场景使用,效率仍然会有很大的提示,本文将看看MySQL优化器如何评估和选择此类执行计划。MySQL手册对此只是三言两语简单介绍了一下,这里做个较为详细的说明。

    这类执行计划完整名称应该是:The Index Merge Intersection Access Algorithm,下文简称Intersection

    1. 为什么需要考虑Intersection

    考虑如下查询:

    SELECT COUNT(*) FROM t1 WHERE key1=1 AND key2=1;

    优化器可以考虑使用索引key1或者key2进行REF/Range访问,如果使用key1,那么key2=1则作为过滤条件。另外,优化器还会考虑使用Intersection,即同时使用索引key1和key2。这样做可能的好处是:

    (a) 如果两次索引扫描后做交集,如果最后ROWID很少,则回表次数大大减少

    (b) 如果扫描这两个索引能是覆盖扫描的话,则无需回表 (more…)

  • 前面以案例的形式介绍了什么是index merge,以及它的使用场景。本文将介绍index merge实现的主要数据结构以及MySQL如何评估index merge的成本。在开始本文之前,需要先理解Range访问相关的数据结构介绍:SEL_ARG结构SEL_TREE结构(more…)

  • 还有五天过年

    最近博客都很枯燥,总写些不着边际的技术文章,虽然自己不觉得,不过还是枯燥之极。生活枯燥了吗?没有。大概是忙了,心理牵挂多了。今年是2月9号过年,去年好像是1月几号,为什么不同的年份农历年和公历年差这么多?是啊,为什么?好吧,本文就八卦一下这个吧。

    本文尝试说明白:什么是农历?农历一年到底有多长?

    1. 公历年

    这个相对简单。地球有公转,即绕太阳转,绕太阳一周,就是我们说的一年,也叫回归年或者太阳年。准确的说是365.2421990741天(参考),所以一般一年就是365天。

    但如果就按照365天过,每隔4年,我们的”一年”就会与地球公转的周期相差约一天(4*0.2421990741),所以就有”闰年”的概念,即每隔四年我们就让2月多一天,也就是全年是366天。借此保障我们的”一年”能够准确的描述地球的公转。但如果更精确的计算,你还会发现,每四年一个闰年还会有问题,因为4*0.2421990741并不是完整的一天的,比一天少了0.0312037036天。

    所以如果按照上面计算,每四年我们的”一年”就比地球公转周期多0.0312037036天。那么每隔100年,也就相差0.78(0.0312037036*25)天。于是,我们又规定,每隔100年,即使被四整除,也不闰年了。是的,还没完!!要是按照这样算,每过100年我们的”一年”仍然与地球的公转周期相差0.22天,于是我们又规定,每隔400年我们还是在闰一次。所以每隔400年,我们的”一年”与又比地球公转周期多了0.12天,是的,没玩了…各个科学界的大佬们也觉得,后面的事情不要再操心了。按照这样的计算要再过3200年,我们的”一年”才比地球的公转周期多整整一天,后面的事情让子孙后代操心吧…

    总结一下,公历年正常是365天一年,闰年是366天。如果能被4整数,但不能被100整除是闰年,除此,如果恰好被400整除也是闰年。

    2 农历年

    农历年是根据月球绕地球会合周期来计算的,每十二个月盈月亏为一个农历年;但是,农历年又尝试通过闰月的方式去近似回归年,正是这个”近似”的粒度(按月)很粗,所以,让每年的春节日期与公历相差很多。

    2.1 农历月

    开始介绍农历年前,我们先看看农历月。农历月是精确的按照月亮的盈亏(望朔)来制定的。这与回归年没有什么关系。月亮绕地球的会和周期(从地球的角度观察到的天球上原来的位置所需要的时间)是29.530589天。所以农历月就是29天或者30天,至于哪个月30天,那个月29天,这依赖于精确的天文观察,规定是:”月初必须是朔日”,实际中连续大月或者小月的情况很多。

    所以农历八月十五太阳总是圆的。公历就没有哪天说月亮就一定是圆的。 (more…)

  • 在MySQL官方手册上,关于index merge的介绍非常非常少。甚至还有不少误导的地方,这次把5.1版本关于此类优化处理的代码细看了一遍,以案例的方式介绍了各种实用index merge访问类型的SQL。后续的还会继续介绍index merge实现的主要数据结构,以及成本评估。

    1. 什么是index merge

    MySQL优化器如果发现可以使用多个索引查找后的交集/并集定位数据,那么MySQL优化器就会尝试index merge这类访问方式。index merge主要分为两大类,多个索引交集访问(intersections),多个索引并集访问,当然这两类还可以组合出更为复杂的方式,例如多个交集后做并集。

    1.1 index merge的限制:range优先

    MySQL在5.6.7之前,使用index merge有一个重要的前提条件:没有range可以使用。这个限制降低了MySQL index merge可以使用的场景。理想状态是同时评估成本后然后做出选择。因为这个限制,就有了下面这个已知的bad case(参考):

    SELECT * FROM t1 WHERE (goodkey1 < 10 OR goodkey2 < 20) AND badkey < 30;

    优化器可以选择使用goodkey1和goodkey2做index merge,也可以使用badkey做range。因为上面的原则,无论goodkey1和goodkey2的选择度如何,MySQL都只会考虑range,而不会使用index merge的访问方式。这是一个悲剧…(5.6.7版本针对此有修复) (more…)

  • 前文着重介绍了MySQL的WHERE条件如何针对单个索引构造对应的SEL_ARG结构,本文是一个补充,将简单介绍多个索引对应的SEL_TREE结构。

    对于一个完整的WHERE条件,MySQL会遍历所有可以使用的索引,逐一构造其对应的SEL_ARG结构,所有的SEL_ARG结构以指针数组的形式存放在SEL_TREE->keys中。如果对应索引无法构造SEL_ARG,那么对应的指针为空。 (more…)

  • 在看MySQL优化器代码过程中,这应该是相对较简单/代码较清晰的部分了。MySQL优化器有两个自由度:单表访问方式,多表顺序选择。前文已经介绍过MySQL单表访问的一些考量(ref/range等),本文将介绍JOIN在顺序选择上的复杂度分析。

    当有多个表需要JOIN的时候,MySQL首先会处理两类特殊情况,一个是常数表,一个是由于外连接导致顺序依赖关系。前者总是放在关联的最前面,后者会在遍历的时候考虑。本文将忽略上面两点,从较宏观角度看JOIN顺序选择时候的复杂度。 (more…)