前两天和一个做后端的朋友吃饭,他刚接手一个老项目,数据库慢得像蜗牛爬,用户点个查询按钮,得等出一杯咖啡的时间。他愁得直挠头,问我数据库优化到底该怎么搞。其实这事儿,说复杂也复杂,说简单也简单,核心就一句话:别让数据库做它不擅长的事。数据库最擅长的是存数据和查数据,但如果让它去算复杂逻辑、处理海量无索引的扫描,那它肯定会罢工。

咱们先聊聊最基础也最容易被忽略的一步:索引。很多人一上来就建索引,建完发现查询快了,但写入慢了,更新也卡了。这是因为索引像书的目录,查得快,但每加一页新内容,目录就得跟着改。所以索引不是越多越好,必须精准。比如业务里经常按用户ID查订单,那就在 上建普通索引;要是经常按时间范围查,那就建组合索引,把时间字段放在第一位。索引还有个坑:最左前缀原则。你建了 的组合索引,查询 或 都能用上,但查询 或 就用不上。很多人觉得索引没用,实际上是因为列的顺序写反了。
再说说 SQL 本身。很多时候,慢不是因为数据库不行,而是 SQL 写得太“野”。比如 这种写法,看起来省事,但数据库得把所有字段都捞出来,哪怕你只需要两个字段。更糟的是,如果使用 ,索引基本就失效,因为左通配符会让索引失效。还有,别在 条件里对字段做函数运算,例如 ,改成 ,索引就能用了。这些细节,改一行代码,性能可能翻几倍。
有些场景,光靠 SQL 优化救不了,得动表结构。比如一张表已经有千万级数据,查询仍然全表扫描,那就得考虑分库分表。分库分表听起来高大上,实际上逻辑很简单:把一张大表拆成多张小表,按用户 ID 哈希、按时间分区、或按业务模块拆分。但分表有代价——跨表查询会很痛苦,例如统计所有用户的总订单数,以前一条 SQL 能搞定,现在得遍历所有分表再汇总。所以分表前要想清楚,业务里哪些是高频查询,哪些是低频统计。低频的,宁可写个定时任务慢慢跑,也别把系统搞得太复杂。
缓存是另一招大招。很多慢是因为同样的数据被反复查询。比如首页的热门文章列表,用户点一次查一次数据库,高峰期能查几千次。这时在数据库前加一层缓存,如 Redis 或 Memcached,把热点数据存进去,查询直接走内存,毫秒级返回。缓存还有经典的旁路缓存策略:查询时先查缓存,有就直接返回,没有再查数据库,然后写回缓存。更新时先更新数据库,再删除缓存,避免数据不一致。但缓存不是万能药,别把所有数据都塞进去,只缓存那些变化少、访问频率高的数据。
硬件和配置层面常被新手忽略。数据库跑慢,有时不是代码问题,而是机器扛不住。比如磁盘是机械硬盘,随机读写慢得要命,换成 SSD,性能立刻提升一个档次。内存也一样,数据库的缓存池越大,磁盘 I/O 越少。MySQL 的 InnoDB 引擎, 建议设为物理内存的 70% 左右。还有,连接数别开太大,默认 100,很多人直接改成 1000,结果数据库被并发连接撑爆。连接数要根据业务峰值和服务器配置来算,不是越大越好。
还有一类优化属于“治本”思路:重新设计业务逻辑。有时候慢是因为业务本身就有问题。比如一个用户详情页,要查询用户信息、订单列表、收藏列表、评论列表,你写四个 SQL 挨个查,每个都要一次网络往返。这种情况,不如一次查询把数据全部拿回来,或在应用层做批量查询。再比如报表查询要跑几分钟,业务方要求实时出结果,那就得思考:实时真的必要吗?能否改成离线计算,凌晨跑一次,把结果存到一张结果表里?很多时候,优化不是技术问题,而是需求问题。
说一个容易被骂但很管用的方法:删数据。很多数据库越跑越慢,是因为里面塞了太多无用的数据。比如日志表,三年前的数据谁还看?但每次查询都要扫一遍。该删就删,该归档就归档。可以搞一个定时任务,每天凌晨把三个月前的数据转移到历史表或冷存储,主表只保留热数据。表小了,索引也小了,查询自然快。别舍不得删,数据不是文物,放着只会拖垮性能。
所以你看,数据库优化没什么玄学,就是把那些“明明可以不做”的事一件件砍掉。别让数据库干它不擅长的活,别写 SQL 时偷懒,别舍不得删数据,别一上来就搞分库分表。从最便宜的索引和 SQL 改起,再到缓存、表结构,最后才是硬件和业务重构。每一步都能看到实实在的效果。吃完饭,我那朋友说回去先查慢查询日志,看看哪些 SQL 在拖后腿。我说这就对了,优化数据库,第一件事永远是找到最慢的那条 SQL,而不是瞎猜。


