减少交互
原生接口支持丰富的数据结构,但对有些操作支持的不够好。
比如在对key进行设置时,可以同时写入超时时间。这样在一条指令中就可以完成。
如果是incr操作,原生没有提供超时时间参数,需要调用两次接口实现。
可能产生的问题是,一是如果第一条操作后程序出现问题,导致超时时间没有设置,则会造成redis内存无法释放。二是多了一次网络交互,会增加业务的响应延迟。
因此在使用lua脚本把两条操作合并,可以达到减少交互的作用。
实现原子操作
redis本身是单线程执行,可以实现原子操作,但对于抢锁这样的业务,则无法用原生接口实现。
比如实现一个分布式锁,加锁过程可以使用set命令实现
red:set(key, name, "NX", "EX", ttl)
但是解锁过程却不能简单使用del删除,因为锁有过期时间,过期后被其它人获取到,再进行释放就产生了bug。
所以先判断是否自己再进行删除
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
例可重入锁的实现:
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('setex', KEYS[1], ARGV[2], ARGV[1]) else return redis.call('set', KEYS[1], ARGV[1], 'ex', ARGV[2], 'nx') end
实现复杂逻辑
下面例子用zset实现时间戳队列,更新超时时间的算法。当用两次操作取出再放入时,会有丢数据风险,因此用脚本实现
redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'limit', ARGV[3], ARGV[4])\
if #ret > 0 then\
local val = {}\
for i = 1, #ret do\
val[2 * i - 1] = ARGV[5]\
val[2 * i] = ret[i]\
end\
redis.call('zadd',KEYS[1],unpack(val))\
end\