当前位置: 首页 > news >正文

MySQL索引指南

文章目录

  • 第一部分:索引基础
    • 一、索引的本质
      • 1.1 什么是索引?
      • 1.2 索引解决的核心问题
      • 1.3 索引的核心思想
    • 二、索引的底层数据结构
      • 2.1 为什么选择B+树?
        • 常见数据结构对比
        • 为什么不用其他结构?
      • 2.2 B+树详解
        • B+树的特点
        • B+树容量计算
  • 第二部分:索引类型
    • 三、MySQL索引分类
      • 3.1 按数据结构分类
      • 3.2 按物理存储分类
      • 3.3 按逻辑功能分类
      • 3.4 按字段数量分类
      • 3.5 按功能分类
    • 四、主键索引与唯一索引对比
      • 4.1 核心区别
      • 4.2 NULL值处理差异
      • 4.3 使用场景对比
    • 五、聚簇索引与非聚簇索引
      • 5.1 聚簇索引(InnoDB主键)
      • 5.2 非聚簇索引(二级索引)
      • 5.3 InnoDB vs MyISAM
  • 第三部分:索引失效
    • 六、索引失效的13种场景
      • 6.1 索引列使用函数或表达式
      • 6.2 隐式类型转换
      • 6.3 LIKE前缀模糊
      • 6.4 OR条件有非索引列
      • 6.5 负向查询
      • 6.6 违反最左前缀原则
      • 6.7 范围查询后的列失效
      • 6.8-6.13 其他失效场景
    • 七、如何诊断索引问题
      • 7.1 使用EXPLAIN
      • 7.2 查看索引统计
  • 第四部分:索引优化
    • 八、索引设计原则
      • 8.1 什么时候建索引
      • 8.2 索引设计6大原则
      • 8.3 联合索引设计
    • 九、索引优化实战
      • 9.1 案例1:函数导致索引失效
      • 9.2 案例2:覆盖索引优化
      • 9.3 案例3:深度分页优化
  • 第五部分:面试必备
    • 十、高频面试问题
      • Q1:什么是索引?作用是什么?
      • Q2:为什么使用B+树?
      • Q3:聚簇索引和非聚簇索引的区别?
      • Q4:主键索引和唯一索引的区别?
      • Q5:什么是覆盖索引?
      • Q6:联合索引的最左前缀原则?
      • Q7:什么情况下索引会失效?
      • Q8:如何设计高效索引?
      • Q9:索引越多越好吗?
      • Q10:主键为什么推荐自增整型?
    • 总结
      • 索引核心知识图谱
      • 索引使用口诀


第一部分:索引基础

一、索引的本质

1.1 什么是索引?

索引(Index) 是一种数据结构,用于帮助数据库高效地查找数据。

类比理解

图书馆找书的方式:没有索引 = 逐本翻找(全表扫描)
有索引   = 通过目录快速定位(索引查找)书籍目录:
- 章节目录:按顺序组织(B+树)
- 关键词索引:按字母排序(索引)
- 页码:快速定位到具体位置(主键)

1.2 索引解决的核心问题

问题场景

-- 100万条用户数据
SELECT * FROM users WHERE name = '张三';

无索引

  • 需要扫描100万行数据(全表扫描)
  • 时间复杂度:O(n)
  • IO次数:取决于表的大小

有索引

  • 通过B+树快速定位
  • 时间复杂度:O(log n)
  • IO次数:通常3-4次(树的高度)

性能对比

数据量      无索引耗时    有索引耗时    提升倍数
1万       10ms          1ms           10倍
10万      100ms         1ms           100倍
100万     1000ms        1ms           1000倍
1000万    10000ms       2ms           5000倍

1.3 索引的核心思想

  1. 空间换时间:额外存储索引数据,换取查询速度
  2. 有序结构:数据按特定规则排列,支持快速查找
  3. 减少IO:减少磁盘访问次数(数据库性能瓶颈)

二、索引的底层数据结构

2.1 为什么选择B+树?

常见数据结构对比
数据结构查找时间优点缺点适用场景
数组O(n)简单查询慢小数据量
有序数组O(log n)查询快插入/删除慢静态数据
链表O(n)插入快查询慢不适合索引
二叉搜索树O(log n) ~ O(n)平衡可能退化内存结构
AVL树O(log n)严格平衡旋转成本高内存结构
红黑树O(log n)平衡树高较高内存结构
哈希表O(1)查询极快不支持范围查询等值查询
B树O(log n)多路查找非叶子节点存数据文件系统
B+树O(log n)范围查询快结构复杂数据库索引
为什么不用其他结构?

