📖 模块简介
在支付系统中,常见的业务需求包括"超时未支付自动取消订单"等延迟任务。传统定时任务方案存在分布式一致性、性能瓶颈等挑战。Redis 凭借其高性能和丰富的数据结构,成为实现延迟任务队列(如订单超时自动取消)的主流技术选型。本文将系统介绍其原理、金融场景、Go 语言集成实现、常见问题及最佳实践。
🧠 基础原理
- 有序集合(Sorted Set)存储任务:延迟任务以订单号等唯一标识为 member,以到期时间戳为 score 存入 Redis ZSET。
- 任务投递:创建订单时,将任务写入 ZSET,score 设置为超时时间。
- 定时扫描与消费:后台定时任务(如每秒/分钟)用 ZRANGEBYSCORE 查询到期任务,依次处理(如取消订单),并从 ZSET 删除。
- 幂等与分布式锁:防止多实例重复处理同一任务,可结合 Redis 锁机制保障幂等。
💼 金融业务应用场景
- 超时未支付订单自动取消:下单后一定时间未支付,自动关闭订单、释放库存。
- 支付二维码过期失效:二维码生成后,超时未支付自动失效,防止重复使用。
- 优惠券/红包过期处理:到期未使用自动失效,避免资金风险。
- 分期还款提醒:还款日前定时推送提醒,提升用户体验。
💻 示例代码(Go + Redis)
1. 任务投递(订单创建时写入 ZSET)
go
// 创建延迟任务:订单号为 member,到期时间为 score
orderID := "order_123456"
timeout := time.Now().Add(30 * time.Minute).Unix() // 30分钟后超时
rdb.ZAdd(ctx, "order:timeout:zset", &redis.Z{Score: float64(timeout), Member: orderID})
2. 定时扫描与自动取消
go
// 每隔一段时间执行(如每分钟),扫描到期订单并取消
now := time.Now().Unix()
orders, _ := rdb.ZRangeByScore(ctx, "order:timeout:zset", &redis.ZRangeBy{
Min: "-inf",
Max: fmt.Sprintf("%d", now),
})
for _, orderID := range orders {
// 业务处理:取消订单、释放库存等
cancelOrder(orderID)
// 删除已处理任务
rdb.ZRem(ctx, "order:timeout:zset", orderID)
}
🚨 常见问题与注意事项
- 任务丢失/未及时消费:服务重启、异常可能导致部分任务未被及时消费,建议配合持久化和补偿机制。
- 大批量订单超时压力:高峰时刻批量超时需合理分批处理,避免单次扫描压力过大。
- 时钟漂移问题:多实例部署时需保证各节点时间同步,否则可能提前/延迟处理。
- 幂等性保障:取消订单等操作需保证幂等,防止重复处理。
- ZSET 无限增长:需及时删除已处理任务,防止内存膨胀。
✅ 最佳实践建议
- 合理设置扫描频率与批次大小,避免单次压力过大。
- 业务处理需保证幂等,防止重复取消等问题。
- 结合 Redis 分布式锁,避免多实例重复消费。
- 关键节点启用持久化(RDB/AOF),防止数据丢失。
- 定期监控与告警,及时发现异常任务堆积。
- 大批量任务可分片处理,提升系统可用性。