Documentation Home

8.2.1.2 范围优化

range访问方法使用单个索引来检索包含在一个或多个索引值区间内的表行的子集 。它可用于单部分或多部分索引。以下部分详细描述了如何从WHERE 子句中提取间隔。

单部分索引的范围访问方法

对于单项索引,索引取值区间可以方便地用 WHERE子句中对应的条件来表示,记为 范围条件 而不是区间”。

单部分索引的范围条件定义如下:

  • 对于BTREE和 索引,使用, , , , or运算符HASH时,关键部分与常量值的比较是范围条件 。 =<=>IN()IS NULLIS NOT NULL

  • 此外,对于索引,使用、 、 、 、 、 或 运算符BTREE时,关键部分与常量值的比较是范围条件 ,或者 如果参数 to 是不以通配符开头的常量字符串则进行比较。 ><>=<=BETWEEN!=<>LIKELIKE

  • 对于所有索引类型,多个范围条件组合ORAND形成一个范围条件。

前面描述中的 常数值”是指以下之一:

  • 来自查询字符串的常量

  • 来自同一连接 的一个const 或表的列system

  • 不相关子查询的结果

  • 完全由上述类型的子表达式组成的任何表达式

WHERE以下是子句 中带有范围条件的查询示例:

SELECT * FROM t1
  WHERE key_col > 1
  AND key_col < 10;

SELECT * FROM t1
  WHERE key_col = 1
  OR key_col IN (15,18,20);

SELECT * FROM t1
  WHERE key_col LIKE 'ab%'
  OR key_col BETWEEN 'bar' AND 'foo';

在优化器常量传播阶段,一些非常量值可能会转换为常量。

MySQL 尝试从 WHERE每个可能的索引的子句中提取范围条件。在提取过程中,丢弃不能用于构造范围条件的条件,合并产生重叠范围的条件,去除产生空范围的条件。

考虑以下语句,其中 key1是索引列 nonkey且未索引:

