你有没有遇到过这种情况:系统用着用着,查个数据要等好几秒,点一下搜索就像在等开水烧开。其实问题很可能不在网络,也不在硬件,而是数据库的索引没整明白。
索引到底是什么?
你可以把数据库表想象成一本厚厚的电话簿,如果想找到“张伟”的号码,只能一页页翻,效率当然低。但如果你有个按姓名拼音排序的目录,一下就能定位到位置。这个目录,就是索引。
索引的本质是为字段建立一种快速查找的数据结构,最常见的就是B+树。它能让原本需要全表扫描的操作变成几次磁盘读取就搞定。
什么时候该建索引?
别一上来就给所有字段都加索引。索引不是免费的,每次插入、更新、删除数据时,索引也得跟着变,写操作会变慢。而且索引还占磁盘空间。
真正该建索引的是那些经常出现在WHERE条件里的字段。比如用户登录频繁通过email查询,那就在email字段上建索引:
CREATE INDEX idx_user_email ON users(email);
如果你经常按创建时间排序查订单,那就给create_time加索引:
CREATE INDEX idx_order_create_time ON orders(create_time DESC);
复合索引有讲究
有时候查询条件不止一个字段,比如既要查状态又要查时间范围。这时候可以建复合索引:
CREATE INDEX idx_order_status_time ON orders(status, create_time);
注意顺序很重要。这个索引能高效支持“status=1”或“status=1 AND create_time > '2024-01-01'”的查询,但如果你只查create_time,这个索引大概率用不上。
口诀是:等值字段放前面,范围查询放后面。
别被“假命中”骗了
有时候EXPLAIN看执行计划显示用了索引,但查询还是很慢。可能是因为索引虽然命中了,但还是要回表查大量数据。比如你查“status=0”的订单,结果有上万条,数据库就得一次次去主表拿数据,I/O压力很大。
解决办法之一是覆盖索引。把查询需要的字段都包含在索引里,避免回表:
CREATE INDEX idx_covering ON orders(status, create_time, user_id, amount);
这样SELECT这几个字段时,直接从索引就能拿到结果,速度提升明显。
查询语句写法也影响性能
哪怕有索引,写法不对也白搭。比如:
SELECT * FROM users WHERE YEAR(create_time) = 2024;
这种对字段使用函数的方式,会让索引失效。应该改成:
SELECT * FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';
这样才能利用到索引的有序性。
定期检查和清理
项目上线后别忘了回头看。可以用数据库自带的工具分析哪些索引长期没被使用,及时删掉。MySQL可以用sys.schema_unused_indexes视图查看。
同时关注那些查询慢的日志,结合EXPLAIN分析执行计划,看看是不是缺了关键索引,或者现有索引设计不合理。