目录

前言

一、索引是什么?核心价值何在

1. 索引的本质

2. 索引的核心作用

3. 索引的缺点

二、索引结构:为什么 InnoDB 偏爱 B+Tree?

1. 常见索引结构对比

2. InnoDB 选择 B+Tree 的核心原因

3. 哈希索引补充说明

三、索引分类:不同场景选对类型

1. 按功能分类(4 种核心类型)

2. 按存储形式分类(InnoDB 专属)

(1)聚集索引(Clustered Index)

(2)二级索引(Secondary Index)

3. 关键区别:聚集索引 vs 二级索引

四、索引语法:创建、查看、删除实战

1. 基础语法

(1)创建索引

(2)查看索引

(3)删除索引

2. 关键语法说明

五、SQL 性能分析:找到需要优化的索引

1. 查看 SQL 执行频率

2. 慢查询日志

(1)开启慢查询日志

(2)查看慢查询日志

3. profile 分析 SQL 执行细节

4. explain 执行计划(最常用)

(1)使用语法

(2)关键字段解读

六、索引使用规则:避免失效的核心技巧

1. 最左前缀法则(联合索引必守)

2. 范围查询陷阱

3. 索引失效的 8 种常见情况

4. 优化技巧:覆盖索引与回表查询

5. 前缀索引与联合索引选择

七、索引设计原则

总结


前言

在 MySQL 数据库中,索引是提升查询效率的核心利器,就像图书馆的藏书目录,能帮我们快速定位目标数据,避免全表扫描的低效操作。但索引并非 “越多越好”,不合理的设计反而会拖慢增删改性能。本文结合 MySQL 底层原理和实战场景,带大家全面吃透索引知识。

一、索引是什么?核心价值何在

1. 索引的本质

索引(Index)是 MySQL 为高效获取数据而维护的有序数据结构,它独立于数据本身存在,通过指针关联原始数据,支持多种查找算法(如 B+Tree、Hash),从而降低数据库 IO 成本和 CPU 排序消耗。

2. 索引的核心作用

优势 具体说明
提速查询 避免全表扫描,通过索引结构快速定位数据(如百万级数据查询从秒级降至毫秒级)
优化排序 索引本身有序,可直接利用索引完成排序,无需额外计算
降低开销 减少磁盘 IO 次数(索引通常远小于数据量)和 CPU 排序消耗

3. 索引的缺点

  • 占用存储空间:索引列会额外占用磁盘空间,多索引场景下开销明显
  • 降低写性能:增删改操作需同步维护索引结构(如 B+Tree 分裂 / 合并),操作越频繁影响越大

二、索引结构:为什么 InnoDB 偏爱 B+Tree?

MySQL 的索引结构由存储引擎实现,不同引擎支持的结构不同(如 InnoDB 支持 B+Tree,Memory 支持 Hash)。其中 B+Tree 是最常用的结构,我们先理清各类结构的优劣:

1. 常见索引结构对比

结构 特点 适用场景 缺陷
二叉树 简单二叉查找 无,已淘汰 顺序插入成链表,层级过深(大数据量下效率极低)
红黑树 自平衡二叉树 无,已淘汰 仍为二叉结构,百万级数据层级达 20+,IO 开销大
B-Tree 多叉平衡树,非叶子节点存数据 早期数据库 非叶子节点存数据,导致单节点键值少,层级仍较高
B+Tree 多叉平衡树,仅叶子节点存数据,叶子节点形成链表 绝大多数场景(InnoDB 默认) 无明显缺陷,完美适配磁盘 IO 和范围查询
Hash 哈希表映射,等值匹配极快 Memory 引擎临时表 / 缓存 不支持范围查询、排序,存在哈希冲突

2. InnoDB 选择 B+Tree 的核心原因

  • 层级更低:多叉结构(如 4 阶 B+Tree 单节点可存多个键值),百万级数据仅需 3 层(根节点→中间节点→叶子节点),IO 次数少
  • 范围查询高效:叶子节点通过双向链表连接,无需回溯父节点(如查询 age>30 且 age<50,直接遍历链表)
  • 数据集中存储:仅叶子节点存数据,非叶子节点仅存索引键值,单节点可容纳更多索引项,进一步降低层级
  • 适配磁盘 IO:磁盘 IO 以 “页”(默认 16KB)为单位,B+Tree 的节点大小设计刚好适配页大小,减少 IO 浪费

3. 哈希索引补充说明

  • 仅 Memory 引擎支持显式 Hash 索引,InnoDB 的 Hash 索引为自适应(根据 B+Tree 自动构建)
  • 优势:等值查询(=、in)效率极高,单次哈希计算即可定位
  • 劣势:不支持范围查询(>、<、between)、排序,哈希冲突会导致性能下降

三、索引分类:不同场景选对类型

1. 按功能分类(4 种核心类型)

