Jw
首页
技术
英语
首页
技术
英语
  • MySQL

    • MySQL 基础
    • 环境变量配置(PATH)
    • SQL 语法与分类
    • MySQL 数据类型
    • SQL - DDL 数据定义语言
    • SQL - DML 数据操作语言
    • SQL - DQL 数据查询语言
    • SQL - DCL 数据控制语言
    • MySQL 函数
    • SQL 约束
    • SQL 外键约束
    • 多表关系
    • 多表查询(连接查询)
    • 隐式内连接 vs 显式内连接
    • 子查询
    • 多表查询案例
    • 事务
    • 并发事务问题与隔离级别
    • MySQL 体系结构与存储引擎
    • 索引概述
    • 索引结构
    • 索引分类
    • 索引语法
    • SQL 性能分析
    • 索引使用
    • 索引设计原则
    • SQL 优化 - 插入数据与主键优化
    • SQL 优化 - order by 与 group by
    • SQL 优化 - limit / count / update
    • 视图
    • 存储过程
    • 存储过程 - 变量
    • 存储过程 - 流程控制、游标与条件处理
    • 存储函数
    • 触发器
    • 锁概述与全局锁
    • 表级锁
    • 行级锁
    • InnoDB 逻辑存储结构
    • InnoDB 架构
    • 事务原理(redo / undo log)
    • MVCC
    • MySQL 系统数据库
    • MySQL 常用工具
    • 数据备份与导入导出
    • 错误日志与二进制日志
    • 查询日志与慢查询日志
    • 主从复制 - 概述与原理
    • 主从复制 - 搭建
    • Mycat 配置
    • Mycat 垂直拆分与水平拆分
    • Mycat 分片规则
    • Mycat 管理与监控
    • Mycat 读写分离 - 一主一从
    • Mycat 读写分离 - 双主双从

索引使用

索引怎么用才生效:最左前缀法则、常见失效场景、SQL 提示、覆盖索引与前缀索引。

一、验证索引效率

tb_sku 表按 sn 查询,建索引前后对比:

SELECT * FROM tb_sku WHERE sn = '100000003145001';   -- 未建索引:20.78 sec
CREATE INDEX idx_sku_sn ON tb_sku(sn);               -- 为 sn 建索引
SELECT * FROM tb_sku WHERE sn = '100000003145001';   -- 建索引后:0.00 sec

海量数据下,无索引的等值查询只能全表扫描,耗时从「秒级」降到「毫秒级」。

二、最左前缀法则

对联合索引,查询条件必须从索引的最左列开始,并且不跳过中间列;跳过某一列,则该列之后的字段索引失效。

以 idx_user_pro_age_sta(profession, age, status) 为例:

查询条件typekey_len结论
profession = '软件工程' and age = 31 and status = '0'ref54三列全部命中
age = 31 and status = '0' and profession = '软件工程'ref54条件顺序可任意,仍全部命中
profession = '软件工程' and age = 31ref54命中前两列
profession = '软件工程'ref47只命中第一列
profession = '软件工程' and age > 30 and status = '0'range49范围查询右侧的 status 失效
age = 31 and status = '0'all—跳过最左列,索引失效

三、索引失效场景

场景原因 / 示例
联合索引范围查询范围条件(>、<)右侧的列索引失效,如 profession='软件工程' and age > 30 and status='0'
索引列参与运算在索引列上做函数/运算会失效:where substring(phone,10,2) = '15' → 全表
字符串不加引号字符串字段不加引号会隐式类型转换,索引失效:where phone = 17799990015 → 全表
头部模糊匹配like '%工程'、like '%工%' 失效;like '软件%'(尾部模糊)不失效
or 连接的条件or 前后都需要有索引:where id = 10 or age = 23,age 无索引时整体不走索引(key = NULL);给 age 建索引后变为 index_merge,key = PRIMARY,idx_user_age
数据分布影响优化器评估「走索引比全表更慢」时就不走索引。如 phone >= '17799990000' 命中大部分行 → type=all;phone >= '17799990020' 命中少量行 → type=range
is null / is not null是否走索引同样取决于数据分布,与字段取值比例有关,两种写法都可能全表

判断依据看 explain 的 type(all 即全表)与 key(NULL 即未用索引)。

四、SQL 提示

人为建议优化器使用 / 忽略某个索引:

-- 建议使用指定索引
explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';

-- 忽略指定索引
explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程';

-- 强制使用指定索引
explain select * from tb_user force index(idx_user_pro) where profession = '软件工程';

三者都是「提示」,最终是否采用仍由优化器决定(force index 例外,它要求必须使用)。

五、覆盖索引

覆盖索引:查询使用了索引,并且需要返回的列在该索引中都能找到 → 不需要回表,效率更高。

以 idx_user_pro_age_sta(profession, age, status) 为例:

-- Extra: Using where; Using index —— 返回列都在索引中,无需回表
explain select id, profession, age, status from tb_user
where profession = '软件工程' and age = 31 and status = '0';

-- Extra: Using index condition —— 返回的 name 不在索引中,需要回表
explain select id, profession, age, status, name from tb_user
where profession = '软件工程' and age = 31 and status = '0';

explain select * from tb_user
where profession = '软件工程' and age = 31 and status = '0';

尽量用覆盖索引、少用 select *:id 是二级索引叶子节点自带的,所以 select id 也算覆盖。

六、前缀索引

字符串字段(varchar / text)很长时,整列建索引会占用大量空间、浪费磁盘 IO,此时只对前 n 个字符建索引:

CREATE INDEX idx_xxxx ON table_name(column(n));

n 取多少取决于选择性:选择性 = 不重复的索引值(基数)/ 表记录总数,越高越好,唯一索引的选择性为 1。

-- 整列的选择性
select count(distinct email) / count(*) from tb_user;
-- 前 5 个字符的选择性(接近整列即可)
select count(distinct substring(email,1,5)) / count(*) from tb_user;

create index idx_email_5 on tb_user(email(5));
show index from tb_user;   -- Sub_part 列显示前缀长度

查询流程:先在辅助索引 email(5) 上定位到前缀匹配的记录(拿到 id),再回表取出完整行、比较完整值,若有多条前缀相同的记录需要多次回表。

七、单列索引与联合索引

  • 单列索引:一个索引只包含单个列。
  • 联合索引:一个索引包含多个列。

多个查询条件时,建议建联合索引而不是多个单列索引:

-- 已有 idx_user_phone(phone)、idx_user_name(name)
explain select id, phone, name from tb_user where phone = '17799990010' and name = '韩信';
-- key = idx_user_phone —— 优化器会评估哪个字段的索引效率更高,选择其一

-- 建联合索引后
create unique index idx_phone_name on tb_user(phone, name);
explain select id, phone, name from tb_user use index(idx_phone_name)
where phone = '17799990010' and name = '韩信';
-- key_len = 249,Extra = Using index(覆盖索引)

多条件联合查询时,MySQL 优化器会评估哪个字段的索引效率更高,选择该索引完成本次查询。

相关

  • SQL 性能分析:explain 各字段含义
  • 索引设计原则:建索引的取舍原则
  • 索引概述 / 索引结构 / 索引分类 / 索引语法
最近更新: 2026/10/10 23:52
Contributors: liujw
Prev
SQL 性能分析
Next
索引设计原则