1. 哈希表

优点:O(1) 查找
缺点:❌ 不支持范围查询(WHERE age > 20)❌ 不支持排序(ORDER BY)❌ 不支持模糊查询(LIKE 'zhang%')❌ 哈希冲突问题使用场景:Memory引擎的HASH索引

2. 二叉搜索树/红黑树

缺点:❌ 树高太高:100万数据,红黑树高度约20层 = 20次IO❌ 每个节点只存一个值,IO利用率低❌ 不适合磁盘存储B+树高度:100万数据,高度约3-4层 = 3-4次IO

3. B树

缺点:❌ 非叶子节点存储数据,导致每个节点存储的索引项更少❌ 范围查询需要中序遍历,效率低B+树优势:✅ 非叶子节点只存索引,每个节点可存更多索引项✅ 叶子节点用链表连接,范围查询效率高

2.2 B+树详解

B+树的特点
B+树结构示例(3阶B+树):[20, 50]               ← 根节点(只存索引)/    |    \[10,15]  [30,40]  [60,70]     ← 中间节点(只存索引)/  |  \   /  |  \   /  |  \[...] [...] [...] [...] [...] ← 叶子节点(存数据+指针)↔     ↔     ↔     ↔     ↔   ← 双向链表

核心特性

  1. 所有数据都在叶子节点

    • 非叶子节点只存索引键
    • 叶子节点存完整数据(或指针)
  2. 叶子节点形成有序链表

    • 支持高效的范围查询
    • 支持顺序遍历
  3. 高度平衡

    • 所有叶子节点在同一层
    • 查询任意数据的IO次数一致
  4. 多路查找

    • 每个节点可以有多个子节点(不是二叉)
    • 减少树的高度,减少IO次数
B+树容量计算

假设

  • InnoDB页大小:16KB
  • 索引键(bigint):8字节
  • 指针大小:6字节
  • 数据行大小:1KB

非叶子节点

每个节点能存储的索引项数 = 16KB / (8B + 6B) ≈ 1170个3层B+树能存储的数据量:第1层(根):1个节点第2层:1170个节点第3层(叶子):1170 × 1170 = 1,368,900个节点每个叶子节点存储数据:16KB / 1KB = 16条
总数据量:1,368,900 × 16 ≈ 2000万条结论:3次IO可以查询2000万数据!

第二部分:索引类型

三、MySQL索引分类

3.1 按数据结构分类

  • B+树索引(默认)
  • Hash索引(Memory引擎)
  • 全文索引(FULLTEXT)
  • 空间索引(SPATIAL)

3.2 按物理存储分类

  • 聚簇索引(Clustered Index)
  • 非聚簇索引(Secondary Index)

3.3 按逻辑功能分类

  • 主键索引(PRIMARY KEY)
  • 唯一索引(UNIQUE)
  • 普通索引(INDEX)
  • 全文索引(FULLTEXT)

3.4 按字段数量分类

  • 单列索引
  • 联合索引(复合索引)

3.5 按功能分类

  • 覆盖索引(Covering Index)
  • 前缀索引(Prefix Index)
  • 索引下推(ICP)

四、主键索引与唯一索引对比

4.1 核心区别

特性主键索引唯一索引
NULL值不允许允许(可多个NULL)
数量只能1个可以多个
索引类型聚簇索引(InnoDB)二级索引
性能⭐⭐⭐⭐⭐ 最快(无需回表)⭐⭐⭐⭐ 较快(需回表)
作用唯一标识每一行保证业务唯一性

关系

主键 = 唯一索引 + NOT NULL + 聚簇索引(InnoDB) + 只能一个

4.2 NULL值处理差异

-- 主键:不允许NULL
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO t1 VALUES (NULL, '张三');  -- ❌ 报错-- 唯一索引:允许多个NULL
CREATE TABLE t2 (id INT PRIMARY KEY AUTO_INCREMENT,email VARCHAR(100) UNIQUE
);
INSERT INTO t2 (email) VALUES (NULL);  -- ✅ 成功
INSERT INTO t2 (email) VALUES (NULL);  -- ✅ 成功(SQL标准:NULL != NULL)
INSERT INTO t2 (email) VALUES ('a@example.com');  -- ✅ 成功
INSERT INTO t2 (email) VALUES ('a@example.com');  -- ❌ 报错(重复)

4.3 使用场景对比

