如何在Redis中用Lua实现高并发优惠券防超领逻辑?

小丽小哥_4164

小丽小哥_4164

2026-11-13

525人浏览

原创

直接用DECR不够用,因其仅保证原子递减但无法在库存为0时拒绝请求并跳过后续逻辑;若先GET再DECR则存在竞态导致超卖;必须用Lua脚本将“判断库存>0→扣减→记录用户”三步封装为单线程原子操作,才能真正防超领。

如何在redis中用lua实现高并发优惠券防超领逻辑?

为什么直接用 DECR 不够用?

优惠券库存扣减看似只是个计数器递减,但实际要同时满足:原子性、超领拦截、领取成功后写用户记录。单纯靠 DECR 只能保原子性,无法在库存为 0 时拒绝请求并跳过后续逻辑;更麻烦的是,如果先查 GET 再 DECR,必然存在竞态——两个请求几乎同时读到 “1”,都执行 DECR,结果变成 -1,超发。

必须把「判断 + 扣减 + 记录」三步收进一个 Lua 脚本里,由 Redis 单线程串行执行,才能真正防超领。

EVAL 脚本里怎么安全判断并扣减?

核心是用 redis.call("GET", KEYS[1]) 拿当前库存,再用 tonumber() 转数字(避免 nil 或字符串比较出错),然后显式判断是否 > 0。注意:不能用 redis.call("DECR", KEYS[1]) 后再判断返回值,因为 DECR 在库存为 0 时会返回 -1,已经扣过了。

  • 正确顺序:先 GET → 判断是否 > 0 → 是则 DECR → 再 SADD 用户集合或 HSET 领取记录
  • 返回值建议统一:成功返回 1,失败返回 0,方便业务层直接判断
  • KEYS[1] 存库存(如 "coupon:1001:stock"),ARGV[1] 存用户 ID(如 "u_8892"),避免硬编码
if tonumber(redis.call("GET", KEYS[1])) > 0 then
  redis.call("DECR", KEYS[1])
  redis.call("SADD", KEYS[2], ARGV[1])
  return 1
else
  return 0
end

为什么推荐用 SADD 而不是 HSET 记用户?

记录谁领了券,本质是「去重写入」,SADD 天然幂等且 O(1);而 HSET 虽能存更多字段(如时间),但单次调用不保证原子写入多个字段,且集合操作比哈希更轻量。

Sentinal Redis
Sentinal Redis

监控Redis服务器健康状况、内存、性能及BullMQ队列。检查队列深度、失败任务,分析慢查询并诊断问题。

下载
  • 用 SADD coupon:1001:users u_8892,重复执行不会报错,也不会多写
  • 如果真要记时间,可在 Lua 里用 redis.call("TIME") 拿秒级时间戳,再拼成字符串写入 set(如 "u_8892|1717023456"),但得自己解析
  • 别用 LPUSH:列表无去重,高并发下可能重复插入同一用户

本地测试时容易忽略的兼容性坑

Redis 6.0+ 默认开启 ACL,脚本里调用的命令必须在用户权限范围内;另外 Lua 的 math.random 在 Redis 中被禁用,别试图加随机延迟来“缓解”竞争——它根本跑不起来,会报 Lua script attempted to access a non-existent global variable。

  • 确保运行脚本的 Redis 用户有 GET、DECR、SADD 权限(ACL 中对应 read 和 write 类命令)
  • 脚本里不要用 os.time() 或 math.random(),可用 redis.call("TIME") 替代
  • 本地用 redis-cli --eval 测试时,注意参数顺序:redis-cli --eval coupon.lua coupon:1001:stock coupon:1001:users , u_8892,逗号前后空格不能少

真正上线前,务必用 redis-benchmark -n 10000 -c 100 --eval 压测脚本响应和成功率——很多问题只在并发量上来时才暴露。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

redis

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4949

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

4515

11

mongodb和redis哪个读取速度快
mongodb和redis哪个读取速度快

redis 的读取速度比 mongodb 更快。原因包括:1. redis 使用简单的键值存储,而 mongodb 存储 json 格式的数据,需要解析和反序列化。2. redis 使用哈希表快速查找数据,而 mongodb 使用 b-tree 索引。因此,redis 在需要高性能读取操作的应用程序中是一个更好的选择。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.02

8772

6

redis怎么做缓存服务器
redis怎么做缓存服务器

redis 作为缓存服务器的答案:redis 是一款开源、高性能、分布式的键值存储,可作为缓存服务器使用。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

683

6

redis怎么解决数据一致性
redis怎么解决数据一致性

redis 提供了两种一致性模型,以维护副本数据一致性:强一致性 (sync) 确保写操作仅在复制到所有从节点后才完成;最终一致性 (async) 则在主节点上写操作后认为已完成,牺牲一致性换取性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

776

6

mysql和redis怎么保证双写一致性
mysql和redis怎么保证双写一致性

确保 mysql 和 redis 双写一致性的技术包括:1、事务性更新:同时更新 mysql 和 redis,保证一致性;2、主从复制:mysql 主服务器更改同步到 redis 从服务器;3、基于事件的更新:mysql 记录更改并发送到 redis等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

9122

6

redis缓存一般存些什么数据
redis缓存一般存些什么数据

redis缓存中存储的数据类型包括:字符串、哈希、列表、集合、有序集合、位图、地理空间数据和hyperloglog。这些数据类型适用于存储各种数据,从简单信息到复杂对象和地理位置。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

1200

6

redis的8种数据类型有哪些
redis的8种数据类型有哪些

redis 提供 8 种数据类型:字符串(文本、数字、二进制)、哈希(键值对)、列表(有序集合)、集合(无序唯一元素)、有序集合(按分数排序)、地理空间(地理位置)、hyperloglog(估计大数据基数)和位图(位序列存储)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

1456

6

redis主要作用有哪些
redis主要作用有哪些

redis 的主要作用包括:1. 缓存数据,提高访问速度;2. 充当消息队列,实现消息传递;3. 存储各种数据类型,如字符串、散列和集合;4. 管理会话信息,确保可靠性和可用性;5. 限制请求速率,防止服务器超载等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

7778

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go-Zero官方文档
Go-Zero官方文档

共0课时 | 0人学习