如何利用Redis Lua脚本提升高并发下单接口性能?

雨宇君_8366

雨宇君_8366

2026-07-24

310人浏览

原创

使用lua脚本将库存校验、一人一单、扣减、下单标记四步合并为一次原子操作,避免应用层分步调用导致的超卖问题;因redis单命令线程安全但多命令不保证原子性,“读-判-写”链路必须封装进lua,且get返回字符串需用tonumber()转数值。

如何利用redis lua脚本提升高并发下单接口性能?

直接用 redis.call() 把库存校验、一人一单、扣减、下单标记这四步打包进一个 Lua 脚本里执行,就能把原本需要 4 次网络往返 + 数据库事务的流程,压成 1 次原子操作,接口响应从几百毫秒降到几毫秒。

为什么不能在应用层分步调用 Redis

常见错误是先 GET 库存,再 INCRBY 扣减——中间存在时间窗口。两个并发请求同时读到库存为 1,都判断“够”,然后都执行扣减,结果库存变成 -1。这不是 Redis 不线程安全,而是应用层逻辑拆开了原子性边界。

使用场景:优惠券秒杀、限量商品抢购、防重复提交表单

  • Redis 单次命令天然线程安全,但多个命令之间不保证原子性
  • 只要涉及“读-判-写”链路(比如查库存 → 判是否 >0 → 扣减),就必须进 Lua
  • Spring Boot 中若用 StringRedisTemplate.opsForValue().get() + .decrement() 组合,等于主动放弃原子性

脚本里必须用 tonumber() 转换字符串值

Redis 所有 GET 返回的都是字符串,Lua 里直接拿它做数值比较会出错。比如 redis.call('get', 'seckill:stock:1001') 返回 "5",而 "5" 在 Lua 中是 <code>false(字符串比较),但 tonumber("5") 才是预期的 <code>false。

容易踩的坑:

Redis Skill - 高性能缓存管理
Redis Skill - 高性能缓存管理

Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。

下载
  • 忘记对 ARGV 参数调用 tonumber(),比如用户 ID 当作数字参与 sismember 没问题,但若后续要算差值就会失败
  • 对空值没做防御:tonumber(nil) 返回 nil,再参与运算会报错,应先用 if not stock then return -1 end
  • 哈希结构取值后,goodsInfo[1] 是字符串,必须 tonumber(goodsInfo[1]) 才能和数字比较

KEYS 和 ARGV 的传参方式决定脚本复用性

脚本里所有动态部分必须通过 KEYS 或 ARGV 注入,硬编码 key 名会让脚本无法复用。比如库存 key 是 seckill:stock:{voucherId},就得让调用方传 voucherId 进 ARGV[1],再在脚本里拼接:local stockKey = 'seckill:stock:' .. ARGV[1]。

参数差异:

  • KEYS 用于 Redis 键名,必须是非空字符串,且会被 Redis 集群路由机制校验(集群模式下所有 KEYS 必须落在同一 slot)
  • ARGV 用于任意值,包括数字、用户 ID、布尔标识,不参与路由,更灵活
  • Spring Boot 中调用时,redisTemplate.eval(script, keys, args...) 的 keys 是 List<string></string>,args 是可变参数列表,顺序必须和脚本里 KEYS[1]、ARGV[1] 严格对应

返回值设计要覆盖所有业务分支

不要只返回成功/失败布尔值。Lua 脚本的 return 值会原样透传给应用层,建议用整数编码状态,方便前端或网关快速分流:

  • return 0:抢购成功
  • return 1:库存不足(tonumber(redis.call('get', stockKey)) )
  • return 2:用户已下单(redis.call('sismember', orderKey, userId) == 1)
  • return -1:key 不存在或类型错误(比如期望是 string 却是 hash)

性能影响:返回简单整数比返回 JSON 字符串快一个数量级,且避免了序列化开销;兼容性上,所有 Redis 客户端都支持接收 Long 类型返回值。

复杂点在于,一旦脚本里出现未捕获的运行时错误(比如除零、nil 参与运算),Redis 会直接抛 LUA script failed 异常,而不是返回你定义的错误码。所以每个数值操作前都要加 if not xxx then return -1 end 防御。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

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

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2023.10.19

3128

3

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

2025.10.17

4769

12

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2025.12.29

3769

9

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

2026.01.19

446

15

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

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

2023.11.02

4449

19

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

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

2023.11.14

3975

11

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

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

2024.04.02

7312

6

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

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

2024.04.07

643

6

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

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

2024.04.07

756

6

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
phpEnv手册
phpEnv手册

共0课时 | 0人学习