这几天和一个做后端的朋友吃饭,他吐槽说公司业务量上来了,数据库成了最大的瓶颈。每天凌晨跑报表,得等两个小时才能出结果。我问他怎么调的,他说:“我加了索引,但感觉没啥用。”这话一听,就知道他掉进了“索引万能”的坑。

数据库优化这事儿,很多人一上来就想着加索引,觉得索引就是万能药。其实不是。索引就像书的目录,目录再详细,也不会为了查一句话就把整本书翻烂。但很多人恰恰相反,不管三七二十一,把所有字段都加上索引,结果查询没快多少,写入反而慢了。因为每次插入数据,数据库都要更新一堆索引,等于给自己挖坑。真正有用的做法是先搞清楚业务场景:是读多写少,还是写多读少?查询是频繁还是偶尔?像我们做电商的订单表,用户查询订单频率高,后台统计报表频率低。于是可以给订单表的用户ID、状态、时间这几个关键字段加索引,其他字段就别动了。索引多了,就像家里东西乱放,找东西反而更慢。
再说慢查询,这是数据库优化的另一个大坑。很多开发人员写完SQL就跑,不管它跑多久。我见过最离谱的,一个查询跑了三分钟,数据库 CPU 直接飙到 100%。怎么查慢查询?MySQL 有慢查询日志,打开它,设置阈值,比如超过 1 秒的查询都记录下来。然后逐个分析,看是索引没走对、关联表太多,还是数据量太大。我记得有个案例,,明明有索引,查询还是慢。后来一查,status 字段的值分布极不均匀,90% 的订单都是 pending,索引反而成了负担。这时索引就失效,需要考虑分区表或加缓存。
说到缓存,很多人觉得缓存就是 Redis,丢进去就行。其实没那么简单。缓存策略有很多种,常见的有缓存穿透、缓存击穿、缓存雪崩。比如缓存穿透,用户查询一个不存在的记录,缓存里没有,数据库里也没有,每次请求都打到数据库上。解决办法是布隆过滤器或缓存空值。缓存击穿时,一个热点 key 失效,大量请求同时落到数据库。可以设置热点 key 永不过期,或者使用互斥锁。缓存雪崩更麻烦,大量 key 同时失效,数据库撑不住。解决办法是给缓存过期时间加上随机值,避免集中失效。这些听起来复杂,但实际操作并不难,关键是根据业务特点选对策略。
数据库设计阶段,很多人忽视了字段类型的选择。我见过一个表,用户 ID 用 存数字,这简直是浪费。数字类型用 或 ,存储空间小,查询速度快。还有日期类型,有人用 存时间,结果排序、范围查询都慢得要命。正确的做法是用 或 。另外,字段长度设置过大也会影响性能。比如手机号固定 11 位,设 就够了,别设 255。字段越短,索引越小,查询越快。这些细节看似不起眼,却能累计成显著的性能提升。
分库分表是数据库优化的终极方案,但很多公司过早上手。我见过一家创业公司,用户才几千人,就搞了分库分表,结果开发成本翻了好几倍,运维也麻烦得要死。数据量不大的时候,先做好读写分离和缓存就够了。什么时候该分?当单表数据量超过千万级,或者查询慢到影响业务时,再考虑分。分的方式也有讲究,水平分是把同一张表的数据分散到多个库,比如按用户 ID 取模;垂直分是把不同业务的数据分开,比如把订单表和用户表放在不同库。分完后,还要考虑跨库查询的问题,这就复杂了,不能仅靠加个分片键解决。
数据库优化还有一个容易被忽略的点:连接池的大小。很多人觉得连接数越多越好,结果把连接池设成 200 个,数据库瞬间被压垮。因为数据库的并发连接数是有限的,每个连接都要占用内存和 CPU。正确的做法是根据数据库的处理能力算出一个合理的连接数。比如 MySQL 默认连接数是 151,但实际业务中,50 个连接就够用了。太多反而会引发连接竞争,导致响应变慢。连接超时也要适当设置,太短会频繁断开重连,太长则空闲连接占资源。一般设 30‑60 秒比较合理。
说个容易被忽视的优化点:定期清理和归档。很多数据库越跑越慢,不是因为业务增长,而是积累了大量历史数据。比如订单表,两年前的订单几乎没人查了,却仍然躺在表里,拖慢查询速度。正确的做法是定期把历史数据归档到另一个表或冷存储,例如每个月把超过一年的订单移到 表。这样主表的数据量就会变小,查询自然更快。归档后别忘了重建索引,因为索引也会因删除数据而碎片化。这些运维工作看似繁琐,但做好了,数据库轻松不少。优化数据库不是一蹴而就的事,需要慢慢调、慢慢试,找到最适合业务的方案。


