虽然上一句中的order by用到了索引但是没有计算到key_len中!
范围访问方法使用一个索引来检索包含一个或多个索引间隔中的表行的子集。它可以使用索引中的一列或者多列,以下各节描述了优化器使用范围访问的条件
对于一个单列索引,索引值间隔可以方便地由 WHERE 条件中的相应条件表示,表示为范围条件而不是 intervals 。
上述的 常量 指以下情况之一:
以下是在 WHERE 子句中具有范围条件的查询示例
一些非常量可能会在优化器传播阶段转换为常量
MySQL对于每个可能使用的索引,尝试从 WHERE 子句中提取范围条件。在提取过程中,不能用于构建条件范围的条件被删除,产生重复范围的条件被合并,产生空范围的条件被删除。
假设有以下语句, key1 是一个被索引的列,而 nonkey 没有索引
提取 key1 索引的过程如下:
通常,范围扫描使用的条件比 WHERE 子句中的限制要少()。MySQL执行额外的检查来过滤满足范围条件但是不完全满足 WHERE 子句的行。
范围条件提取算法可以处理任意深度嵌套的 AND/OR 构造,并且它的输出不取决于条件在 WHERE 子句中出现的顺序
MySQL不支持为空间索引的 range 访问合并多个范围。要解决此限制,可以在相同的 SELECT 语句中使用 UNION 语句,将每个空间谓词放在不同的 SELECT 中。
多列索引的范围条件是单列索引的扩展,多列索引的范围条件将索引行限制在一个或多个索引元组的间隔中。索引元组间隔是一个按照索引顺序的,索引元组的集合。
假设有一个多列索引 key1(key_part1,key_part2,key_part3) ,按照索引顺序,具有以下键值元组列表
key_part1 = 1 定义了一个间隔: (1,-inf,-inf) <= (key_part1,key_part2,key_part3) <(1,+inf,+inf) ,这个间隔包括上面的第4、5、6个元组并且可以被用来进行范围访问。
但是, key_part3 = 'abc' 没有定义间隔并且不能被范围访问方法使用。
就是索引的最左前缀原则,B树索引是有序的,多列索引是首先按照第一列进行排序,然后在第一列排序的基础上,再对第二列数据进行排序,所以后面的列的顺序独立来看不是有序的,就不能单独用后面的列来进行排序或者范围访问的 *** 作。
对于 HASH 索引,只能使用包含相同值的每个间隔。这意味着只能针对以下形式的条件生成间隔:
这里, const1,const2... 是常量, cmp 是比较表达式: =,<=>,IS NULL ,并且条件覆盖所有的索引部分(就是说,如果有 N 个条件,那么每个条件都需要是一个 N列 索引的一部分)。例如:以下是一个三列 HASH 索引的一个范围条件
对于 BTREE 索引,一个间隔可以是使用 AND 组成的多个范围条件的集合,每个条件都将索引的一部分和一个常量使用 =,<=>,IS NULL,>,<,>=,<=,!=,<>,BETWEENT,LIKE 'pattern'(pattern不以通配符开始) 进行比较。只要可以确定与条件匹配的一个索引元组,就可以使用一个间隔( !=,<>使用两个间隔)
当比较运算符是 =,<=>,IS NULL 时,优化器尝试使用索引的其他部分来确定间隔。如果比较运算符是 >, <, >=, <=, !=, <>, BETWEEN, LIKE ,优化器使用索引,但不考虑索引中的其他列。
对于以下表达式,优化器使用第一个 = ,也会使用第二个 >= ,但是忽略其他索引部分,并且不将第三部分用作间隔构造。
key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 >10
单个间隔为:
创建的间隔中可能包括比原始条件更多的行,比如,前面这个间隔可能会包括 ('foo',11,0) 这个值, 0<10 ,这个值不满足原始条件
如果覆盖间隔中的行集合的条件使用 OR 进行组合,则他们会形成间隔的并集。
如果条件使用 AND 进行组合,他们形成一个包括间隔交集的行集合。
示例:
这个在两列索引上的条件:
(key_part1 = 1 AND key_part2 <2) OR (key_part1 >5)
间隔是:
可以查看 EXPLAIN 输出中的 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 是一个索引的列
只要 col_name 等同于这些值中的任意一个,这个表达式结果就是 true 。这种比较是等值范围比较(其中的“范围”是一个单独的值)。
优化器按照以下方法,估算读取相等的值来进行等值范围比较的成本:
当使用 index dive 时,优化器在每个范围的末端进行 dive 并且使用该范围中的行数作为估算值。例如: col_name IN (10, 20, 30) 具有三个等值范围,优化器对每个范围进行两次 dive 以生成估算值。每次 dive 都会得出具有给定值的行数的估算值。
使用 index dive 提供了准确的行数估算值,但是随着表达式中要比较的值的数量增加,优化器需要使用更长的时间来生成行数的估算值。而使用索引统计信息的准确性不如直接使用索引,但是可以对大表进行更快的估算。
eq_range_index_dive_limit 选项可以控制优化器选择评估策略的值。要对 N 个等值范围使用 index dive ,将 eq_range_index_dive_limit 设置为 N+1 ,要禁用统计信息,总是使用 index dive ,将 eq_range_index_dive_limit 设置为0。
在MySQL8.0以前,除了使用 eq_range_index_dive_limit ,没有其他方法可以跳过 index dive 。在MySQL8.0中,当满足以下条件时,跳过 index dive :
对于 EXPLAIN FOR CONNECTION ,如果跳过了 index dive ,输出结果有所变更:
不包括 FOR CONNECTION 的 EXPLAIN 输出没有变化
在执行跳过 index dive 的查询后, INFORMATION_SCHEMA.OPTIMIZER_TRACE 表包含一个值为 skipped_due_to_force_index 的 index_dives_for_range_access 行
优化器可以对这种形式的查询进行范围扫描:
SELECT ... FROM t1 WHERE ( col_1, col_2 ) IN (( 'a', 'b' ), ( 'c', 'd' ))
要使用范围扫描,查询必须满足以下条件:
要控制有多少内存可以用来进行范围优化,使用 range_optimizer_max_mem_size 变量
使用以下原则估算范围扫描使用的内存:
IN() 中的每个值被当做使用 OR 结合的一个谓词。如果有两个 IN() 列表,每个列表中都是列表中的值的数量个谓词通过 OR 结合。在这种情况下,视作 M × N 个 谓词通过OR 结合。
简单的说:查询的时候生效。
判断mysql的索引生效的方法:用explain 来执行检验sql 例如
explain select * from a
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len |ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | t3 | const | PRIMARY,idx_t3_id | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
第四列 type
这列很重要,显示了连接使用了哪种类别,有无使用索引.
从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
(1).system
这是const联接类型的一个特例。表仅有一行满足条件.
2).const
表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次!
3). eq_ref
对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARYKEY。
eq_ref可以用于使用= *** 作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。
(4).ref
对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARYKEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。如果使用的键仅仅匹配少量行,该联接类型是不错的。
ref可以用于使用=或<=> *** 作符的带索引的列。
(5). ref_or_null
该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。
在下面的例子中,MySQL可以使用ref_or_null联接来处理ref_tables:
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL
(6). index_merge
该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
例如:
mysql>explain select * from t4 where id=3952602 oraccountid=31754306
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
| 1 | SIMPLE | t4 | index_merge | idx_t4_id,idx_t4_accountid |idx_t4_id,idx_t4_accountid | 4,4 | NULL| 2 | Using union(idx_t4_id,idx_t4_accountid)Usingwhere |
+----+-------------+-------+-------------+----------------------------+----------------------------+---------+------+------+------------------------------------------------------+
1 row in set (0.00 sec)
(7). unique_subquery
该类型替换了下面形式的IN子查询的ref:
value IN (SELECT primary_key FROM single_table WHEREsome_expr)
unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
(8).index_subquery
该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:
value IN (SELECT key_column FROM single_table WHEREsome_expr)
(9).range
只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。
当使用=、<>、>、>=、<、<=、ISNULL、<=>、BETWEEN或者IN *** 作符,用常量比较关键字列时,可以使用range
mysql>explain select * from t3 where id=3952602 or id=3952603
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len |ref | rows | Extra |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | t3 | range | PRIMARY,idx_t3_id | idx_t3_id | 4 | NULL | 2 | Using where |
+----+-------------+-------+-------+-------------------+-----------+---------+------+------+-------------+
1 row in set (0.02 sec)
(10).index
该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。
当查询只使用作为单索引一部分的列时,MySQL可以使用该联接类型。
(11). ALL
对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记const的表,这通常不好,并且通常在它情况下很差。通常可以增加更多的索引而不要使用ALL,使得行能基于前面的表中的常数值或列值被检索出。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)