我上周帮朋友看了一个业务系统,慢得像蜗牛爬。点个查询,能等上十几秒,前台的小姑娘急得直跺脚。我扫了一眼 SQL,发现有个关联查询,三个表 JOIN 在一起,其中两个表连索引都没有。这就是典型的“数据库优化没做好,业务跑起来受罪”的案例。其实,查优化这事儿,说难不难,说简单也不简单。核心就一句话:别让数据库做它不擅长的事。数据库擅长利用索引快速定位数据,擅长按主键顺序扫描,擅长在内存里处理结果。但如果让它全表扫描、海量排序、频繁回表,它立马变成一台笨重的老机器,吭哧吭哧半天也吐不出东西。所以,第一个要搞明白的,是你的查询到底在干什么。

很多人写 SQL 的时候,脑子里想的是“我要这个结果”,却没想过“数据库怎么拿到这个结果”。比如,你写 ,如果 字段没有索引,数据库只能从头到尾把整张表翻一遍,找出所有 pending 的记录。这张表有一百万行,它就要扫描一百万行。哪怕只找到十条,也白干了九十九万九千九百九十次。这就是典型的“扫描浪费”。优化方向很简单:给 加个索引。但这里有个坑。如果这张表里大部分记录都是 pending,索引其实帮不上忙,因为数据库会发现回表成本太高,还不如直接全表扫。这时,你需要考虑分区表、物化视图,或者改变业务逻辑,比如把 pending 数据单独存一张小表。索引不是万能的,必须理解数据分布和查询模式。
再说一个常见的坑:模糊查询。很多人喜欢写 ,觉得这样灵活,用户输入什么都能匹配。但数据库看到这种写法,索引就废了,只能全表扫描。想象一下,用户搜“张三”,数据库得把整个 字段逐行匹配,性能自然不佳。解决方案是:如果业务允许,改成 ,索引就能用。或者使用全文索引,像 Elasticsearch 那样的搜索引擎,专门处理模糊匹配。实在不行,还可以考虑分词后建倒排索引。别把数据库当搜索引擎,它并不擅长这活儿。
还有一种情况是 SQL 写得过于“聪明”。比如用了大量子查询、嵌套临时表、复杂的关联条件。看起来一行代码搞定,实际上让数据库做了好几轮中间计算。你写 ,数据库可能先做 JOIN 生成一个巨大的临时结果集,再从中筛选。如果 JOIN 本身没有索引,临时结果集可能上百万行,内存装不下,还得写磁盘。这时,你可以拆开写:先筛选 的数据,再做 JOIN。或者用 代替 ,用 JOIN 代替子查询,让优化器更好地利用索引。SQL 是声明式语言,你告诉它“要什么”,它决定“怎么拿”。但你得帮它一把,别让它走弯路。
除了 SQL 本身,硬件和配置也很关键。我见过一个团队,天天调索引、改 SQL,结果发现服务器内存只有 4 GB,数据库的 buffer pool 设成 512 MB。你再怎么优化,也跑不过硬件瓶颈。查询优化本质上是 I/O 与 CPU 的平衡。如果内存足够大,热点数据都能缓存在内存里,查询自然快;如果磁盘是 SSD,随机 I/O 性能好,全表扫描也不会太慢。所以,优化之前,先看看硬件是否拖后腿。还有数据库的参数,比如 MySQL 的 ,默认值往往偏小,需要根据内存大小和业务数据量调大。基础工作没做好,后面的 SQL 折腾都是白费力气。
还有一个容易被忽略的点:业务逻辑本身。有时候查询慢不是数据库的问题,而是需求设计不合理。比如,你非要在大表上做实时统计,每次查询都 加 ,这本身就是高成本操作。业务方说“我要看实时数据”,但真的需要精确到秒吗?如果可以接受几秒的延迟,可以考虑用缓存(如 Redis)存放定时刷新的统计值,或者用物化视图提前计算好结果,查询直接读视图。很多优化问题,归根到底是“用技术手段解决业务问题”还是“用业务手段解决技术问题”。能少查就少查,能预计算就别实时算,能缓存就别读库。这不是偷懒,而是聪明。
说说监控。没有监控,优化就是盲人摸象。你得知道哪些查询慢,慢在哪里——是扫描行数多、排序消耗大,还是锁等待时间长。MySQL 有慢查询日志,PostgreSQL 有 ,这些工具能帮助定位问题。但很多人装了日志,却从不分析,或者看到慢查询直接加索引了事,不去深究原因。真正有效的做法是:先分析执行计划,看看数据库是怎么执行这条 SQL 的,哪一步消耗最大。然后针对性地优化,改 SQL、加索引、调参数,一步步验证效果。优化完后还要持续监控,留意是否有新的慢查询出现。数据库优化不是一次性工程,而是持续迭代的过程。
说到底,数据库查询优化就是一场“让数据少跑路、让计算少干活”的游戏。你了解数据、了解数据库、了解业务,就能找到最优解。别指望用一个技巧解决所有问题,也别迷信所谓的“银弹”。多花时间理解系统,比在网上搜十条优化口诀管用得多。下次遇到慢查询,别急着拍脑袋,先问自己:数据库到底在忙什么?它为什么这么忙?然后,你自然会知道该怎么帮它。


