Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL数据库索引

MySQL数据库索引优化及应用

作者:rchmin

本文详细介绍了MySQL数据库索引的基本概念、优缺点、常见类型、使用策略、优化技巧以及查看和分析索引使用情况的方法,通过这些内容,读者可以更好地理解和使用索引来优化MySQL数据库的性能,感兴趣的朋友跟随小编一起看看吧

这是一篇关于 MySQL 数据库索引的全面介绍,从基本概念到高级特性,希望能帮助你深入理解。

一、索引是什么?

想象一下一本书的目录。如果没有目录,你想找到某一章节的内容,只能从第一页开始一页一页地翻找。而有了目录,你可以通过章节标题快速定位到对应的页码。

在 MySQL 中,索引就是一种帮助数据库高效获取数据的“目录”。它是在存储引擎层实现的,而不是服务器层,因此不同的存储引擎(如 InnoDB、MyISAM)的索引工作方式有所不同(我们主要讨论最常用的 InnoDB)。

没有索引:数据库需要进行全表扫描,从第一行开始,逐行读取直到找到所有符合条件的行。对于海量数据,这非常缓慢。
有索引:数据库可以通过索引直接定位到数据的大概位置,然后只需检查很少的数据行即可得到结果,效率极高。

二、索引的优缺点

优点

缺点

三、索引的常见类型

1. 按数据结构划分

2. 按物理存储划分(InnoDB 聚簇索引特性)

这是 InnoDB 引擎的一个核心概念。

3. 按字段特性划分

普通索引:最基本的索引,没有任何限制。

CREATE INDEX idx_name ON table_name (column_name);

唯一索引:索引列的值必须是唯一的,但允许有空值。

CREATE UNIQUE INDEX idx_email ON users (email);

主键索引:一种特殊的唯一索引,不允许有空值。每个表只能有一个主键索引。

组合索引(复合索引):在多个列上建立的索引。

CREATE INDEX idx_name_age ON employees (last_name, first_name, age);

最左前缀原则:这是组合索引最重要的特性。查询时,索引只能从最左边的列开始匹配。上面的 idx_name_age 索引对以下查询有效:

WHERE last_name = ‘Smith’

WHERE last_name = ‘Smith’ AND first_name = ‘John’

WHERE last_name = ‘Smith’ AND first_name = ‘John’ AND age = 30

WHERE last_name = ‘Smith’ AND age = 30 (只使用了 last_name,因为 first_name 断了)

但对以下查询无效

WHERE first_name = ‘John’

WHERE age = 30

四、索引的使用策略与优化

索引使用总结:

特性说明
核心作用提高查询速度,避免全表扫描。
核心数据结构B+Tree,适合范围查询和排序。
InnoDB核心聚簇索引(数据即索引)和非聚簇索引(需回表)。
设计黄金法则最左前缀原则,决定了组合索引的有效性。
性能优化利器覆盖索引(避免回表)和 EXPLAIN 命令(分析执行计划)。

正确理解和使用索引是数据库性能优化的基石。它需要在查询速度和写入开销之间做出权衡,并通过不断的分析和测试来找到最佳实践。

五、查看和分析索引使用情况

1. 查看执行计划

使用 EXPLAIN 命令可以查看 SQL 语句的执行计划,这是优化索引最强大的工具。

EXPLAIN SELECT * FROM employees WHERE last_name = ‘Smith' AND age > 30;

重点关注以下几个字段:

        如果出现 Using filesort(文件排序)或 Using temporary(使用临时表),通常意味着需要优化索引。如果出现 Using index,则说明使用了覆盖索引,性能很好。

2. 查看估算成本

使用 EXPLAIN FORMAT=JSON 可以查看优化器估算的精确成本值。

EXPLAIN FORMAT=JSON SELECT * FROM employees WHERE last_name = 'Smith' AND department_id = 5;
{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "10.45"  // ← 这是整个查询的预估总成本!
    },
    "table": {
      "table_name": "employees",
      "access_type": "ref",  // 访问类型
      "possible_keys": ["idx_last_name", "idx_department"],
      "key": "idx_last_name",
      "used_key_parts": ["last_name"],
      "key_length": "62",
      "ref": ["const"],
      "rows_examined_per_scan": 45,  // 预估扫描行数
      "rows_produced_per_join": 5,   // 预估产出行数
      "filtered": "11.11",           // 过滤效率百分比
      "cost_info": {
        "read_cost": "9.95",         // 读取成本
        "eval_cost": "0.50",         // 计算评估成本
        "prefix_cost": "10.45",      // 当前表的总成本
        "data_read_per_join": "1K"   // 读取数据量
      },
      "used_columns": [...]
    }
  }
}

关键成本指标:

3. 查看实际执行

使用 MySQL 8.0 的 EXPLAIN ANALYZE,它不仅显示优化器的预估,还实际执行查询并返回实际执行的统计信息!