-- 主键索引:系统标识
CREATE TABLE users (id BIGINT PRIMARY KEY AUTO_INCREMENT,  -- 主键:系统唯一标识username VARCHAR(50) UNIQUE,           -- 唯一索引:业务约束email VARCHAR(100) UNIQUE,             -- 唯一索引:业务约束phone VARCHAR(20) UNIQUE               -- 唯一索引:业务约束
);-- 订单表:主键 + 业务唯一号
CREATE TABLE orders (id BIGINT PRIMARY KEY AUTO_INCREMENT,  -- 主键:内部IDorder_no VARCHAR(32) UNIQUE,           -- 唯一索引:业务订单号idempotent_key VARCHAR(64) UNIQUE      -- 唯一索引:幂等性控制
);

五、聚簇索引与非聚簇索引

5.1 聚簇索引(InnoDB主键)

定义:数据行和索引存储在一起,叶子节点存储完整数据行。

特点

  • ✅ 一个表只能有一个
  • ✅ 主键就是聚簇索引
  • ✅ 查询速度快(无需回表)
  • ❌ 插入可能导致页分裂

结构

聚簇索引(主键索引):[20, 50]/    |    \[10,15] [30,40] [60,70]/  |      |       |[完整数据行] [完整数据行] [完整数据行]id:10      id:20       id:30name:张三   name:李四    name:王五age:25     age:30      age:35...        ...         ...

5.2 非聚簇索引(二级索引)

定义:索引和数据分离,叶子节点存储主键值。

结构

二级索引(name索引):[李四, 王五]/    |    \[张三] [李四,刘备] [王五,赵六]|      |           |[name:张三  [name:李四   [name:王五→ id:10]   → id:20]     → id:30]↓回表查询主键索引

回表查询

SELECT * FROM users WHERE name = '张三';执行过程:
1. 在name索引中找到 '张三' → 得到主键id=10
2. 回到主键索引,通过id=10找到完整数据行
3. 返回结果共2次索引查找(2次B+树遍历)

5.3 InnoDB vs MyISAM

特性InnoDBMyISAM
主键索引聚簇索引(数据在索引中)非聚簇索引(数据在独立文件)
二级索引存储主键值存储数据地址
回表性能需要通过主键索引查询直接通过地址查询
数据文件.ibd(索引+数据).MYD(数据)+ .MYI(索引)
事务支持✅ 支持❌ 不支持
行锁✅ 支持❌ 不支持(表锁)

第三部分:索引失效

六、索引失效的13种场景

6.1 索引列使用函数或表达式

原因:破坏索引的有序性。

-- ❌ 失效
SELECT * FROM users WHERE YEAR(create_time) = 2024;
SELECT * FROM users WHERE age + 1 = 25;-- ✅ 正确
SELECT * FROM users 
WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';
SELECT * FROM users WHERE age = 24;

6.2 隐式类型转换

-- 假设phone是VARCHAR
-- ❌ 失效
SELECT * FROM users WHERE phone = 13800138000;-- ✅ 正确
SELECT * FROM users WHERE phone = '13800138000';规则:
- 字符串索引 + 数值查询 = 失效
- 数值索引 + 字符串查询 = 生效

6.3 LIKE前缀模糊

-- ❌ 失效
SELECT * FROM users WHERE name LIKE '%张三';
SELECT * FROM users WHERE name LIKE '%张三%';-- ✅ 生效
SELECT * FROM users WHERE name LIKE '张三%';

6.4 OR条件有非索引列

-- ❌ 失效
SELECT * FROM users WHERE name = '张三' OR age = 20;  -- age无索引-- ✅ 解决方案
-- 方案1:为age创建索引
-- 方案2:改写为UNION
SELECT * FROM users WHERE name = '张三'
UNION
SELECT * FROM users WHERE age = 20;

6.5 负向查询

-- ❌ 通常失效
SELECT * FROM users WHERE status != 1;
SELECT * FROM users WHERE id NOT IN (1, 2, 3);-- ✅ 改写为正向
SELECT * FROM users WHERE status IN (0, 2, 3, 4);

6.6 违反最左前缀原则

-- 假设索引:INDEX(name, age, city)-- ✅ 走索引
WHERE name = '张三'
WHERE name = '张三' AND age = 20
WHERE name = '张三' AND age = 20 AND city = '北京'-- ❌ 不走索引
WHERE age = 20
WHERE city = '北京'
WHERE age = 20 AND city = '北京'

6.7 范围查询后的列失效

