很多人在优化数据库时,第一反应就是加索引。查询慢?加个索引试试。但索引真不是越多越好,就像家门口的快递柜,放几个方便取件,要是整栋楼全是快递柜,走路都费劲。
索引的作用:让查询更快
索引的本质是为数据建立“目录”。比如你在图书馆找一本书,如果没有分类目录,就得一本本翻。有了索引,数据库就能快速定位到目标记录。常见的 B+ 树索引能让查询从全表扫描变成几毫秒内完成。
比如有张用户表 user,按手机号查信息:
SELECT * FROM user WHERE phone = '13800138000';
如果 phone 字段没有索引,就得扫完整张表。加上索引后,查询速度可能从 2 秒降到 0.02 秒。
索引的代价:写操作变慢,存储占用上升
每增加一个索引,数据写入时就要同步更新多个结构。INSERT、UPDATE、DELETE 都会变慢。因为不只是改数据行,还得维护所有相关索引树。
举个例子,你往订单表插一条数据,如果这个表有 5 个索引,那这条记录要在主键索引、用户ID索引、时间索引、状态索引、商品ID索引里各插入一次。相当于搬一次家要通知五个亲戚,效率自然下降。
另外,每个索引都占磁盘空间。一张千万级的表,一个普通二级索引可能多占几百MB甚至上GB空间。服务器硬盘不是大风刮来的,尤其云服务按量计费,成本实实在在。
冗余索引和重复索引的问题
有些开发图省事,给 (user_id) 加了索引,又给 (user_id, status) 加一个,结果前者被后者覆盖,(user_id) 单独查询时也能用复合索引,前面那个单列索引就成了摆设。
更麻烦的是,MySQL 虽然能用最左前缀匹配复合索引,但顺序很重要。比如有索引 (a,b,c),查询 WHERE b=1 是用不了这个索引的,得从 a 开始。
什么时候该建索引?
优先给经常出现在 WHERE、JOIN、ORDER BY 里的字段加。比如登录频繁查用户名,那就给 username 加;排序常按创建时间,就考虑给 create_time 建索引。
但也要看数据分布。如果某个字段只有两个值,比如性别(男/女),加索引意义不大,因为筛选效果差,俗称“选择性低”。
监控和清理才是关键
线上系统应该定期检查哪些索引从来没被用过。MySQL 可以通过 performance_schema 查索引使用情况。长时间没被访问的索引,可以考虑删除。
有个电商后台,订单表建了七八个索引,后来发现其中三个一年都没被任何 SQL 用上,删掉之后写入性能提升了 15%,还省了 2GB 存储。
索引不是一劳永逸的东西。业务变化了,SQL 改写了,原来有用的索引可能变鸡肋。定期 review 比盲目添加更重要。