您好,欢迎访问数据库运维|优化|安装|迁移|服务官网!
13261661949
从秒级到毫秒级,一次数据库优化实战全记录-行业新闻-数据库运维|优化|安装|迁移|服务_uDBok.com

新闻动态

联系我们

从秒级到毫秒级,一次数据库优化实战全记录-行业新闻-数据库运维|优化|安装|迁移|服务_uDBok.com

地址:北京市昌平区高新经济开发区
手机:13261661949

咨询热线13261661949

从秒级到毫秒级,一次数据库优化实战全记录

发布时间:2026-08-14 22:06:00人气:1893

那天下班前,运维老张在群里甩了个截图,说某个核心接口的响应时间从平时的200毫秒飙升到了3秒多。我盯着屏幕看了三秒,心想坏了。这个接口是给前端首页用的,每次用户登录后加载的第一个数据就是它。如果它慢成三秒,用户体验基本就是灾难级别的。我赶紧拉上后端的小王和DBA的老刘,开了个紧急会议。老刘说数据库CPU跑到了90%,慢查询日志里全是同一个SQL。我说行,那就从这条SQL开始查。

从秒级到毫秒级,一次数据库优化实战全记录

老刘把那条SQL贴出来的时候,我差点没把咖啡喷到屏幕上。那条SQL足足有二十行,左联右联了四个表,最要命的是里面用了两个子查询,还嵌套了一层IN。小王说这是之前一个临时工写的,上线后一直没问题,数据量一上来就崩了。我当时就想,很多数据库性能问题不是突然出现的,而是量变到质变。1万条数据时跑50毫秒,10万条时跑500毫秒,100万条时直接奔着3秒去了。这种指数级的退化,核心原因往往就出在SQL写法上。

老刘开始分析执行计划。他说第一个子查询没有走索引,因为关联字段的字符集不一样。一个表是utf8mb4,另一个是utf8,MySQL在执行关联时会做隐式转换,索引就失效了。第二个子查询更离谱,里面有个NOT IN,而那个字段允许NULL值。NOT IN遇到NULL值,整个子查询的结果集就变成了UNKNOWN,优化器直接放弃了索引,选择了全表扫描。我当时就感慨,很多DBA其实知道这些坑,但真正写SQL的时候,没人会逐条检查字符集和NULL值。这不是技术问题,是工程习惯的问题。

我们决定先做第一轮优化。把两个子查询改成JOIN,把NOT IN改成NOT EXISTS,统一字符集,然后给关联字段加上复合索引。改完之后重新跑了一遍,响应时间降到了800毫秒。老刘说不算满意,因为业务要求是200毫秒以内。小王说要不试试缓存,把热点数据放Redis里。我说可以,但缓存解决不了根本问题,数据一旦更新,缓存就得失效,而且这个接口的数据变化频率很高,缓存命中率可能不高。我们得继续深挖。

老刘又翻了一遍执行计划,发现有个大表走了全表扫描,数据量是800万行。他说这个表是记录用户操作日志的,其实接口根本不需要这么全的数据,只需要最近7天的记录。但原始SQL里写的是查询全部数据,然后在应用层做过滤。我说这就是典型的“取全量、再过滤”的反模式。数据库最怕的就是这种写法,因为全表扫描意味着每次查询都要读800万行数据,即使最终只用到其中1万行,那799万行也白白读了一遍。我们改成只查最近7天的数据,加上时间索引,效果立竿见影。

改完之后,响应时间降到了350毫秒。离目标200毫秒还有差距,但已经接近了。这时候小王提了个建议,说能不能把一些计算逻辑从SQL里挪到应用层。比如接口里有个字段是“用户最近一次操作距今天数”,原来是用DATEDIFF函数算出来的。这个函数在每条数据上都要执行一次,而且没法用索引。我们改成在应用层用Java的LocalDate计算,只传一个日期字段给前端。这个改动看似不大,但减少了数据库的CPU消耗,响应时间又降了50毫秒,到了300毫秒。

300毫秒之后,再往下优化就难了。老刘说我们得看看索引本身是不是有问题。复合索引是(a, b, c)的顺序,但查询条件里经常只用到b和c,没有a。这就导致索引的最左前缀原则失效,索引虽然存在但用不上。我们重新分析了业务查询的分布,把索引顺序调整成(b, c, a),又把一些冗余的单列索引清理掉。优化完后,响应时间降到了180毫秒。终于达标了。

但老刘说别急着庆祝,他还要做一次压力测试。他写了个脚本,模拟100个并发请求连续打三分钟。结果跑了不到一分钟,数据库的CPU又飙到了80%。他说问题不在SQL本身了,而在连接池的配置上。原来的连接池最大连接数是50,但应用的线程池开了200个线程,每个线程都去抢连接,抢不到就排队,排队等久了就超时重试,重试又加重了数据库的负载。我们调整了连接池的最大连接数到100,同时把应用线程池降到150,减少了竞争。重新压测,CPU稳定在40%左右,平均响应时间150毫秒。

这次优化前后花了三天时间。从3秒到150毫秒,提升了20倍。回看整个过程,真正的问题不是某一个环节出了错,而是从SQL写法到索引设计、从字符集到连接池配置,一层一层累积下来的。很多公司做数据库优化,上来就问“要不要加索引”“要不要上分库分表”,但这些其实都是手段,不是目的。真正的优化思路应该是:先看SQL本身有没有写错,再看索引有没有用上,再看数据量能不能裁剪,最后才考虑架构层面的改造。顺序很重要,跳步往往会走弯路。

现在每次有新同学写SQL,我都会让他们先把执行计划跑一遍。执行计划不撒谎,它清清楚楚告诉你每一行数据是怎么被扫描的、用了哪个索引、扫描了多少行。你看懂了执行计划,就相当于拿到了数据库的体检报告。剩下的问题,无非是哪里疼医哪里。从秒级到毫秒级,听起来很神奇,其实一点都不玄乎。就是把每一个细节抠到极致,把每一行代码都当成性能隐患来对待。数据库优化的本质,是尊重数据的结构,敬畏每一行SQL的执行路径。你尊重它,它就给你毫秒级的响应。你糊弄它,它就给你三秒的等待。

推荐资讯

13261661949