面试题答案
一键面试Lua脚本逻辑
- 获取库存:使用
redis.call('GET', key)
获取当前商品库存的键值。这里key
是代表商品库存的Redis键。 - 检查库存:判断获取到的库存值是否大于0。如果库存小于等于0,直接返回0(表示扣减失败)。
- 扣减库存:如果库存大于0,使用
redis.call('DECR', key)
对库存进行扣减操作。 - 返回结果:扣减成功返回1,扣减失败返回0。
示例Lua脚本如下:
local stock = redis.call('GET', KEYS[1])
if stock == false or tonumber(stock) <= 0 then
return 0
else
redis.call('DECR', KEYS[1])
return 1
end
避免超卖问题
- 原子性操作:通过Lua脚本在Redis中执行原子操作。由于Redis执行Lua脚本是原子性的,在脚本执行期间不会被其他命令打断,这就保证了库存检查和扣减操作不会出现并发问题,从而避免超卖。
- 预扣减库存:在抢购活动开始前,可以提前将一部分库存预扣减,以应对高并发瞬间可能出现的超卖。比如活动准备1000个库存,可以先预扣减100个,实际对外显示900个库存。当实际库存扣减到0后,再逐步释放预扣减的库存。
高并发性能优化及减少锁争用
- 分布式锁优化:
- 分段锁:如果是多个商品的抢购,可以将商品进行分段,每个分段使用不同的锁。例如,1 - 100号商品使用一个锁,101 - 200号商品使用另一个锁等。这样可以降低锁的粒度,减少锁争用。
- 锁的过期时间:合理设置分布式锁的过期时间,避免因为锁长时间不释放导致其他请求长时间等待。同时,在获取锁和释放锁的逻辑中要注意异常处理,确保锁能正常释放。
- Redis集群优化:
- 读写分离:对于抢购系统,可以将读操作(如查询库存)分配到从节点,写操作(如扣减库存)分配到主节点。这样可以减轻主节点的压力,提高系统整体性能。
- 使用管道:在发送多个Redis命令时,使用管道技术将多个命令打包一次性发送到Redis服务器,减少网络开销。
- 缓存预热:在抢购活动开始前,将商品信息和库存等数据提前加载到Redis缓存中,避免活动开始时大量缓存穿透导致数据库压力过大。同时,可以对缓存数据设置合理的过期时间,保证数据的一致性。