类型 特点 关键字 适用场景
主键索引 默认自动创建,唯一非空,一张表仅一个 PRIMARY 主键查询(如 where id=10)
唯一索引 避免字段值重复,可多个 UNIQUE 手机号、邮箱等唯一标识查询
常规索引 无特殊约束,可多个 普通查询条件(如 where name=' 张三 ')
全文索引 匹配文本关键词(非精确匹配) FULLTEXT 文章内容、商品描述等文本检索

2. 按存储形式分类(InnoDB 专属)

(1)聚集索引(Clustered Index)
  • 本质:索引与数据存储在一起,叶子节点直接存储整行数据
  • 选取规则:①有主键→主键索引为聚集索引;②无主键→第一个唯一索引为聚集索引;③无主键和唯一索引→InnoDB 自动生成隐藏 rowid 作为聚集索引
  • 优势:查询效率最高,无需回表(直接获取整行数据)
(2)二级索引(Secondary Index)
  • 本质:索引与数据分离,叶子节点存储主键值(而非整行数据)
  • 查询流程:二级索引→获取主键值→聚集索引→获取整行数据(此过程称为 “回表查询”)
  • 示例:如果给 name 字段建二级索引,查询select * from user where name='Arm'时,需先通过 name 索引找到主键 id,再通过聚集索引查询整行数据

3. 关键区别:聚集索引 vs 二级索引

对比项 聚集索引 二级索引
数据存储 叶子节点存整行数据 叶子节点存主键值
数量限制 仅 1 个 可多个
查询效率 高(无回表) 低(需回表,除非覆盖索引)
典型场景 主键查询 非主键字段查询

四、索引语法:创建、查看、删除实战

1. 基础语法

(1)创建索引
-- 常规索引
CREATE INDEX idx_user_name ON tb_user(name);

-- 唯一索引
CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);

-- 联合索引(多字段组合)
CREATE INDEX idx_user_pro_age_sta ON tb_user(profession, age, status);

-- 全文索引
CREATE FULLTEXT INDEX idx_article_content ON article(content);

-- 前缀索引(字符串字段优化)
CREATE INDEX idx_email_5 ON tb_user(email(5)); -- 仅用email前5个字符建索引
(2)查看索引
-- 查看表中所有索引
SHOW INDEX FROM tb_user;

-- 查看建表语句(含索引信息)
SHOW CREATE TABLE tb_user;
(3)删除索引
DROP INDEX idx_user_name ON tb_user;

