如何在Redis中用Lua脚本实现购物车原子批量操作?

陌枫君_4778

陌枫君_4778

2026-09-22

814人浏览

原创

redis单个命令(如hmset、hincrby)本身原子,但组合使用无法保证购物车批量操作原子性,因多命令间存在网络延迟与并发竞态;pipeline仅优化性能,不提供隔离;lua脚本在服务端一次性执行,天然原子,是唯一可靠方案。

如何在redis中用lua脚本实现购物车原子批量操作?

为什么直接用 HMSET + HINCRBY 无法保证购物车批量操作原子性?

因为 Redis 的单个命令是原子的,但多个命令组合(比如先查库存、再扣减、再更新购物车)在网络往返中必然存在竞态。哪怕用 PIPELINE 也只是减少延迟,不提供事务隔离——中间可能被其他客户端插入修改。Lua 脚本在服务端一次性执行,天然具备原子性,这才是真正可靠的方案。

常见错误现象:WATCH + MULTI 在高并发下频繁失败重试,吞吐骤降;或业务层自己加分布式锁,引入复杂性和延迟。

  • Redis 6.0+ 支持 EVALEVALSHA,脚本必须全部在服务端完成,不能调用外部 API 或 sleep
  • 脚本内不能使用 KEYSSCAN 等阻塞命令,否则影响整个实例
  • 所有键名必须通过 KEYS 数组传入,不能硬编码,否则 Redis Cluster 下会报 CROSSSLOT 错误

购物车批量添加/更新的 Lua 脚本怎么写才安全?

典型场景:用户一次性加入 3 个商品,每个带数量,需校验库存、累加数量、记录最后操作时间。脚本必须把「读-判-写」全包在内,且所有 key 都落在同一 slot(用 {cart:123} 这种哈希标签强制对齐)。

关键点:用 redis.call("HGET", KEYS[1], ARGV[i]) 读原值,用 tonumber() 转数字防 nil,用 math.min() 做库存截断,最后统一 HSET 写回。

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

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

下载
local cart_key = KEYS[1]
local stock_keys = KEYS[2] -- "stock:1001", "stock:1002", ...
local item_ids = ARGV[1]   -- "1001,1002,1003"
local quantities = ARGV[2] -- "2,1,5"
<p>local ids = redis.call("SMEMBERS", stock_keys) -- 实际应预存可用 sku 列表
local id_list = {}
for str in string.gmatch(item_ids, "[^,]+") do table.insert(id_list, str) end</p><p>local qty_list = {}
for str in string.gmatch(quantities, "[^,]+") do table.insert(qty_list, tonumber(str)) end</p><p>for i, id in ipairs(id_list) do
local stock_key = "stock:" .. id
local stock = tonumber(redis.call("GET", stock_key) or "0")
local cur_qty = tonumber(redis.call("HGET", cart_key, id) or "0")
local add_qty = qty_list[i] or 0
if cur_qty + add_qty > stock then
return {["error"] = "out_of_stock", ["sku"] = id}
end
end</p><p>-- 全部校验通过才写入
for i, id in ipairs(id_list) do
redis.call("HINCRBY", cart_key, id, qty_list[i])
end
redis.call("HSET", cart_key, "updated_at", tonumber(ARGV[3]))
return "ok"</p>

EVALSHA 缓存脚本后,如何避免 NOSCRIPT 报错?

脚本首次用 EVAL 加载后,Redis 会缓存其 SHA1 值;后续用 EVALSHA 调用更快。但若 Redis 重启或脚本被驱逐(maxmemory-policy 设为 allkeys-lru 时可能),EVALSHA 就会返回 NOSCRIPT 错误。

  • 客户端必须捕获 NOSCRIPT,自动 fallback 到 EVAL 重载脚本
  • 不要依赖 SCRIPT LOAD 提前加载——它不保证持久,且集群模式下需手动分发到各节点
  • 推荐做法:启动时用 EVAL 执行一次空操作(如 return 1),确保脚本已加载;或每次调用前先 SCRIPT EXISTS 检查

HASH 存购物车,为什么别用 EXPIRE 直接设过期?

因为 HSET 不会刷新整个 HASH 的 TTL,只更新字段值。如果用 EXPIRE cart:123 3600,之后每次 HINCRBY 都不会延长过期时间,一小时后整个购物车消失——用户刚加完商品就发现清空了。

  • 正确做法:每次写操作后显式调用 EXPIRE cart:123 3600,Lua 脚本里加一行 redis.call("EXPIRE", KEYS[1], 3600)
  • 更优解:用 RedisJSONStream 存购物车事件,TTL 控制更灵活,但需要 Redis 7.0+
  • 注意 EXPIRE 返回 0 表示 key 不存在,脚本里要检查,避免静默失败

最易忽略的是哈希标签和 key 分布——没加 {cart:123} 就直接用 cart:123,在集群环境下脚本会因跨 slot 被拒绝,错误信息是 CROSSSLOT Keys in request don't hash to the same slot

相关文章

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

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

下载

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

相关专题

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

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

2023.11.02

4009

19

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

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

2023.11.14

3515

11

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

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

2024.04.02

6212

6

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

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

2024.04.07

583

6

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

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

2024.04.07

696

6

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

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

2024.04.07

5622

6

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

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

2024.04.07

1120

6

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

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

2024.04.07

916

6

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

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

2024.04.07

5098

6

热门下载

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

精品课程

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

共0课时 | 0人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.5万人学习