前阵子有个项目上线后出现性能问题,页面加载动不动就超时。一开始以为是代码写得烂,后来查了半天,发现是数据库索引没做好。
说实话,索引这东西说简单也简单,说复杂也复杂。很多开发者知道要加索引,但加在哪些字段、怎么加、加了之后会不会有副作用,这些其实都有讲究。今天就用几个实际遇到的案例来聊聊。
一、最基本的原则:where条件和join字段要加索引
这个可能很多人都知道,但实际开发中还是会漏。举个例子:
SELECT * FROM orders WHERE user_id = 123 AND status = 1
这种查询,user_id和status都应该加索引。但要注意,如果查询条件是AND关系,最好建联合索引,而不是单独建两个索引。为什么?因为MySQL一次查询只能用一个索引(大多数情况下),联合索引的效率更高。
联合索引的顺序也有讲究,要把区分度高的字段放前面。比如status只有0和1两个值,区分度就很低;user_id有成千上万个值,区分度就高。所以应该建(user_id, status)而不是(status, user_id)。
二、别忽略ORDER BY和GROUP BY
很多时候我们只注意WHERE条件,忘了ORDER BY和GROUP BY也需要索引。特别是数据量大的时候,file sort真的会很慢。
SELECT * FROM articles ORDER BY create_time DESC LIMIT 10
这种查询,create_time最好有索引,不然每次都要全表扫描再排序,性能可想而知。
三、索引不是越多越好
有个项目之前维护的人给每个字段都加了索引,结果INSERT和UPDATE慢得要死。因为每次写入数据都要更新索引,索引越多,写入越慢。
建议:一个表的索引数量控制在5个以内,超过这个数就要好好考虑一下了。
四、小心索引失效的情况
这个坑我踩过。建了索引,但查询还是走全表扫描,后来才发现是索引失效了。常见的失效场景:
1. 使用函数或表达式
WHERE DATE(create_time) = \'2024-01-01\'
这样写索引会失效,应该改成范围查询
2. 隐式类型转换
字段是varchar,但查询用了int类型的值,MySQL会自动转换类型,索引就失效了
3. LIKE以通配符开头
WHERE name LIKE \'%张三\'
这种写法索引不生效,但\'张三%\'是可以的
4. OR条件有一边没索引
如果OR两边的字段不是都有索引,整个索引都会失效
五、一个实际案例
有个报表查询,一次要查好几分钟。看了下SQL,大概是这样:
SELECT * FROM logs WHERE DATE(create_time) = CURDATE() AND type IN (1,2,3)
问题很明显:DATE函数让create_time的索引失效了,type字段没索引。
优化方案:
1. 把DATE(create_time)改成范围查询:create_time >= CURDATE() AND create_time < CURDATE() + INTERVAL 1 DAY
2. 给type字段加索引
改完之后,查询时间从3分钟降到了200毫秒。
六、定期检查索引使用情况
建了索引不代表就万事大吉了,要定期看看索引的使用情况。MySQL有个sys库,可以查到哪些索引从来没被用过:
SELECT * FROM sys.schema_unused_indexes;
对于那些从来没被用过的索引,可以考虑删掉,省得浪费资源。
索引优化是个需要持续关注的事情,不是一劳永逸的。希望这些案例能对你有帮助。
