面试题答案
一键面试实现步骤
- 获取锁:
- 使用
SETNX
(SET if Not eXists)命令尝试设置一个特定的键值对来表示锁。例如,以库存操作的唯一标识(如商品ID)作为键,一个随机生成的字符串作为值。如果设置成功(返回1),表示获取到锁;如果设置失败(返回0),表示锁已被其他进程持有。
- 使用
- 执行库存扣减操作:
- 在获取到锁之后,从Redis中获取当前库存值,然后进行扣减操作,再将扣减后的库存值写回Redis。这一系列操作需要保证原子性,可以使用Lua脚本来实现。
- 释放锁:
- 在完成库存扣减操作后,使用
DEL
命令删除表示锁的键,释放锁资源。为了避免误删其他进程持有的锁,在删除之前需要验证锁的值是否与获取锁时设置的值一致,同样可以通过Lua脚本实现。
- 在完成库存扣减操作后,使用
涉及的Redis命令
- SETNX:
- 语法:
SETNX key value
- 作用:只有在键
key
不存在时,才设置键的值为value
。如果设置成功返回1,否则返回0。用于尝试获取分布式锁。
- 语法:
- GET:
- 语法:
GET key
- 作用:获取指定键
key
的值。用于获取当前库存值。
- 语法:
- SET:
- 语法:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
- 作用:设置键
key
的值为value
,可以设置过期时间(EX
为秒,PX
为毫秒),NX
表示只有键不存在时才设置,XX
表示只有键存在时才设置。用于更新库存值。
- 语法:
- DEL:
- 语法:
DEL key [key ...]
- 作用:删除指定的键及其值。用于释放分布式锁。
- 语法:
- EVAL:
- 语法:
EVAL script numkeys key [key ...] arg [arg ...]
- 作用:在Redis服务器端执行Lua脚本
script
。numkeys
表示键参数的个数,后面跟着键参数和其他参数。用于实现库存扣减和锁释放的原子性操作。
- 语法:
示例Lua脚本(以库存扣减和锁释放为例):
-- 获取锁的值
local lockValue = redis.call('GET', KEYS[1])
-- 判断锁的值是否与预期一致
if lockValue == ARGV[1] then
-- 获取当前库存
local stock = redis.call('GET', KEYS[2])
-- 扣减库存
stock = stock - 1
-- 更新库存
redis.call('SET', KEYS[2], stock)
-- 释放锁
redis.call('DEL', KEYS[1])
return stock
else
return -1 -- 表示未获取到锁或锁已过期
end
在实际应用中,使用 EVAL
命令执行上述Lua脚本,将锁的键和库存的键作为 KEYS
参数,获取锁时设置的值作为 ARGV
参数传入。