Mysql索引优化
目录
Mysql索引优化
SQL性能分析方法
执行频次
方式:
|
|
慢查询日志
方式:
|
|
profile分析
方式:
|
|
explain(执行计划)
方式:
|
|
EXPLAIN 输出详解
1. id
说明:查询中每个 SELECT 子句的唯一标识符。
详细解读:
id相同:如果多行的id相同,表示它们属于同一个查询块,执行顺序 从上到下。id不同:如果id不同,id值 越大,优先级越高,越先被执行。这通常出现在子查询、派生表或UNION中。id为NULL:这一行是UNION操作的结果集,它合并了其他id的查询结果。
示例:
|
|
在这个查询中,子查询 SELECT emp_no FROM salaries ... 的 id 会是 2,而外部主查询的 id 会是 1。MySQL 会先执行 id=2 的子查询。
2. select_type
说明:SELECT 查询的类型。
常见值及解读:
SIMPLE:最常见的类型,表示查询不包含子查询或UNION。PRIMARY:查询中若包含任何复杂的子部分(如子查询、UNION),最外层的SELECT被标记为PRIMARY。SUBQUERY:在SELECT列表或WHERE子句中出现的子查询(非FROM子句中)。DERIVED:在FROM子句中出现的子查询。MySQL 会将该子查询的结果放到一个临时表(派生表)中,该行显示的就是对这个临时表的操作。UNION:在UNION操作中,第二个及之后的SELECT语句。UNION RESULT:UNION操作的结果集。
3. table
说明:这一行数据是关于哪个表的。 详细解读:
- 直接显示表名。
- 可能是别名(如果查询中使用了别名)。
- 可能是
<derivedN>:表示这一行是针对id为N的派生表进行的操作。 - 可能是
<unionM,N>:表示这一行是id为M和N的UNION结果集。
4. partitions
说明:查询匹配到的分区。 详细解读:
- 这个字段只有在表是分区表时才有意义。
- 它显示了查询将要访问的分区。如果为
NULL,表示该表不是分区表,或者查询没有触发任何分区裁剪(Partition Pruning)优化。 - 如果能明确命中少数几个分区,说明分区设计是有效的。
5. type (⭐ 极其重要 ⭐)
说明:表的连接类型或数据访问类型,这是衡量查询性能好坏的 最关键指标。
性能从好到坏的顺序:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > range > index > ALL
system:表只有一行数据(系统表),是const类型的特例。几乎不会出现。const:通过主键或唯一索引,最多只匹配一行数据。查询速度极快,因为数据库把它当做一个常量来处理。... WHERE primary_key = 123
eq_ref:在JOIN查询中,前一张表的每一行,都能在后一张表中通过主键或唯一索引精确匹配到 一行 数据。这是JOIN的最佳类型。... FROM t1 JOIN t2 ON t1.pk = t2.pk
ref:非唯一性索引扫描,或者唯一索引的最左前缀扫描。对于每个匹配的键值,可能会返回 多行 数据。... WHERE non_unique_index_col = 'some_value'
range:使用索引进行范围扫描。常见于BETWEEN,>,<,IN()等操作。比全索引扫描好,因为它只扫描索引的一部分。... WHERE id BETWEEN 100 AND 200
index:全索引扫描(Full Index Scan)。它会遍历整个索引树。虽然比ALL快(因为索引通常比表数据小),但仍然是性能不佳的表现。通常发生在查询的列全部在索引中(覆盖索引),但没有WHERE条件来缩小范围时。ALL:全表扫描(Full Table Scan)。MySQL 将遍历整张表来寻找匹配的行。这是 最坏 的情况,是性能优化的首要目标。
优化建议:你的目标应该是让 type 至少达到 range 级别,最好是 ref 或 eq_ref。如果看到 ALL,通常意味着需要添加索引。
6. possible_keys
说明:MySQL 认为 可能 在这个查询中使用的索引列表。 详细解读:
- 这个字段是“理论上”可用的索引。
- 如果这里是
NULL,表示没有找到任何合适的索引可供使用。这时你需要检查WHERE子句涉及的字段是否建了索引。
7. key
说明:MySQL 实际决定 使用的索引。 详细解读:
- 这是
possible_keys中的一个,也可能是NULL。 - 如果
key是NULL但possible_keys有值,说明 MySQL 评估后认为全表扫描比使用索引更快(例如,表数据量很小)。 - 强制使用索引:可以使用
FORCE INDEX(index_name)来强制 MySQL 使用某个索引,但这通常不推荐,因为优化器通常更准。
8. key_len
说明:实际使用的索引的长度(字节数)。 详细解读:
- 这个值可以帮助你判断 MySQL 使用了联合索引中的哪些部分(最左前缀原则)。
key_len越短通常越好。- 计算规则(大致):
VARCHAR(N):N * 字符集字节数 + 2(变长字段有2字节记录长度)CHAR(N):N * 字符集字节数INT:4字节BIGINT:8字节- 如果字段允许为
NULL,则需要额外1个字节。
- 通过
key_len的值,你可以精确推断出联合索引(a, b, c)中,查询用到了(a)还是(a, b)还是(a, b, c)。
9. ref
说明:显示将哪些列或常量与 key 列指定的索引进行比较,以从表中选择行。
详细解读:
const:表示用一个常量值与索引进行比较,如WHERE id = 10。db_name.table_name.col_name:表示用到了一个列进行关联,常见于JOIN操作中,如t1.id = t2.ref_id,在t2表的执行计划行中,ref字段会显示db_name.t1.id。
10. rows
说明:MySQL 估算的为了找到所需行而需要读取的行数。 详细解读:
- 这是一个 估算值,不是精确值。
- 这个数字越小越好。如果
rows值很大,而实际返回的行数很少,说明查询效率低下。
11. filtered
说明:一个百分比,表示经过 WHERE 条件过滤后,剩下行数的百分比(相对于 rows 列)。
详细解读:
rows* (filtered/ 100) = 估算出的将与下一张表连接的行数。- 这个值越高越好。如果
filtered很低(例如 10%),说明rows中读取的大部分行都被WHERE条件丢弃了,这可能意味着索引的区分度不高。
12. Extra (⭐ 非常重要 ⭐)
说明:包含不适合在其他列中显示的额外重要信息。 常见关键值解读:
Using index:(好) 这是一个非常好的信号!表示查询的列信息可以直接从索引中获取,而无需回表(访问实际的数据行)。这被称为“覆盖索引”(Covering Index)。这是性能优化的一个重要目标。Using where:(正常) 表示 MySQL 服务器层在从存储引擎获取数据后,需要进行额外的WHERE条件过滤。这是很常见的。Using index condition:(好) 索引下推(Index Condition Pushdown, ICP)优化。在访问索引时,服务器把WHERE条件的一部分下推到存储引擎层去判断,这样可以减少存储引擎返回给服务器层的数据量。Using temporary:(坏) 表示 MySQL 需要创建一个临时表来存储中间结果,以完成查询。常见于GROUP BY和ORDER BY的列不同,或UNION操作。创建临时表开销很大,是主要的性能瓶颈之一。应尽量避免。Using filesort:(坏) 表示 MySQL 无法利用索引来完成排序,必须在内存或磁盘上进行额外的排序操作(文件排序)。这也是一个主要的性能瓶瓶颈。通常可以通过创建合适的索引或修改ORDER BY子句来避免。Using join buffer (Block Nested Loop):(坏) 在JOIN操作中,被驱动表(后一个表)没有使用索引,MySQL 不得不使用一个连接缓冲区来批量处理。这意味着关联字段上缺少索引。
如何解读和优化
- 定位问题查询:首先找到
type为ALL或index的查询,这是全表扫描或全索引扫描,是首要优化对象。 - 检查索引使用:查看
key字段,确认是否使用了正确的索引。如果key为NULL,检查possible_keys和WHERE子句。 - 分析 Extra 信息:密切关注
Extra列中是否出现Using temporary和Using filesort。如果出现,需要分析GROUP BY,ORDER BY,DISTINCT等子句,并尝试通过创建合适的索引来消除它们。 - 追求覆盖索引:如果查询的列都在一个索引里,
Extra会显示Using index,这是最高效的查询方式之一。 - 减少扫描行数:关注
rows和filtered字段,确保 MySQL 估算的扫描行数尽可能少。
Mysql索引使用
索引分类
- 主键索引
- 唯一索引
- 普通索引
- 全文索引(少用)
- 联合索引 (多个字段上创建的索引)
- 二级索引 (非聚簇索引)
- 聚簇索引 (一般主键索引)
- 前缀索引 (对字段的前缀创建索引) 语法:
|
|
注意项
- 索引字段的顺序:在联合索引中,字段的顺序会影响索引的效率。一般将区分度高的字段放在前面。
- 索引字段的长度:前缀索引对字段的前几个字符创建索引,适用于文本字段的索引。
- 索引的维护成本:索引会占用额外的磁盘空间,并且在插入、更新、删除操作时需要维护索引。因此,需要根据实际情况评估是否需要创建索引。
- 使用索引不一定能提高查询效率:在某些情况下,即使创建了索引,MySQL 优化器也可能选择不使用索引,例如当查询条件中包含函数操作、OR 条件等。
- 值的区分度:索引的区分度越高,索引的效率就越高。因此,在创建索引时,应该选择区分度高的字段。
- 不应该选择区分度低的字段创建索引,例如性别、状态等字段。
- 不应该选择经常变化的字段创建索引,例如订单状态等字段。
- 不应该选择小表创建索引,例如只有几条记录的表(数据量小的表查询时全表扫描比索引效率高)。
- 索引覆盖:当查询的列都在索引中时,可以避免回表查询,提高查询效率。
- 索引合并:当查询条件中包含多个索引时,MySQL 可以选择使用索引合并来提高查询效率。
- 如果索引列不能为NULL,那么在创建字段时,应该将字段设置为NOT NULL,这样可以避免索引失效。
索引优化
- 尽可能使用联合索引:当查询条件中包含多个字段时,应该考虑创建联合索引。
- 不能创建过多的索引:一般每个表的索引数量不能超过5个,否则会影响查询效率。
- 最左前缀原则:在联合索引中,查询条件中必须包含索引的最左边的字段,否则索引不会被使用。