第 4 篇:性能治理不是只看 CPU
数据库一变慢,很多团队的第一反应是升配。CPU 高了,升。内存紧了,升。吞吐不稳,再升。这么做不是永远没用,但它经常把真正的问题盖住,而且特别烧钱。
云数据库里的性能问题,绝大多数都不是一句“机器不够大”能解释完的。更真实的情况往往是几种问题叠在一起:
- SQL 写得差
- 索引设计不对
- 连接池开得太大
- 热点键或热点分区集中打爆
- 缓存命中率下降
- 后台批处理和线上流量抢资源
- 只读副本复制延迟,导致读流量也开始抖
最后在监控上统一表现为:延迟变高、吞吐变差、错误率上升。
先分清楚是吞吐问题还是延迟问题
性能问题最怕一上来就优化。第一步应该是分类,不是调参。
你得先搞清楚到底是哪种慢:
- 是平均延迟上升,还是尾延迟恶化
- 是全局变慢,还是某几个接口特别慢
- 是读变慢,写变慢,还是连接建立变慢
- 是稳定高负载,还是周期性抖动
这一步很关键。因为如果你的问题是少量慢查询把连接和工作线程卡住,那你就算升配,爆炸机制也还在,只是把爆炸时间往后推了一点。
SQL 和索引仍然是第一现场
无论数据库怎么演进,大部分线上性能事故最后都能追到 SQL 和索引上。这件事十几年没变过。
常见问题非常朴素:
- 模糊查询和范围查询没有合适索引
- 复合索引顺序不匹配过滤条件
- 用函数处理索引列,导致索引失效
- 大分页使用
offset导致扫描成本飙升 - 一次查询返回过多列和过多行
再往深一点说,你还要关注这些细节:
- 查询条件的选择性够不够高
- 排序字段和过滤字段能不能共用索引
- 回表次数是不是过多
- 扫描行数和返回行数的比值是不是离谱
- 执行计划有没有随着数据量变化而漂移
这些问题在低流量时往往藏得很好,一到活动、批处理、月结或业务增长,就会被放大成事故。
连接数问题经常被低估
数据库不是 Web 服务,连接数不是越多越好。这个误区非常常见。
很多服务一部署就把连接池拉满,理由通常是“避免请求排队”。但数据库连接本身就是重资源,线程、内存、上下文切换都要成本。连接一多,数据库可能还没开始真正干活,就已经把资源浪费在连接管理上了。
云环境里这个问题更容易被放大。因为应用扩容通常很容易,一旦 HPA 或自动扩容触发,应用实例数翻倍,数据库看到的不是 QPS 先翻倍,而是连接数先翻倍。
所以你至少要建立下面几个基线:
- 单实例允许的合理连接范围
- 每个服务实例的连接池上限
- 连接泄漏和空闲连接积压的报警阈值
热点比总量更危险
很多数据库整体 QPS 不高,平均资源也不高,但就是会抖。这种情况十有八九不是总量问题,而是热点问题。
热点之所以难搞,是因为总体资源看起来很健康,但局部竞争已经非常严重。常见场景包括:
- 秒杀库存集中写某几行
- 用户详情集中读某几个明星账号
- 时间分区设计不合理,所有写入压在最新分区
- 自增主键或单调递增索引导致写入页争用
热点问题通常不会靠数据库单边解决,往往要应用侧配合。比如:
- 打散键设计,避免单一分区过热
- 引入缓存和本地缓存,分散读压力
- 用消息队列削峰,把同步写变成异步写
- 对统计场景做预聚合,减少重复扫表
- 对库存、配额这种高竞争数据引入更明确的并发控制策略
监控不要只盯 CPU
数据库监控如果只有 CPU、内存和磁盘占用,基本不够做性能治理。你真正应该长期盯的是这些指标:
- 慢查询数量和分位延迟
- 活跃连接数和等待事件
- 锁等待和死锁趋势
- 磁盘吞吐与 IOPS 峰值
- Buffer 命中率和缓存抖动
- 复制延迟和只读节点滞后
而且只看指标本身还不够,你得让指标和请求、SQL、应用版本、配置变更、活动时间点关联起来。只有这样,性能问题才能形成完整闭环。
先治理,再扩容
扩容不是错,但顺序不能错。更稳妥的做法通常是:
- 找出最差的那 1% SQL。
- 修正最明显的索引和查询路径问题。
- 控制连接池和批处理并发。
- 识别热点并做业务层分流。
- 最后再判断是否需要升级规格或增加只读节点。
这个顺序的价值在于,你拿到的不只是短期性能改善,还会得到更清楚的系统边界和更可预测的成本曲线。
小结
云数据库性能治理的关键,不是一直把机器买大,而是把访问路径管顺、把连接池管住、把热点识别出来、把坏 SQL 揪出来。
只看 CPU,最后很容易把结构性问题误诊成资源问题。真正成熟的性能治理,应该能回答这几个问题:慢在哪里,为什么慢,下一次怎么提前发现,是否真的需要花钱扩容。
下一篇我们就接着聊和性能高度相关的另一个问题:成本。