支付系统里,DB 往往最早成为瓶颈。本篇聚焦读写优化和结构优化。

背景

支付系统:MySQL 分片中讨论了分库分表。随着用户与业务增长,数据库压力主要来自:

  • 读写 QPS 增长
  • 单表数据量上涨
  • 新功能带来的新查询

负载来源

主要由三部分构成:

  • 读取
  • 写入
  • 数据量

读写通常更急迫,数据量更多靠归档处理。

如何定位瓶颈

常用手段:

  • 慢查询日志
  • SHOW PROCESSLIST
  • EXPLAIN

找到慢 SQL 后,优化索引与查询,再考虑 DBA 调参与硬件。

表结构优化

核心原则:让查询有合适索引,让行更小更稳定。

常见建议:

  • 主键用 UNSIGNED INT + AUTO_INCREMENT
  • IP 使用整型存储
  • 尽量固定长度字段
  • 字段越小越好
  • 尽量使用 NOT NULL
  • 大表做水平拆分
  • 宽表做垂直拆分
  • 低区分度字段不要建索引
  • 高更新字段不适合建索引
  • 避免 UUID 作主键
  • 不使用外键
  • 少用 ENUM

读优化

关键策略:

  • 针对 WHEREGROUP BYORDER BY 建索引
  • 避免 SELECT *
  • 小结果集加 LIMIT 1
  • 避免 %xxx 前缀模糊匹配
  • 避免表达式运算导致索引失效
  • 分页用游标替代 OFFSET
  • 控制 IN 数量
  • 慎用 COUNT(*)
  • 尽量避免复杂 JOIN

大范围扫描

大扫描要拆分或分页,否则会拖垮库。

缓存缺失导致高 QPS

缓存 key 过细或过宽都会产生大量 DB 读。

典型例子:用户设置表稀疏,查询为空时要写入默认值到缓存,避免重复穿透。

写优化

CAS 更新

用版本号 CAS 提高并发写能力。

参考:支付系统: 余额更新

避免死锁

常见死锁原因:

  • 不同事务锁顺序不一致
  • 多索引导致锁顺序不同
  • Gap Lock

对策:

  • 保持锁顺序一致
  • 缩小锁范围
  • 降低隔离级别

小结

数据库优化的本质是:减少无效读写、让查询命中索引、降低锁竞争。优化到一定程度后,就必须通过分库分表继续扩展。