-- 假设索引:INDEX(name, age, city)
SELECT * FROM users 
WHERE name = '张三' AND age > 20 AND city = '北京';
-- name和age走索引,city不走(age是范围查询)

6.8-6.13 其他失效场景

详见完整表格(篇幅原因省略,包括IS NULL、区分度低、结果集过大、IN过多、字符集不一致等)


七、如何诊断索引问题

7.1 使用EXPLAIN

EXPLAIN SELECT * FROM users WHERE name = '张三';关键字段:
- type: ALL(最差)< index < range < ref < const(最优)
- key: 实际使用的索引(NULL表示未使用)
- rows: 扫描行数(越少越好)
- Extra: - Using index(覆盖索引,最优)- Using filesort(需要排序优化)- Using temporary(需要临时表优化)

7.2 查看索引统计

-- 查看索引信息
SHOW INDEX FROM users;-- 更新统计信息
ANALYZE TABLE users;-- 查看未使用的索引
SELECT * FROM sys.schema_unused_indexes;-- 查看冗余索引
SELECT * FROM sys.schema_redundant_indexes;

第四部分:索引优化

八、索引设计原则

8.1 什么时候建索引

✅ 应该建索引

  • WHERE条件列
  • ORDER BY排序列
  • GROUP BY分组列
  • JOIN连接列
  • 高频查询列
  • 区分度高的列(>0.1)

❌ 不应该建索引

  • 区分度低的列(如性别)
  • 频繁更新的列
  • 大字段(TEXT、BLOB)
  • 很少使用的列
  • 小表(<1000行)

8.2 索引设计6大原则

1. 选择性原则:高区分度列优先
2. 最左前缀原则:联合索引按顺序使用
3. 覆盖索引原则:包含查询所需列
4. 索引顺序原则:等值>范围、高频>低频
5. 避免冗余原则:删除重复索引
6. 主键设计原则:自增整型优先

8.3 联合索引设计

-- 查询需求:WHERE status = ? AND user_id = ? ORDER BY create_time-- ✅ 推荐
CREATE INDEX idx_status_user_create 
ON orders(status, user_id, create_time);-- 理由:
-- 1. status和user_id是等值查询,放前面
-- 2. create_time是排序列,放后面(避免filesort)
-- 3. 可以覆盖索引(如果只查这些列)

九、索引优化实战

9.1 案例1:函数导致索引失效

-- ❌ 问题SQL(1.5秒)
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-01';-- EXPLAIN: type=ALL, rows=1000000-- ✅ 优化后(0.01秒)
SELECT * FROM orders 
WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';-- EXPLAIN: type=range, rows=1000, 提升150倍

9.2 案例2:覆盖索引优化

-- ❌ 问题SQL(10ms,需要回表)
SELECT user_id, order_no, amount, status 
FROM orders 
WHERE user_id = 12345;-- ✅ 建立覆盖索引
CREATE INDEX idx_user_order_amount_status 
ON orders(user_id, order_no, amount, status);-- 优化后(2ms,无需回表),提升5倍

9.3 案例3:深度分页优化

-- ❌ 慢(5秒)
SELECT * FROM orders ORDER BY id LIMIT 1000000, 10;-- ✅ 快(0.5秒)- 子查询
SELECT o.*
FROM orders o
INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 10
) t ON o.id = t.id;-- ✅ 最快(0.01秒)- 游标分页
SELECT * FROM orders 
WHERE id > 1000000  -- 上次最后一条ID
ORDER BY id LIMIT 10;

第五部分:面试必备

十、高频面试问题

Q1:什么是索引?作用是什么?

索引是一种数据结构(通常是B+树),用于帮助数据库高效查找数据。

作用

  1. 大幅提升查询速度(O(n) → O(log n))
  2. 加速排序和分组
  3. 避免全表扫描
  4. 保证数据唯一性

Q2:为什么使用B+树?

B+树 vs 红黑树

  • 红黑树:20层 = 20次IO
  • B+树:3-4层 = 3-4次IO

B+树 vs B树

  • B+树叶子节点有序链表,范围查询更快

Q3:聚簇索引和非聚簇索引的区别?

特性聚簇索引非聚簇索引
数据存储索引和数据一起索引和数据分离
叶子节点存储完整数据行存储主键值
数量限制一个表只能一个可以多个
查询性能快(无需回表)慢(需要回表)

