一、先讲个真实故事:一次索引优化的威力
上周帮朋友公司排查一个生产问题:用户注册接口响应时间从200ms暴涨到8秒。查看慢查询日志,发现一条简单的SELECT * FROM users WHERE phone='138xxxx'竟然执行了6.8秒!
问题根源:2000万用户表,phone字段没有索引,每次查询都在做全表扫描。
解决方案:给phone字段加索引后,查询时间直接降到0.002秒,性能提升3400倍!
这就是索引的魔法。接下来带你解锁MySQL索引的正确使用姿势。
二、索引原理大白话(新手必看)
1. 索引就像字典的目录
没有索引时找数据:
SELECT * FROM book WHERE content='abc' ➔ 需要逐页翻完整本书
加了索引后:
直接查目录找到页码 ➔ 精准定位内容
2. 底层数据结构
- B+树:MySQL最常用的索引结构(平衡多路查找树)
- 哈希索引:Memory引擎专用,适合等值查询
- 全文索引:用于文本搜索(倒排索引)
B+树索引结构示意图(文字描述版)
[根节点]
/ | \
[非叶节点A] [非叶节点B] [非叶节点C]
/ | \ / | \ / | \
[叶子节点1]->[叶子节点2]->[叶子节点3]->...(双向链表连接)
关键特征标注建议:
-
树形结构:3层结构(根节点-非叶节点-叶子节点)
-
节点内容:
- 非叶节点:存储键值 + 子节点指针(如
[15][→] [30][→] [45][→]) - 叶子节点:存储完整数据/主键 + 前后指针(双向链表)
- 非叶节点:存储键值 + 子节点指针(如
-
数据连续性:用箭头表示叶子节点之间的链表连接
-
平衡特性:所有叶子节点在同一层
三、必须掌握的6大核心技巧
1. 最左匹配原则(联合索引的精髓)
创建联合索引:
ALTER TABLE orders ADD INDEX idx_status_created(status, created_at);
生效场景:
- WHERE status=1 ➔ ✅
- WHERE status=1 AND created_at>'2023-01-01' ➔ ✅
- WHERE created_at>'2023-01-01' ➔ ❌ (跳过了最左字段)
2. 避免索引失效的坑
-- 案例:2000万用户表查询
SELECT * FROM users
WHERE YEAR(create_time) = 2023; -- ❌ 函数导致索引失效
-- 正确写法
SELECT * FROM users
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'; -- ✅
其他失效场景:
- 使用
!=或NOT IN - 类型转换(如字符串字段用数字查询)
- LIKE以通配符开头
'%abc'
3. 覆盖索引的妙用
-- 普通查询(需要回表)
SELECT * FROM products WHERE category='手机';
-- 覆盖索引优化
ALTER TABLE products ADD INDEX idx_category_name(category, name);
SELECT category, name FROM products WHERE category='手机'; -- ✅ 直接走索引
4. 巧用前缀索引
当索引长文本字段时:
-- 完整索引(占用空间大)
ALTER TABLE articles ADD INDEX idx_content(content);
-- 优化:取前20字符
ALTER TABLE articles ADD INDEX idx_content(content(20));
5. 索引下推(Index Condition Pushdown,ICP)
MySQL 5.6+的黑科技:
SELECT * FROM users
WHERE age > 18
AND name LIKE '张%';
-- 没有ICP:先通过age>18筛选,再回表查name
-- 有ICP:在存储引擎层直接过滤age和name
6. 唯一索引的取舍
优点:
- 保证数据唯一性
- 查询性能稍优于普通索引
代价:
- 插入时需要唯一性校验
- 影响写入性能(高并发场景)
四、真实场景案例库
案例1:电商订单查询优化
-- 原始SQL(执行时间2.3秒)
SELECT * FROM orders
WHERE user_id=123
AND status=3
ORDER BY create_time DESC
LIMIT 10;
-- 优化方案:创建联合索引
ALTER TABLE orders ADD INDEX idx_user_status_time(user_id, status, create_time);
-- 执行时间降为0.015秒
案例2:千万级用户表分页
-- 错误写法(越往后越慢)
SELECT * FROM users
ORDER BY id
LIMIT 1000000, 10;
-- 优化方案:游标分页
SELECT * FROM users
WHERE id > 1000000
ORDER BY id
LIMIT 10;
五、这些坑千万别踩!
-
乱建索引综合症
- 每个字段都加索引 ➔ 索引占用空间超过数据本身
- 更新变慢:每次INSERT/UPDATE都要维护多个索引
-
过度依赖执行计划
EXPLAIN不是万能药,要注意:- 数据量变化后执行计划可能改变
- 索引统计信息不及时(可用
ANALYZE TABLE刷新)
-
忽视隐式类型转换
-- phone是varchar类型
SELECT * FROM users WHERE phone=13800138000; -- ❌ 触发类型转换
六、性能监控三板斧
- 慢查询日志分析
-- 开启慢日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 超过1秒的查询
- 查看索引使用情况
SHOW INDEX FROM table_name;
-- 关注Cardinality(基数),越接近行数越好
- 性能分析神器
EXPLAIN SELECT ...;
-- 重点关注:
-- type(最好到const/ref)
-- rows(扫描行数)
-- Extra(Using index/Using filesort)
七、终极使用原则
-
黄金平衡点
索引数量 = 写入性能 vs 查询性能的平衡点 -
定期体检制度
- 每月分析慢查询日志
- 清理无用索引(推荐使用pt-index-usage工具)
-
不要盲目相信经验
用数据说话:- 测试环境压测
- 生产环境A/B测试
结语
索引就像数据库的加速器,但使用不当就会变成性能杀手。记住:最好的索引策略是根据业务场景量身定制。现在就去检查你的数据库,把学到的技巧用起来吧!
思考题:你在工作中遇到过哪些有意思的索引优化案例?欢迎在评论区分享你的实战经验!