2. 关键语法说明

  • 联合索引:字段顺序影响极大(需遵循 “最左前缀法则”,后面会讲)
  • 前缀索引:适用于长字符串字段(如 varchar (100)),通过截取前缀减少索引大小,需平衡前缀长度和选择性(选择性 = 不重复索引值 / 总记录数,越高越好)
  • 全文索引:MySQL5.6 + 支持 InnoDB 全文索引,匹配关键词用match...against,而非 like(如select * from article where match(content) against('MySQL索引')

五、SQL 性能分析:找到需要优化的索引

在优化索引前,需先定位低效 SQL,常用 4 种工具:

1. 查看 SQL 执行频率

通过统计增删改查次数,判断数据库类型(查询为主需优化索引):

-- 查看全局执行频率(session查看当前会话,global查看全局)
SHOW GLOBAL STATUS LIKE 'Com_______'; -- 下划线为7个,匹配Com_select、Com_insert等
  • Com_select:查询次数
  • Com_insert/Com_update/Com_delete:增删改次数

2. 慢查询日志

记录执行时间超过long_query_time(默认 10 秒,建议设为 2 秒)的 SQL,是定位低效查询的核心工具:

(1)开启慢查询日志
-- 临时开启(重启失效)
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2; -- 执行时间超过2秒记录

-- 永久开启(修改my.cnf配置文件)
slow_query_log=1
slow_query_log_file=/var/lib/mysql/slow.log
long_query_time=2
(2)查看慢查询日志
cat /var/lib/mysql/slow.log

3. profile 分析 SQL 执行细节

查看 SQL 执行的每个阶段耗时(如解析 SQL、执行查询、返回结果):

-- 开启profile(当前会话有效)
SET profiling = 1;

-- 执行需要分析的SQL
SELECT * FROM tb_user WHERE name = '白起';
SELECT COUNT(*) FROM tb_sku;

-- 查看所有SQL的执行耗时
SHOW PROFILES;

-- 查看指定SQL的详细阶段耗时(query_id替换为上一步的ID)
SHOW PROFILE FOR QUERY 1;

-- 查看CPU使用情况
SHOW PROFILE CPU FOR QUERY 1;

4. explain 执行计划(最常用)

通过explain分析 SQL 的索引使用情况、连接方式等,是优化索引的核心工具:

(1)使用语法
EXPLAIN SELECT * FROM tb_user WHERE profession = '软件工程' AND age = 31;
(2)关键字段解读
字段 核心含义 关注重点
type 连接类型 性能排序:null>system>const>eq_ref>ref>range>index>all(需优化 range 以下的类型)
possible_keys 可能使用的索引 非空说明有候选索引
key 实际使用的索引 非空说明索引生效,null 说明索引失效
key_len 索引使用的字节数 越长说明使用的索引字段越多(联合索引中可判断生效字段)
rows MySQL 估计要扫描的行数 数值越小越好
Extra 额外信息 Using index(覆盖索引,最优)、Using filesort(排序未用索引,需优化)、Using temporary(临时表,需优化)、Using where(全表扫描后过滤,需优化)

六、索引使用规则:避免失效的核心技巧

1. 最左前缀法则(联合索引必守)

  • 规则:联合索引(如 a,b,c)的查询需从最左列开始,不能跳过中间字段,否则后续字段索引失效
  • 生效场景:where a=?where a=? and b=?where a=? and b=? and c=?(字段顺序不影响,MySQL 会自动调整)
  • 失效场景:where b=?(跳过 a)、where a=? and c=?(跳过 b,c 字段失效)
  • 示例:联合索引idx_pro_age_sta(profession,age,status)
    • 生效:where profession='软件工程' and age=31
    • 失效:where profession='软件工程' and status='0'(status 字段失效)

2. 范围查询陷阱

  • 规则:联合索引中,范围查询(>、<)右侧的字段索引失效;使用>=、<= 则不失效
  • 示例:where profession='软件工程' and age>30 and status='0'
    • 结果:age 字段用范围查询,status 字段索引失效(key_len 仅包含 profession 和 age)
  • 优化方案:业务允许时,用 >=、<= 替代>、<(如age>=31替代age>30

3. 索引失效的 8 种常见情况

失效场景 示例 SQL 优化方案
索引列运算 where substring(phone,10,2)='15' 避免索引列运算,改为where phone like '%15'(需配合前缀索引)
字符串不加引号 where phone=17799990015(phone 为 varchar) 字符串加引号:where phone='17799990015'
头部模糊匹配 where profession like '%工程' 改为尾部模糊匹配:where profession like '软件%'(索引生效)
or 连接无索引字段 where phone='17799990015' or age=23(age 无索引) 给 age 建索引,或拆分 SQL 为两个查询
数据分布影响 where phone>='17799990005'(匹配 90% 数据) MySQL 会判断全表扫描更快,索引失效,需调整查询条件或分表
使用 not in、is not null where id not in (1,2,3) 改为where id <1 or id>3(范围查询,索引生效)
联合索引字段顺序错误 索引 (a,b,c),查询where b=? and c=? 调整查询条件包含 a,或重建索引为 (b,c)
用函数或表达式修改索引列 where date(create_time)='2024-01-01' 改为where create_time between '2024-01-01 00:00:00' and '2024-01-01 23:59:59'

4. 优化技巧:覆盖索引与回表查询

  • 回表查询:二级索引查询后需通过主键查询聚集索引(如select * from user where name='Arm',name 是二级索引,需回表获取其他字段)
  • 覆盖索引:查询的字段全部在索引中,无需回表(如select id,name from user where name='Arm',id 和 name 都在二级索引中)
  • 优化方案:设计索引时,将查询常用字段纳入联合索引(如create index idx_name_id on user(name,id)),避免select *

5. 前缀索引与联合索引选择

  • 前缀索引:适用于长字符串字段(如 email、address),截取前 N 个字符建索引(如email(5)),需保证选择性(建议不低于 0.8)
  • 联合索引:多查询条件时优先使用(如where a=? and b=?),比多个单列索引更高效(MySQL 仅会选择一个单列索引,联合索引可同时使用多个字段)

七、索引设计原则

  1. 按需创建:仅对查询频繁的字段建索引(如 where、order by、group by 中的字段),避免冗余索引
  2. 优先联合索引:多条件查询时,联合索引比多个单列索引更高效(减少索引数量,降低维护成本)
  3. 区分度优先:选择区分度高的字段建索引(如手机号、身份证号,区分度 = 1),避免对性别、状态等区分度低的字段建索引
  4. 控制索引数量:单表索引不超过 5 个(索引越多,增删改维护成本越高)
  5. 字符串用前缀索引:长字符串字段(如 varchar (255))用前缀索引减少索引大小
  6. 避免过度索引:不要对频繁更新的字段建过多索引(更新字段时需同步维护所有相关索引)
  7. 主键用自增 ID:自增主键保证顺序插入,避免 B+Tree 页分裂(UUID 等无序主键会导致频繁页分裂,性能下降)
  8. 利用覆盖索引:查询字段尽量在索引中,减少回表查询

总结

索引是 MySQL 性能优化的核心,其本质是 “以空间换时间” 的权衡。掌握 B+Tree 底层原理、索引分类、使用规则和设计原则。实际开发中,需结合业务场景(查询频率、数据分布、更新频率)设计索引,通过explain和慢查询日志持续优化,才能让数据库查询性能最大化。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