Q4:主键索引和唯一索引的区别?

  1. NULL值:主键不允许,唯一索引允许
  2. 数量:主键1个,唯一索引多个
  3. 类型:主键是聚簇索引,唯一索引是二级索引
  4. 性能:主键查询更快(无需回表)

Q5:什么是覆盖索引?

查询的所有列都在索引中,无需回表查询。

优势:减少IO、提升速度、减少锁竞争

-- 假设索引:INDEX(name, age)-- ✅ 覆盖索引
SELECT name, age FROM users WHERE name = '张三';
-- Extra: Using index-- ❌ 非覆盖索引
SELECT * FROM users WHERE name = '张三';
-- 需要回表

Q6:联合索引的最左前缀原则?

联合索引INDEX(a, b, c)相当于创建了(a)(a,b)(a,b,c)三个索引。

查询必须包含最左列才能使用索引。

Q7:什么情况下索引会失效?

  1. 索引列使用函数
  2. 隐式类型转换
  3. LIKE前缀模糊
  4. OR条件有非索引列
  5. 负向查询
  6. 违反最左前缀
  7. 查询结果集过大

Q8:如何设计高效索引?

设计原则

  1. 选择性原则:高区分度列(>0.1)
  2. 最左前缀原则:合理安排顺序
  3. 覆盖索引原则:包含查询列
  4. 索引顺序:等值>范围、高频>低频

Q9:索引越多越好吗?

不是!索引有代价

  • ❌ 占用存储空间
  • ❌ 降低写入性能
  • ❌ 增加维护成本

建议

  • 单表索引不超过5个
  • 定期删除未使用的索引

Q10:主键为什么推荐自增整型?

特性自增整型UUID
存储4/8字节36字节
插入顺序插入随机插入,页分裂
性能整型比较快字符串比较慢
分布式需特殊处理天然支持

折中方案:雪花算法(BIGINT,有序)


总结

索引核心知识图谱

索引本质:空间换时间的数据结构↓
底层结构:B+树(多路平衡查找树)↓
索引分类:
├─ 主键索引(聚簇索引,最快)
├─ 唯一索引(二级索引,较快)
├─ 普通索引(二级索引,常用)
└─ 特殊索引(全文、空间)↓
优化要点:
├─ 避免失效(13种场景)
├─ 合理设计(6大原则)
├─ 覆盖索引(减少回表)
└─ 定期维护(ANALYZE、OPTIMIZE)↓
性能提升:O(n) → O(log n)

索引使用口诀

索引设计三原则:选择性、有序性、覆盖性
索引失效三大忌:函数、类型、通配符
索引优化三步走:EXPLAIN、分析、重构

http://www.dtcms.com/a/511778.html

相关文章:

  • 分销网站制作条件免费机械网站模板
  • day01 pyspark入门和基础环境
  • 公司网站 URL 地址规范
  • 建设一个广告联盟的网站免费祝福网页在线制作
  • 实现Trie(前缀树)
  • 杰理SDK入门教程(六):自定义按键事件
  • gray = roi.clone();和gray = roi;的区别是什么?
  • STM32中MX_TIM2_Init函数和HAL_TIM_Base_MspInit函数区别
  • 【基于Selenium的智能滑块验证码破解技术详解】
  • AI体测设备服务商
  • 网站之间如何做视频交换惠州搜索引擎优化
  • 李宏毅机器学习笔记28
  • 【开题答辩实录分享】以《智慧社区信息化服务平台》为例进行答辩实录分享
  • 京东网站建设过程东莞建设网站的位置
  • SAP SD销售订单执行跟踪报表分享
  • 数据挖掘比赛baseline参考
  • [人工智能-大模型-19]:GitHub Copilot:程序员的 AI 编程副驾驶
  • 【JVM】低延迟垃圾收集器:Shenandoah收集器与ZGC收集器
  • 深圳网站建设deyondwordpress主题wake
  • Day44 | J.U.C中的LockSupport详解
  • 网络安全生态及学习路线
  • 深度学习-卷积神经网络基础
  • 广州教育网站设计公司在建工程项目查询
  • 【瀑布流大全】分析原理及实现方式(微信小程序和网页都适用)
  • wordpress网站的常规安全设置经验分享
  • 代码随想录Day53|110. 字符串接龙、105.有向图的完全联通、106. 岛屿的周长
  • 做婚恋网站这几年做哪个网站致富
  • 【案例实战】听歌学英语鸿蒙APP从零到上架全流程回顾
  • 基于频域的数字盲水印blind-watermark
  • 三、网站开发使用软件环境中小企业建站的方法