EXPLAIN ANALYZE SELECT * FROM employees WHERE last_name = 'Smith' AND department_id = 5;

输出示例:

-> Filter: (employees.department_id = 5)  (cost=10.45 rows=5) (actual time=0.125..0.256 rows=3 loops=1)
    -> Index lookup on employees using idx_last_name (last_name='Smith')  (cost=10.45 rows=45) (actual time=0.120..0.248 rows=45 loops=1)

示例解读:

这是最可靠的性能分析工具,因为它对比了优化器的预估和实际执行情况。

六、访问类型详解

EXPLAIN 命令输出中的 type 列是非常重要的,它告诉我们 MySQL 是如何在表中查找行的。以下是从最优到最差排序的各个访问类型的详细解释。

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

在实际优化中,我们最常关注的是 const, eq_ref, ref, range, index, ALL

1.system

含义:这是 const 类型的一个特例。表示表中只有一行数据(等于系统表)。这是性能最好的情况。

触发场景:通常是查询系统表,或者使用 MyISAM 存储引擎且只有一条记录的表。

示例

-- 假设只有一条记录的系统配置表
EXPLAIN SELECT * FROM system_config WHERE id = 1;

2.const

EXPLAIN SELECT * FROM users WHERE id = 10; -- id 是主键
EXPLAIN SELECT * FROM users WHERE email = 'admin@example.com'; -- email 有唯一索引

3.eq_ref

含义:在表连接时使用,通常出现在被驱动表的连接条件上。它使用主键或唯一索引作为关联条件,对于驱动表的每一行,在被驱动表中只能找到唯一的一行与之对应。

说明:这是除了 system 和 const 之外最好的连接类型。

示例

EXPLAIN SELECT *
FROM orders
JOIN customers ON orders.customer_id = customers.id; -- customers.id 是主键

在这个例子中,对于 orders 表中的每一个 customer_id,到 customers 表中通过主键 id 查找,每次查找都只返回一条记录。

4.ref

-- 假设 last_name 字段有一个普通索引
EXPLAIN SELECT * FROM employees WHERE last_name = 'Smith';
-- 表连接中使用非唯一索引
EXPLAIN SELECT *
FROM orders o
JOIN products p ON o.product_sku = p.sku; -- p.sku 是一个普通索引(非唯一)

5.ref_or_null

EXPLAIN SELECT * FROM users WHERE phone_number = '123456' OR phone_number IS NULL;
-- 假设 phone_number 上有索引

6.range

EXPLAIN SELECT * FROM employees WHERE age BETWEEN 25 AND 35; -- age 有索引
EXPLAIN SELECT * FROM users WHERE id IN (1, 5, 10); -- id 是主键
EXPLAIN SELECT * FROM products WHERE price > 50; -- price 有索引

7.index

-- 假设 (status, created_at) 有一个复合索引
EXPLAIN SELECT status, created_at FROM articles; -- 覆盖索引,但无 WHERE 条件
EXPLAIN SELECT id FROM users ORDER BY id; -- 按主键排序

8.ALL

EXPLAIN SELECT * FROM products WHERE name = 'Laptop'; -- name 列上没有索引

9. 其他较少见的类型

访问类型总结:

类型性能含义优化目标
system, const最优通过主键/唯一索引找到唯一一行理想状态
eq_ref极佳表连接时使用主键/唯一索引连接查询的理想状态
ref良好使用非唯一索引进行等值查找非常常见且健康的状态
range不错使用索引进行范围查找对于范围查询是正常状态
index较差全索引扫描考虑是否可以添加 WHERE 条件或优化查询
ALL最差全表扫描必须优化! 为查询列创建索引

核心建议
在优化时,我们的目标是让 type 至少达到 range 级别,最好能达到 ref。一旦看到 ALL,就应该立即检查是否可以为相关列创建有效的索引。

七、实际应用:成本对比分析

让我们通过一个实际例子来展示如何用成本分析来比较不同索引的效果:

-- 情况1:无合适索引
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31' AND customer_id = 100;
-- 结果:query_cost = "1250.25", type = "ALL"
-- 添加索引后
ALTER TABLE orders ADD INDEX idx_customer_date (customer_id, order_date);
-- 情况2:使用新索引
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31' AND customer_id = 100;
-- 结果:query_cost = "8.75", type = "range"

结论:通过成本对比,我们可以量化索引带来的性能提升:从 1250.25 降到 8.75,性能提升了 140多倍

成本评估总结:

记住:成本值本身没有绝对的好坏标准,重要的是通过对比不同查询方案或索引设计的成本值,来选择最优的执行计划。通常成本值降低 10倍以上就说明优化非常有效。

到此这篇关于MySQL数据库索引全面介绍的文章就介绍到这了,更多相关MySQL数据库索引内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文