SELECT * FROM t1 WHERE
  (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
  (key1 < 'bar' AND nonkey = 4) OR
  (key1 < 'uux' AND key1 > 'z');

key的提取过程key1如下:

  1. 从原始WHERE条款开始:

    (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
    (key1 < 'bar' AND nonkey = 4) OR
    (key1 < 'uux' AND key1 > 'z')
  2. 删除nonkey = 4key1 LIKE '%b'因为它们不能用于范围扫描。删除它们的正确方法是将它们替换为TRUE,这样我们在进行范围扫描时就不会遗漏任何匹配的行。TRUE用产量 代替它们:

    (key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR
    (key1 < 'bar' AND TRUE) OR
    (key1 < 'uux' AND key1 > 'z')
  3. 折叠始终为真或假的条件:

    • (key1 LIKE 'abcde%' OR TRUE)永远是真的

    • (key1 < 'uux' AND key1 > 'z')总是假的

    将这些条件替换为常量会产生:

    (key1 < 'abc' AND TRUE) OR (key1 < 'bar' AND TRUE) OR (FALSE)

    删除不必要TRUE的和 FALSE常量产量:

    (key1 < 'abc') OR (key1 < 'bar')
  4. 将重叠间隔合并为一个产生用于范围扫描的最终条件:

    (key1 < 'bar')

通常(如前面的示例所示),用于范围扫描的条件比WHERE子句的限制要少。MySQL 执行额外的检查以过滤掉满足范围条件但不满足完整WHERE子句的行。

范围条件抽取算法可以处理任意深度的嵌套 AND/OR 构造,其输出不依赖于条件在 WHERE子句中出现的顺序。

MySQL 不支持 range为空间索引的访问方法合并多个范围。要解决此限制,您可以使用UNIONwith identical SELECTstatements,只是您将每个空间谓词放在不同的 SELECT.

多部分索引的范围访问方法

多部分索引的范围条件是单部分索引范围条件的扩展。多部分索引的范围条件将索引行限制在一个或多个键元组间隔内。键元组间隔是在一组键元组上定义的,使用从索引排序。

例如,考虑定义为 的多部分索引 ,以及以下按键顺序列出的键元组集: key1(key_part1, key_part2, key_part3)

key_part1  key_part2  key_part3
  NULL       1          'abc'
  NULL       1          'xyz'
  NULL       2          'foo'
   1         1          'abc'
   1         1          'xyz'
   1         2          'abc'
   2         1          'aaa'

条件key_part1 = 1定义了这个区间:

(1,-inf,-inf) <= (key_part1,key_part2,key_part3) < (1,+inf,+inf)

区间覆盖了前面数据集中的第4、5、6元组,可以被range access方法使用。

相比之下,该条件 key_part3 = 'abc'没有定义单个区间并且不能被范围访问方法使用。

以下描述更详细地说明了范围条件如何适用于多部分索引。

  • 对于HASH索引,可以使用包含相同值的每个区间。这意味着只能为以下形式的条件生成间隔:

        key_part1 cmp const1
    AND key_part2 cmp const2
    AND ...
    AND key_partN cmp constN;

    这里,const1, const2, … 是常量,cmp=, <=>, 或IS NULL比较运算符之一,条件覆盖所有索引部分。(也就是说,N 有条件,一个 N-part 索引的每个部分一个。)例如,以下是一个三部分 HASH索引的范围条件:

    key_part1 = 1 AND key_part2 IS NULL AND key_part3 = 'foo'

    有关常量的定义,请参阅 单部分索引的范围访问方法

  • 对于索引, BTREE区间可能可用于与 组合 的 条件 , 其中 AND每个 条件 使用 =、、、、、、、、、、、、 或 ( 其中 <=>IS NULL><>=<=!=<>BETWEENLIKE 'pattern''pattern' 不以通配符开头)。只要可以确定包含与条件匹配的所有行的单个键元组(或者如果 使用<> or!= 则为两个间隔),就可以使用间隔。

    只要比较运算符是 、 或 ,优化器就会尝试使用额外的关键部分来 =确定 <=>区间IS NULL。如果运算符是 >, <, >=, <=, !=, <>, BETWEEN, 或 LIKE,优化器将使用它但不再考虑更多的关键部分。对于以下表达式,优化器 =从第一次比较开始使用。它还使用 >= 来自第二次比较但不考虑进一步的关键部分并且不使用第三次比较进行区间构造:

    key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10

    单区间为:

    ('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)

    创建的间隔可能包含比初始条件更多的行。例如,前面的区间包含('foo', 11, 0)不满足原始条件的值 。

  • 如果覆盖包含在区间内的行集的条件与 组合 OR,它们将形成一个覆盖包含在其区间并集内的行集的条件。如果条件与 组合 AND,它们形成一个条件,涵盖包含在其间隔交集内的一组行。例如,对于两部分索引的这种情况:

    (key_part1 = 1 AND key_part2 < 2) OR (key_part1 > 5)

    间隔是:

    (1,-inf) < (key_part1,key_part2) < (1,2)
    (5,-inf) < (key_part1,key_part2)

    在这个例子中,第一行的间隔使用一个关键部分作为左边界,两个关键部分作为右边界。第二行的区间只用了一个关键部分。输出中的key_lenEXPLAIN指示使用的键前缀的最大长度。

    在某些情况下,key_len可能表示使用了关键部分,但这可能不是您所期望的。假设 key_part1key_part2可以是 NULL。然后该 key_len列显示以下条件的两个关键部分长度:

    key_part1 >= 1 AND key_part2 < 2

    但是,实际上,条件被转换为:

    key_part1 >= 1 AND key_part2 IS NOT NULL

有关如何执行优化以组合或消除单部分索引范围条件的间隔的说明,请参阅单部分索引的 范围访问方法。对多部分索引的范围条件执行类似的步骤。

多值比较的相等范围优化

考虑这些表达式,其中 col_name是一个索引列:

col_name IN(val1, ..., valN)
col_name = val1 OR ... OR col_name = valN

col_name如果等于多个值中的任何一个, 则每个表达式为真 。这些比较是相等范围比较(其中范围是单个值)。优化器估计读取符合条件的行以进行相等范围比较的成本如下:

  • 如果 上有唯一索引 col_name,则每个范围的行估计值为 1,因为最多一行可以具有给定值。

  • 否则,任何索引 col_name都是非唯一的,优化器可以使用深入了解索引或索引统计信息来估计每个范围的行数。

使用索引潜水,优化器在范围的每一端进行潜水,并使用范围中的行数作为估计值。例如,表达式 col_name IN (10, 20, 30)具有三个相等范围,优化器对每个范围进行两次潜水以生成行估计。每对潜水都会产生具有给定值的行数的估计值。

索引潜水提供准确的行估计,但随着表达式中比较值数量的增加,优化器需要更长的时间来生成行估计。索引统计的使用不如索引潜水准确,但允许对大值列表进行更快的行估计。

系统 eq_range_index_dive_limit 变量使您能够配置优化程序从一个行估计策略切换到另一个的值的数量。要允许使用索引潜水来比较N 相等范围,请设置 eq_range_index_dive_limitN+ 1。要禁用统计信息并始终使用索引潜水,而不管 N,请设置 eq_range_index_dive_limit 为 0。

要更新表索引统计信息以获得最佳估计,请使用 ANALYZE TABLE.

即使在本应使用索引潜水的情况下,对于满足所有这些条件的查询也会跳过它们:

  • 存在单索引FORCE INDEX索引提示。这个想法是,如果强制使用索引,则执行深入索引的额外开销不会有任何好处。

  • 该索引是非唯一的,不是 FULLTEXT索引。

  • 不存在子查询。

  • 不存在DISTINCT, GROUP BY, 或ORDER BY子句。

这些跳转条件仅适用于单表查询。对于多表查询(连接),不会跳过索引潜水。