topic: backend
MySQL 索引优化初体验
2020 年 6 月,被一个 SQL 查询坑惨了。
问题
一个看起来很简单的查询,跑了 30 秒没出结果:
1 | SELECT * FROM orders |
orders 表,100 万条数据。
排查
首先怀疑网络问题 ping 了一下,没问题。
然后看执行计划:
1 | EXPLAIN SELECT * FROM orders |
结果吓一跳:type: ALL,rows: 1000000
全表扫描!100 万条数据一条一条遍历。
解决
加索引:
1 | CREATE INDEX idx_user_status_time ON orders(user_id, status, created_at); |
再跑一次:
1 | EXPLAIN SELECT * FROM orders |
这次 type: ref,rows: 15
0.01 秒出结果。
索引知识总结
什么时候加索引
- WHERE 条件中经常出现的列
- JOIN 的关联列
- 排序 ORDER BY 的列
- 覆盖索引:查询的列正好在索引中
索引类型
- 主键索引:唯一且非空
- 唯一索引:唯一但允许空
- 普通索引:普通列
- 复合索引:多列组合
复合索引最左前缀
1 | CREATE INDEX idx_a_b_c ON table(a, b, c); |
这个索引可以用于:
- WHERE a = xxx
- WHERE a = xxx AND b = xxx
- WHERE a = xxx AND b = xxx AND c = xxx
但不能用于:
- WHERE b = xxx
- WHERE c = xxx
什么时候索引失效
- LIKE ‘%xxx%’
- OR 导致索引失效
- 函数作用于索引列
- 数据类型隐式转换
后续优化
1. 覆盖索引
1 | CREATE INDEX idx_user_status_time ON orders(user_id, status, created_at) |
查询不需要回表。
2. 慢查询日志
开启慢查询日志:
1 | SET GLOBAL slow_query_log = 'ON'; |
定期分析慢查询,优化索引。
3. 分表
数据量太大,考虑分库分表:
- 水平分表:按时间、按用户ID
- 垂直分表:按业务拆分
感悟
索引是 MySQL 优化的第一步,也是最重要的一步。
但索引不是万能的:
- 索引占用空间
- 写入时维护索引有开销
- 过多索引反而变慢
经验:
- 善用 EXPLAIN
- 关注慢查询日志
- 索引优化是持续的过程
现在每次写 SQL,我都会先想:有没有索引?能不能用上?