MySQL联合索引与覆盖索引实战_Sublime语句示例分析索引命中情况

要判断mysql查询是否使用了联合索引或覆盖索引,需使用explain命令分析查询计划;1.查看type列:ref、range、eq_ref表示有效使用索引;2.查看key列:显示实际使用的索引名称;3.查看key_len列:反映索引的哪部分被使用;4.查看extra列:using index表示覆盖索引生效。联合索引的列顺序影响查询性能,遵循最左前缀原则;1.索引列顺序应优先放置区分度高且常用于等值匹配的列;2.范围查询后的列无法有效利用索引;3.设计时需结合具体查询模式以最大化索引利用率。

MySQL联合索引与覆盖索引实战_Sublime语句示例分析索引命中情况

当谈到mysql性能优化,联合索引和覆盖索引绝对是绕不开的话题。简单来说,联合索引就是多个列组合在一起创建的索引,而覆盖索引则是一种特殊情况,它意味着查询所需的所有数据都可以在索引本身中找到,无需回表查询数据行。这两种索引策略,如果用得好,能让你的数据库查询速度飞起来,尤其是在处理大量数据时,那效果简直立竿见影。我个人在实际项目中,经常会遇到一些慢查询,追根溯源,往往就是索引没用对或者压根没索引。所以,理解并实战这俩概念,真不是什么理论知识,而是提升系统响应速度的硬核技能。

MySQL联合索引与覆盖索引实战_Sublime语句示例分析索引命中情况

实战联合索引和覆盖索引,我的经验是,得从你的查询语句出发,反推索引设计。假设我们有一个orders表,包含user_id, order_status, order_time, amount等字段。

场景一:联合索引优化多条件查询 我们经常会查询某个用户在特定状态下的订单,比如:

MySQL联合索引与覆盖索引实战_Sublime语句示例分析索引命中情况

select * FROM orders WHERE user_id = 123 AND order_status = 'completed' ORDER BY order_time DESC;

对于这个查询,一个常见的错误是只给user_id或order_status单独建索引。但更优的做法是创建一个联合索引:(user_id, order_status, order_time)。

sublime Text里,我通常会这样写SQL,然后用EXPLaiN分析:

MySQL联合索引与覆盖索引实战_Sublime语句示例分析索引命中情况

-- 创建联合索引 CREATE INDEX idx_user_status_time ON orders (user_id, order_status, order_time);  -- 查询语句 EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND order_status = 'completed' ORDER BY order_time DESC;

执行EXPLAIN后,关注type、key、key_len、ref、extra这些列。如果type是ref或range,并且key显示使用了idx_user_status_time,那就说明联合索引被有效利用了。key_len会告诉你索引的哪部分被用到了,这对于理解索引的最左前缀原则非常关键。

场景二:覆盖索引避免回表 现在,假设我们只需要查询满足条件的用户ID和订单时间,不需要其他字段:

SELECT user_id, order_time FROM orders WHERE user_id = 123 AND order_status = 'completed';

如果我们的联合索引是(user_id, order_status, order_time),那么这个查询就是一个典型的覆盖索引案例。因为SELECT列表中的user_id和order_time,以及WHERE条件中的user_id和order_status,所有需要的数据都可以在这个索引的B+树节点中直接获取,数据库引擎无需再去访问实际的数据行(回表)。

在Sublime里,我同样会用EXPLAIN来验证:

EXPLAIN SELECT user_id, order_time FROM orders WHERE user_id = 123 AND order_status = 'completed';

这次,EXPLAIN的Extra列会显示Using index。这个提示就是覆盖索引生效的标志。它意味着查询完全依赖索引,没有进行回表操作,性能自然是极高的。

我遇到过不少开发者,他们在查询SELECT *的时候,即便有联合索引,也无法实现覆盖索引的效果,因为*意味着需要所有列的数据,而索引通常只包含部分列。所以,精细化你的SELECT列表,只选取你真正需要的字段,是实现覆盖索引,进一步提升性能的关键一步。当然,这在某些ORM框架下可能没那么直观,但理解这个原理,能帮助你更好地优化ORM生成的SQL。

如何判断MySQL查询是否使用了联合索引或覆盖索引?

判断MySQL查询是否有效使用了索引,特别是联合索引和覆盖索引,最直接也是最权威的工具就是EXPLAIN命令。这是我日常工作中分析慢查询的起手式。

