
在金融支付系统中,通过缓存预热、分布式锁优化及数据库读写分离,将系统QPS从8万提升至12万,响应延迟从800ms降至150ms,有效解决了高并发下的性能瓶颈。
高并发场景下,系统性能瓶颈常源于数据库资源竞争(如锁竞争、网络I/O)。当大量请求同时访问数据库时,锁等待队列会急剧增长,导致请求积压,形成“性能瀑布”。类比:城市主干道(数据库)同时被大量车辆(请求)占用,交通拥堵(延迟),需要通过分流(缓存、异步处理)缓解压力。缓存的作用是减少数据库压力,分布式锁保证业务原子性,读写分离提升数据库吞吐量。
对比缓存预热与直接访问数据库,以及分布式锁的选择:
| 方案 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 缓存预热 | 系统启动或低峰期预加载热点数据到缓存 | 提前填充缓存,避免高并发时直接访问数据库 | 电商秒杀、金融支付等热点数据场景 | 需平衡预热时间与数据实时性 |
| Redis分布式锁 | 多节点间共享的互斥锁 | 基于SETNX实现,支持超时自动释放 | 库存扣减、订单生成等需要原子性操作 | 锁过期时间需大于业务执行时间,避免死锁 |
| 本地缓存 | 单机部署的缓存 | 读取速度快,但高并发下数据不一致 | 单机环境、低并发场景 | 不适用于分布式系统 |
| 数据库读写分离 | 主从架构的数据库部署 | 主库处理写,从库处理读 | 高并发读多写少场景 | 需保证从库数据一致性(如同步延迟) |
缓存预热(系统启动时执行):
func preheatCache() {
hotItems := []string{"item1", "item2", "item3"} // 热门商品ID
for _, item := range hotItems {
stock, _ := db.QueryRow("SELECT stock FROM goods WHERE id = ?", item).Scan()
redis.Set(ctx, item, stock, 60*10) // 预热60秒,缓存有效期10分钟
}
}
支付请求处理(优化后):
func processPayment(ctx context.Context, userId, itemId string) error {
// 1. 检查缓存预热数据
stock, err := redis.Get(ctx, itemId).Int()
if err != nil {
return err
}
if stock <= 0 {
return errors.New("stock out")
}
// 2. 加锁前检查库存(减少锁竞争)
lockKey := fmt.Sprintf("payment:lock:%s:%s", userId, itemId)
locked, err := redis.SetNX(ctx, lockKey, 1, 4*time.Second).Result()
if !locked {
return errors.New("concurrent payment")
}
// 3. 事务扣减库存
tx := db.Begin()
tx.Exec("UPDATE goods SET stock = stock - 1 WHERE id = ? AND stock > 0", itemId)
if tx.RowsAffected() == 0 {
tx.Rollback()
return errors.New("stock not enough")
}
tx.Commit()
// 4. 释放锁
redis.Del(ctx, lockKey)
// 5. 返回成功
return nil
}
我之前参与过一个金融支付系统,负责高并发交易处理。项目在支付高峰时,QPS达到8万,响应延迟超过800ms,用户超时率很高。我主要做了三方面优化:一是系统启动时预加载热门商品库存到Redis(缓存预热),避免高并发时直接访问数据库,将读取延迟从200ms降到10ms;二是优化分布式锁,加锁前先检查库存是否足够,减少锁等待时间,锁过期时间设置为业务执行时间(约2秒)加上网络延迟(0.5秒)和安全余量(1秒),共4秒,避免死锁;三是数据库读写分离,主库处理写操作,从库处理读操作,提升数据库吞吐量。优化后,系统QPS提升至12万,响应延迟降至150ms,支付成功率从70%提升至95%。