🚀 扩展至 93K RPM:将配额管理从 SQL 移至 Redis
发布: (2026年1月15日 GMT+8 12:41)
3 min read
原文: Dev.to
Source: Dev.to
发生了什么?
你最喜欢的艺术家宣布即将举办演唱会,顾客们抢购门票。电子票务平台开始返回错误并变得异常缓慢。
根本原因是数据库 CPU 使用率飙升至 100 %,因为用于统计活动配额的单个“热点”行被持续查询和更新。表结构如下:
| eventId | showtimeId | quota | reserved |
|---|---|---|---|
| 1 | 1 | 15,000 | 10,000 |
每个请求都会读取并写入这行数据,导致锁争用并耗尽 CPU 资源。
解决方案
将配额计数器迁移到 Redis
Redis 将数据存储在内存中,延迟远低于基于磁盘的数据库。一个朴素的实现可能如下所示:
function reserveTicket(showtimeId, amount) {
const reserved = parseInt(redis.get('{key}'));
if (reserved {
// …order creation logic…
const result = await redis.eval(reserveLuaScript, {
keys: ['{key}'],
arguments: [String(amount), String(quota)],
});
if (result !== 1) {
throw error('reservation failed');
}
});
// …other logic…
}
将计数持久化回数据库
因为 Redis 是缓存,权威的真相来源仍然是关系型数据库。后台工作线程每 5 秒轮询一次 Redis,并将当前计数写回数据库,从而在不牺牲性能的前提下实现最终一致性。
结果
- 每分钟订单量从 5,000 提升至 93,240。
- 数据库负载显著下降,CPU 瓶颈被消除。
- 系统现在能够可靠地处理高峰售票流量。
关键要点
- 诊断性能问题的真实原因;本例中是热点行导致的 DB 锁争用。
- 将系统设计与实际使用模式对齐——在高吞吐场景下,热点行需要特殊处理。
- 使用 Redis(或其他内存存储)配合原子 Lua 脚本安全地管理计数器。
- 保持关系型数据库作为真相来源,并通过异步同步来保证持久性。