单部分索引的范围访问方法
单部分索引的范围条件的定义如下:
- 对于这两种
BTREE
和HASH
索引,使用时具有恒定值的关键部分的比较是一个范围条件 = <=>,in(),is null or,is not null运营商。 - 另外,对于
BTREE
索引,使用时具有恒定值的关键部分的比较是一个范围条件 >,<,>=,<=,between,!=,<>运营商,或者like比较,如果该参数 like是一个常数字符串不与通配符开始。 - 对于所有索引类型,多个范围条件与范围条件组合 or 或
AND
形成范围条件。
以下是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');
密钥的提取过程key1
如下:
- 从原始
WHERE
条款开始:
(key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
(key1 < 'bar' AND nonkey = 4) OR
(key1 < 'uux' AND key1 > 'z')
- 删除
nonkey = 4
并key1 LIKE '%b'
因为它们不能用于范围扫描。删除它们的正确方法是用它们替换它们TRUE
,这样我们在进行范围扫描时不会错过任何匹配的行。用TRUE
产量替换它们:
(key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR
(key1 < 'bar' AND TRUE) OR
(key1 < 'uux' AND key1 > 'z')
- 折叠条件始终为真或假:
-
(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')
- 将重叠间隔组合成一个会产生用于范围扫描的最终条件:
(key1 < 'bar')
通常(并且如前面的示例所示),用于范围扫描的条件比该WHERE
子句的限制性更小。MySQL执行额外的检查以过滤掉满足范围条件但不满足完整WHERE
子句的行。
范围条件提取算法可以处理 任意深度的嵌套 and/or构造,其输出不依赖于条件在WHERE
子句中出现的顺序 。
多部分索引的范围访问方法
以下描述更详细地说明了范围条件如何适用于多部分索引。
- 对于
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'
例子:
key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10
单个间隔是:
('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)
例子:
(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_part1 >= 1 AND key_part2 < 2
但是,事实上,条件转换为:
key_part1 >= 1 AND key_part2 IS NOT NULL
多值比较的等价范围优化
col_name IN(val1, ..., valN)
col_name = val1 OR ... OR col_name = valN
在MySQL 8.0中,对于满足所有这些条件的查询,可以进行索引潜水跳过:
- 查询用于单个表,而不是多个表的连接。
- 存在单索引
FORCE INDEX
索引提示。我们的想法是,如果强制使用索引,那么从潜在的索引中获取额外开销就无法获得任何好处。 - 该索引不是唯一的而不是
FULLTEXT
索引。 - 没有子查询。
- 不
DISTINCT
,GROUP BY
或ORDER BY
条款存在。
跳过扫描范围访问方法
CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRIMARY KEY(f1, f2));
INSERT INTO t1 VALUES
(1,1), (1,2), (1,3), (1,4), (1,5),
(2,1), (2,2), (2,3), (2,4), (2,5);
INSERT INTO t1 SELECT f1, f2 + 5 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 10 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 20 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 40 FROM t1;
ANALYZE TABLE t1;
EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;
对于前面显示的数据集,算法的运行方式如下:
- 获取第一个关键部分(
f1 = 1
)的第一个不同值。 - 根据第一个和第二个关键部分构造范围(
f1 = 1 AND f2 > 40
)。 - 执行范围扫描。
- 获取第一个关键部分(
f1 = 2
)的下一个不同值。 - 根据第一个和第二个关键部分构造范围(
f1 = 2 AND f2 > 40
)。 - 执行范围扫描。
行构造函数表达式的范围优化
优化器能够将范围扫描访问方法应用于此表单的查询:
SELECT ... FROM t1 WHERE ( col_1, col_2 ) IN (( 'a', 'b' ), ( 'c', 'd' ));
优化为:
SELECT ... FROM t1 WHERE ( col_1 = 'a' AND col_2 = 'b' )
OR ( col_1 = 'c' AND col_2 = 'd' );
要使优化器使用范围扫描,查询必须满足以下条件:
- 只使用
IN()
谓词,而不是NOT IN()
。 - 在
IN()
谓词的左侧 ,行构造函数仅包含列引用。 - 在
IN()
谓词的右侧 ,行构造函数仅包含运行时常量,这些常量是在执行期间绑定到常量的文字或本地列引用。 - 在
IN()
谓词的右侧 ,有多个行构造函数。
限制内存使用范围优化
要控制范围优化程序可用的内存,请使用 range_optimizer_max_mem_size系统变量:
- 值为0表示“ 无限制。”
- 值大于0时,优化程序会在考虑范围访问方法时跟踪消耗的内存。如果要超过指定的限制,则放弃范围访问方法,并考虑其他方法,包括全表扫描。这可能不太理想。如果发生这种情况,会发生以下警告(
N
当前 range_optimizer_max_mem_size值在哪里 ):
要估计处理范围表达式所需的内存量,请使用以下准则:
- 对于诸如以下的简单查询,其中有一个候选键用于范围访问方法,每个谓词组合OR 使用大约230个字节:
SELECT COUNT(*) FROM t
WHERE a=1 OR a=2 OR a=3 OR .. . a=N;
- 类似地,对于诸如以下的查询,每个谓词组合AND使用大约125个字节:
SELECT COUNT(*) FROM t
WHERE a=1 AND b=1 AND c=1 ... N;
- 对于带in()谓词的查询:
SELECT COUNT(*) FROM t
WHERE a IN (1,2, ..., M) AND b IN (1,2, ..., N);
in()
列表 中的每个文字值都 计为一个谓词组合or。如果有两个in() 列表,则组合的谓词 or数是每个列表中文字值的数量的乘积。因此,与or前一种情况相结合的谓词数 是 M
× N
。
文章持续更新,转发表明出处,方便更新!