做开发的这些年,我见过太多系统崩溃的惨案。去年双十一,某电商平台刚上线三小时,数据库直接挂了,后台日志里全是超时报警。运维兄弟凌晨两点爬起来,一边骂娘一边重启实例。结果发现,罪魁祸首不过是一条没加索引的 SQL,查了全表几千万条记录。这事让我想起刚入行时,老大说过一句话:数据库优化不是锦上添花,而是保命的手段。别等到用户骂娘、老板摔手机才想起去做优化。今天就跟大家聊聊,我这些年踩过的坑和总结的经验。

先说索引,这是最基础也最容易翻车的地方。很多人觉得加索引就是万能药,恨不得每列都建一个。结果查询时索引没走,反而让数据库维护索引的开销翻了好几倍。我见过一个项目,订单表上建了 12 个索引,每次写入都慢得像蜗牛爬。后来我查了慢查询日志,发现真正用到的只有三个,其他索引纯属摆设,占着硬盘空间不说,还拖慢更新速度。正确做法是先分析业务场景,找出高频查询条件,比如按用户 ID 查订单、按时间范围查记录,这些字段才值得建索引。联合索引的列顺序也很关键:最常过滤的字段放前面,例如查询订单时,用户 ID 比订单状态更常用,就把用户 ID 放在第一列。
再说 SQL 写法,这坑更多。我见过最离谱的,有人写了个子查询,里面套了三层,光跑一次就要 20 秒。后来我把子查询改成 JOIN,速度降到 0.3 秒。为什么?因为子查询很多时候会生成临时表,数据量大时内存直接爆掉。JOIN 是在原表上操作,只要索引匹配,效率天差地别。还有 SELECT * 这种写法,很多新手觉得省事,但查询出来的字段如果用不到,就白白浪费 IO 和网络带宽。应该只挑需要的列,例如只查用户名字和邮箱,就别把手机号、地址全拉出来。分页时,LIMIT 100,20 这种写法会让数据库先扫 10 万条再丢掉,等于白干。用上一页的最大 ID 做条件,例如 WHERE id > 100 LIMIT 20,效率能提升几十倍。
表结构设计这块,很多人没重视。我见过一个表,用户信息里居然存了 JSON 字符串,每次查询某个字段都得用 JSONEXTRACT,索引根本用不上。这种设计在数据量小的时候还能忍,上一百万条后查询直接卡死。正确的做法是把 JSON 拆成独立字段,或者用关联表。字段类型选择也要讲究,能用 INT 就别用 VARCHAR,能用 TINYINT 就别用 INT。比如性别字段,用 0、1 就行,没必要存“男”“女”。这样存储空间更小,查询时内存能装更多行,IO 压力也减轻。大字段尽量单独存到关联表里,例如用户头像 URL 放在 useravatar 表,主表只存基本信息,这样查询用户列表时就不会把大字段拖出来。
分库分表和读写分离,是数据量上亿后的必选项。我服务过一家金融公司,用户量冲到 5000 万后,单库单表扛不住,写入延迟飙到 10 秒。后来按用户 ID 哈希分到 64 个库,每个库再按时间分月表,写入延迟降到毫秒级。分库分表的关键是选好分片键,一般是用户 ID 或订单 ID,保证数据均匀分布。读写分离也要注意,主库负责写入和更新,从库只读。但别让业务代码硬编码从库地址,建议使用 MyCat、ShardingSphere 等中间件自动路由。还有一致性问题,主从同步有延迟,刚写入的数据可能读不到。关键操作可以强制读主库,例如支付后查询余额,其他场景允许短暂不一致。
缓存是数据库的救星,但用不好反而添乱。我见过有人给每个查询都加 Redis 缓存,结果缓存命中率不到 20%,大部分时间都在做无用功。正确做法是只缓存热点数据,例如用户登录状态、商品详情页。缓存失效策略也要慎重,别设固定的过期时间,否则缓存同时失效会引发雪崩,所有请求都砸到数据库上。可以加随机偏移量:基础时间 5 分钟,再加 0 到 60 秒的随机值。更新数据时,先删缓存再写数据库,或者采用双删策略:先删缓存,写完数据库后延时几百毫秒再删一次,能避免并发时读到旧数据。
监控和预警是一道防线。我认识一个运维,半夜三点被电话叫醒,说系统挂了。他查日志才发现,数据库连接数早已超限,但监控只报警了 CPU,没报警连接数。于是他加了一堆指标:慢查询数量、锁等待时长、临时表大小、磁盘 IOPS。每个指标设阈值,超过就自动发短信。例如慢查询超过 10 条/分钟,就通知开发排查。还有个坑,很多人只看平均延迟,但峰值更重要。平均延迟 100 毫秒,偶尔飙到 5 秒,这种尖刺最致命。应该监控 95 分位和 99 分位的延迟,这两个指标能更真实地反映用户体验。
说说心态。数据库优化不是一次性的事,它是个持续迭代的过程。业务在变,流量在变,数据量在涨,以前好的方案可能两个月后就不灵了。我见过一个项目,上线半年后,缓存命中率从 90% 跌到 30%,原因是用户访问模式变了,之前缓存的页面没人看了,新功能反而没人缓存。所以每隔一段时间都要重新分析慢查询、检查索引使用情况、评估分库分表策略。别指望一次优化管一辈子,那是做梦。优化这事,跟健身一样,得坚持。别等到系统崩了再后悔,那时候老板的怒火比数据库的延迟可怕多了。