当你执行EXPLAIN your_sql_query;后,会得到一张结果表,你需要重点关注以下几个字段:

  • type列: 这是最重要的指标之一,它表示MySQL如何查找表中的行。
    • const, eq_ref, ref: 这些是最高效的类型,表示通过索引进行精确查找。ref通常是联合索引的最左前缀匹配或单个索引的等值匹配。
    • range: 表示索引范围扫描,比如WHERE id BETWEEN 10 AND 20。
    • index: 表示全索引扫描,虽然比全表扫描(ALL)好,但如果索引很大,性能可能依然不理想。它通常发生在覆盖索引生效,但WHERE条件无法利用索引进行过滤时。
    • ALL: 最差的类型,表示全表扫描,意味着没有使用索引,或者索引选择性太差被优化器放弃。
  • key列: 明确指出MySQL实际使用的索引名称。如果这里显示了你创建的联合索引名称,那么它肯定被用到了。如果显示NULL,那显然没用上索引。
  • key_len列: 显示MySQL实际使用的索引的长度(字节数)。这个值可以帮助你理解联合索引的最左前缀原则。如果你的联合索引是(a, b, c),当你只用a查询时,key_len会显示a的长度;如果用a, b查询,则显示a和b的长度之和。这能直观地告诉你索引的哪部分被利用了。
  • rows列: 估计MySQL为了找到所需行而需要读取的行数。这个值越小越好。
  • Extra列: 这是判断覆盖索引的关键。
    • Using index: 这是覆盖索引的明确标志。它表示查询所需的所有数据都可以在索引中找到,无需回表查询数据行。
    • Using where: 表示MySQL需要通过WHERE子句来过滤结果。如果同时出现Using index,通常是好的;如果单独出现,可能意味着索引没有完全满足查询条件,或者需要额外的过滤步骤。
    • Using filesort: 表示MySQL需要对结果进行外部排序,通常发生在ORDER BY子句中的列没有被索引覆盖,或者索引顺序与排序顺序不匹配时。这通常是性能瓶颈。
    • Using temporary: 表示MySQL需要创建临时表来处理查询,通常发生在复杂的GROUP BY或DISTINCT操作中,也是性能隐患。

我通常会把EXPLAIN的结果复制到sublime text里,然后逐行分析,特别是type和Extra。如果type不是ALL,并且Extra有Using index,那这个查询的索引优化基本就到位了。如果出现Using filesort或Using temporary,我就会开始思考如何调整索引或sql语句来避免它们。

联合索引的列顺序对查询性能有何影响?

联合索引的列顺序,简直是索引设计中的“玄学”,但实际上它有非常明确的逻辑,那就是“最左前缀原则”。这个原则决定了你的联合索引能否被有效利用,以及在何种程度上被利用。

简单来说,如果你的联合索引是(col1, col2, col3),那么它可以支持以下几种查询模式:

  1. 只使用col1:WHERE col1 = ‘xxx’
  2. 使用col1和col2:WHERE col1 = ‘xxx’ AND col2 = ‘yyy’
  3. 使用col1、col2和col3:WHERE col1 = ‘xxx’ AND col2 = ‘yyy’ AND col3 = ‘zzz’
  4. 使用col1和col3,但col2是范围查询或不存在:WHERE col1 = ‘xxx’ AND col3 = ‘zzz’ (此时col3可能无法完全利用索引,因为它跳过了col2)

但它不能直接支持:

  • 只使用col2:WHERE col2 = ‘yyy’
  • 只使用col3:WHERE col3 = ‘zzz’
  • 使用col2和col3:WHERE col2 = ‘yyy’ AND col3 = ‘zzz’

这就意味着,在设计联合索引时,你需要把查询中最常用作等值匹配(=或IN)的列放在前面,尤其是那些选择性(Cardinality,即不重复值的数量)高的列。

举个例子,如果你的orders表经常有这样的查询: SELECT * FROM orders WHERE user_id = ? AND order_status = ?;SELECT * FROM orders WHERE user_id = ?;SELECT * FROM orders WHERE order_status = ?;

那么,将user_id放在联合索引的第一位,即(user_id, order_status),会比(order_status, user_id)更优。因为user_id通常选择性更高(用户ID一般不重复),而且它能满足user_id单独查询的需求。而order_status通常只有少数几个值(比如’completed’, ‘pending’, ‘cancelled’),选择性很低。如果把order_status放在第一位,那么对于user_id的查询就无法利用索引,对于order_status的查询,即便利用了索引,也可能因为选择性太低而扫描大量数据。

一个实用的经验是:将区分度高的列放在联合索引的前面,然后是区分度一般的,最后是区分度低的。同时,也要考虑你的查询模式。如果某个列经常用于范围查询(>, 10 AND c = 5,那么c将无法利用索引,因为b是

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享