Redis Lua脚本原子性与秒杀实现
Redis中的Lua脚本如何保证原子性?请用Lua脚本实现一个秒杀库存扣减的示例。Redis单线程模型对Lua脚本执行有什么限制?KEYS和ARGV的区别是什么?
回答
古法程序员
Lua脚本原子性:
- Redis使用同一个Lua解释器,脚本执行期间其他命令不会被插入
- 脚本执行过程是原子的,不会被其他客户端打断
- 脚本失败时所有写入操作自动回滚
秒杀库存扣减Lua脚本:
-- KEYS[1]: 库存key, ARGV[1]: 扣减数量, ARGV[2]: 用户ID
local stock = tonumber(redis.call('GET', KEYS[1]) or '0')
if stock <= 0 then
return -1 -- 库存不足
elseif stock < tonumber(ARGV[1]) then
return -2 -- 库存不够扣
end
-- 扣减库存
redis.call('DECRBY', KEYS[1], ARGV[1])
-- 记录秒杀成功的用户(去重)
redis.call('SADD', 'sec_kill_users:' .. KEYS[1], ARGV[2])
return 1 -- 成功
限制:
- 脚本中不能使用随机性命令(如RANDOMKEY),否则主从复制不一致
- 脚本执行时间不能过长(默认5秒超时,由lua-time-limit控制)
- 大循环脚本会阻塞整个Redis(单线程)
- 脚本应尽量短小精悍
KEYS vs ARGV:
- KEYS:传递key名称,会参与Redis Cluster的哈希槽计算(确保所有key在同一节点)
- ARGV:传递参数,不参与哈希槽计算
- Cluster模式下必须使用KEYS传key,否则可能出现跨节点错误
- 单机模式下两者无区别