文章出处:http://dev.mysql.com/doc/refman/5.6/en/range-optimization.html (翻译的有纰漏的地方,请见谅)
单列索引的范围查询


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索引的提取过程如下:
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=4和Key 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)
(key1 < 'abc') OR (key1 < 'bar')4、组合重叠的区域到一个区域,最终被用来范围查询的条件如下:(key1 < 'bar')
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
cmp
const1
AND key_part2
cmp
const2
AND ...
AND key_partN
cmp
constN
;
=
, <=>
, 或者 IS NULL比较操作符,
条件覆盖了所有的索引列,(这就是说,有N个条件,每一个都对应N列索引的一个列),举例来说,下面的就是一个3列的HASH索引的范围条件。
=
, <=>
, IS NULL
, >
, <
, >=
, <=
, !=
, <>
, BETWEEN
, 或者 LIKE 'pattern
'(
但是模式不能以通配符开始)等符号来比较一个索引列和一个常量值。只要能通过一个单独的列元组能决定一个完整的行记录,而这个行记录匹配这个条件(或者两个区间如果<>或者!=使用),这个区间就会使用。
当比较操作符是
=
, <=>
, 或者 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_len列来说明使用的键前缀的最大长度。在某种程度上 ,key_len列会说明使用的键列,但是可能不是你想要的。假设key_part1和key_part2有可能为NULL,那么在下面这种情况下,key_len列会显示两个键的长度:
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
如果col_name和括号中的任何一个值相等,那么这个表达式就为真,这些都是等值范围比较(“范围”是个单值),优化器使用以下方法来估算读取满足条件的行记录所付出的的代价:1、如果col_name是个唯一索引列,那么每个范围的估算都是1,因为对于给定值,最多只有一个行记录满足条件;
col_name
IN (10, 20, 30)
有三个等值的范围,优化器会对每个范围进行两次dives来估算,每一对的dives产生给定值的相应的行记录数的估算。
当优化器需要从一个估算策略转换到另一个时,可以使用 eq_range_index_dive_limit
这个系统变量进行配置。如果禁用索引特征,总是使用index dives ,可以设置eq_range_index_dive_limit
为0。如果允许使用比较的index dives增长到N 等值范围,可以设置eq_range_index_dive_limit
为N+1;
eq_range_index_dive_limit
是MySQL5.6中的变量,在5.6.5之前,优化器一直使用index dives,这等价于eq_range_index_dive_limit
= 0;
ANALYZE TABLE
.
,更新索引统计特征。