面试题答案
一键面试实现思路
- 获取锁:使用Redis的SETNX命令获取一个锁,防止并发更新导致数据不一致。
- 检查商品价格:在获取锁后,从Redis中获取商品当前价格,判断价格是否真的发生变化,避免不必要的更新。
- 更新数据:如果价格变化,使用Lua脚本原子性地更新商品价格以及可能相关的缓存数据(如商品详情页缓存等)。
- 释放锁:无论更新是否成功,最后都要释放锁,保证其他操作可以继续进行。
关键代码片段
- 获取锁
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = "product_price_lock:{product_id}"
lock_value = "unique_value"
is_lock_acquired = r.set(lock_key.format(product_id=product_id), lock_value, nx=True, ex=10) # 设置10秒过期时间,防止死锁
- Lua脚本更新数据
-- 假设第一个key为商品价格key,第二个key为相关缓存key
-- 假设第一个参数为新价格
local new_price = ARGV[1]
local current_price = redis.call('GET', KEYS[1])
if current_price ~= new_price then
redis.call('SET', KEYS[1], new_price)
-- 更新相关缓存,如删除旧的商品详情页缓存,这里假设相关缓存key为KEYS[2]
redis.call('DEL', KEYS[2])
return 1
else
return 0
end
- 使用Python调用Lua脚本
if is_lock_acquired:
try:
lua_script = """
local new_price = ARGV[1]
local current_price = redis.call('GET', KEYS[1])
if current_price ~= new_price then
redis.call('SET', KEYS[1], new_price)
redis.call('DEL', KEYS[2])
return 1
else
return 0
end
"""
price_key = "product_price:{product_id}"
cache_key = "product_detail_cache:{product_id}"
result = r.eval(lua_script, 2, price_key.format(product_id=product_id), cache_key.format(product_id=product_id), new_price)
finally:
r.delete(lock_key.format(product_id=product_id)) # 释放锁
通过上述步骤和代码,能在一定程度上利用Redis Lua环境协作组件保证缓存数据的一致性更新,并避免缓存击穿等问题。