美文网首页Mysql索引原理透析
「Mysql索引原理(十三)」索引案例2-避免多个范围条件

「Mysql索引原理(十三)」索引案例2-避免多个范围条件

作者: 源码之路 | 来源:发表于2020-06-20 18:49 被阅读0次

    什么是范围条件?

           从EXPLAIN的输出很难区分MySQL是要查询范围值,还是查询列表值。 EXPLAIN使用同样的词“ range”来描述这两种情况。例如,从type列来看, MySQL会把下面这种查询当作是“ range”类型:

    explain select id from people where id>45
    

           但是下面这条语句呢?

    explain select id from people where id in (1,4,99)
    

           从EXPLAIN的结果是无法区分这两者的,但可以从值的范围和多个等于条件来得出不同。在我们看来,第二个查询就是多个等值条件查询。

           我们不是挑剔:这两种访问效率是不同的。对于范围条件查询,MySQL无法再使用范围列后面的其他索引列了,但是对于“多个等值条件查询”则没有这个限制。

           假设我们有一个last_online列并希望通过下面的查询显示在过去几周上线过的用户:

    ...
    WHERE eye_color IN('brown','blue','hazel')
    AND hair_color IN('black','red','blonde','brown')       
    AND sex   IN('M','F')
    AND last_online >DATE_SUB(NOW(),INTERVAL 7 DAY)
    AND age BETWEEN 18 AND 25
    

           这个查询有一个问题:它有两个范围条件,last_online列和age列,MySQL可以使用last_online列索引或者age列索引,但无法同时使用它们。

           如果条件中只有 last_online而没有age,那么我们可能考虑在索引的后面加上last_online列。这里考虑如果我们无法把age字段转换为一个IN()的列表,并且仍要求对于同时有last_online和age这两个维度的范围查询的速度很快,那该怎么办?答案是,很遗憾没有一个直接的办法能够解决这个问题。但是我们能够将其中的一个范围查询转换为一个简单的等值比较。为了实现这一点,我们需要事先计算好一个active列,这个字段由定时任务来维护。当用户每次登录时,将对应值设置为1,并且将过去连续七天未曾登录的用户的值设置为0。

           这个方法可以让 MySQL使用(active, sex, country,age)索引。 active列并不是完全精确的,但是对于这类査询来说,对精度的要求也没有那么高。如果需要精确数据,可以把 last_online列放到WHERE子句,但不加入到索引中。这和本章前面通过计算URL哈希值来实现URL的快速查找类似。所以这个查询条件没法使用任何索引,但因为这个条件的过滤性不高,即使在索引中加入该列也没有太大的帮助。换个角度来说,缺乏合适的索引对该查询的影响也不明显。

           到目前为止,我们可以看到:如果用户希望同时看到活跃和不活跃的用户,可以在查询中使用IN()列表。我们已经加入了很多这样的列表,但另外一个可选的方案就只能是为不同的组合列创建单独的索引。至少需要建立如下的索引:( active,sex, country, age),(active, country,age),(sex, country,age)和( country,age)。这些索引对某个具体的查询来说可能都是更优化的,但是考虑到索引的维护和额外的空间占用的代价,这个可选方案就不是一个好策略了。

           在这个案例中,优化器的特性是影响索引策略的一个很重要的因素。如果未来版本的MySQL能够实现松散索引扫描,就能在一个索引上使用多个范围条件,那也就不需要为上面考虑的这类查询使用IN()列表了。

    相关文章

      网友评论

        本文标题:「Mysql索引原理(十三)」索引案例2-避免多个范围条件

        本文链接:https://www.haomeiwen.com/subject/eusgxktx.html