索引创建占用内存?别让数据库悄悄吃掉你的服务器成本

最近帮朋友看一个项目,网站突然变慢,服务内存直接飙到95%以上。查了一圈才发现,问题出在数据索引创建上。他为了加快查询速度,在一张上千万数据的表上一口气建了五六个复合索引,结果MySQL在执行ALTER TABLE的时候,内存瞬间被占满,差点把服务搞崩。

索引不是免费的,尤其是创建过程

很多人以为索引只是存硬盘上的结构,不影响运行内存。其实大错特错。当你执行CREATE INDEX时,数据库要把大量数据读进内存排序、构建B+树结构,这个过程非常吃内存。特别是大表,可能一下子就要几百MB甚至几个GB的临时内存。

比如你在MySQL里执行:

CREATE INDEX idx_user_email ON users(email);

如果users表有1000万条数据,这条命令可能在后台触发一个全表扫描加排序操作,innodb_sort_buffer_size和sort_buffer_size这些参数设置得不够,就会频繁使用磁盘临时文件,反而更慢,还卡住其他请求。

线上操作要小心,别在高峰期建索引

曾经见过一个团队,在上午10点业务高峰期给订单表加索引,结果数据库连接池被锁死,前端接口大面积超时。后来他们改到凌晨2点用pt-online-schema-change工具操作,整个过程平滑很多。

像PostgreSQL虽然支持CONCURRENTLY建索引,不阻塞写入,但照样会增加CPU和内存负担:

CREATE INDEX CONCURRENTLY idx_order_status ON orders(status);

这条命令不会锁表,可它会在后台慢慢跑,期间持续占用资源,监控没跟上的话,很容易被忽略。

小内存服务器更要精打细算

如果你用的是4GB内存的VPS跑MySQL,最好别在单表超过50万行时直接建索引。可以先在测试环境模拟,看看内存峰值。或者拆成小批量处理,用程序分页建索引。

还有个实用技巧:先把不必要的索引删掉。有些字段明明很少查询,却被加上了索引,不仅占磁盘,每次更新还拖慢写入速度。定期用以下命令检查冗余索引:

SELECT * FROM sys.schema_unused_indexes;

这是MySQL自带的performance_schema提供的视图,能帮你找出那些从来没被用过的索引,删一个省一点内存和IO。

索引该建还得建,但得讲究时机和方式。别让“优化”变成了“拖累”,省下的不只是钱,还有半夜被报警叫醒的烦恼。