支付系统里,DB 往往最早成为瓶颈。本篇聚焦读写优化和结构优化。
背景
在支付系统:MySQL 分片中讨论了分库分表。随着用户与业务增长,数据库压力主要来自:
- 读写 QPS 增长
- 单表数据量上涨
- 新功能带来的新查询
负载来源
主要由三部分构成:
- 读取
- 写入
- 数据量
读写通常更急迫,数据量更多靠归档处理。
如何定位瓶颈
常用手段:
- 慢查询日志
SHOW PROCESSLISTEXPLAIN
找到慢 SQL 后,优化索引与查询,再考虑 DBA 调参与硬件。
表结构优化
核心原则:让查询有合适索引,让行更小更稳定。
常见建议:
- 主键用
UNSIGNED INT+AUTO_INCREMENT - IP 使用整型存储
- 尽量固定长度字段
- 字段越小越好
- 尽量使用
NOT NULL - 大表做水平拆分
- 宽表做垂直拆分
- 低区分度字段不要建索引
- 高更新字段不适合建索引
- 避免
UUID作主键 - 不使用外键
- 少用
ENUM
读优化
关键策略:
- 针对
WHERE、GROUP BY、ORDER BY建索引 - 避免
SELECT * - 小结果集加
LIMIT 1 - 避免
%xxx前缀模糊匹配 - 避免表达式运算导致索引失效
- 分页用游标替代
OFFSET - 控制
IN数量 - 慎用
COUNT(*) - 尽量避免复杂
JOIN
大范围扫描
大扫描要拆分或分页,否则会拖垮库。
缓存缺失导致高 QPS
缓存 key 过细或过宽都会产生大量 DB 读。
典型例子:用户设置表稀疏,查询为空时要写入默认值到缓存,避免重复穿透。
写优化
CAS 更新
用版本号 CAS 提高并发写能力。
参考:支付系统: 余额更新
避免死锁
常见死锁原因:
- 不同事务锁顺序不一致
- 多索引导致锁顺序不同
- Gap Lock
对策:
- 保持锁顺序一致
- 缩小锁范围
- 降低隔离级别
小结
数据库优化的本质是:减少无效读写、让查询命中索引、降低锁竞争。优化到一定程度后,就必须通过分库分表继续扩展。