Redis Lua Scripting
Posted 搜狗测试
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis Lua Scripting相关的知识,希望对你有一定的参考价值。
对需要存储数据的需求来说,查询效率是一个必须解决的问题,redis的出现有一点点瑕疵的完美解决了这个问题,今天就来说一下后端大神是怎么干掉这个瑕疵的;
redis特性
快
支持多种数据结构
支持多种语言
主从复制
支持过期时间
等等
支持的数据类型及基本命令
字符串/数字
Set / get / append / strlen
Getrange / setrange
Incrby / decrby
hash
Hget / hset / hdel / hlen / hexists
Hmset / hmget / hgetall
Hkeys / hvals
Hincrby / hstrlen
双向列表/队列
Lpush / lpop / rpush / rpop
Len / lrange / lset / lrem
集合
Sadd / srem / smembers / sismember
Sdiff / sinter / sunion / smove
有序集合 / 优先队列
Zadd / zcard / zrem
Zrange / zrangebyscore / zcount / zrank
具体用法就不在这一一赘述了,大家可以自行搜狗,今天主要说一个问题
redis的问题
有一个场景:
需求是这样:需要给1000万的用户下发A开关,500万个用户下发B配置;
实现可以这样:前1000万个用户请求时下发A开关(其中每个请求来的时候通过redis方法hlen查一下这个set的长度是多少,如果<1000万就下发A配置,如果>1000w就下发B配置,当>1500w的时候取消下发配置)
对于测试来说验证这个需求有一条case,就是A开关下发的人数是否是1000万,如果按上述的方法实现,当请求并发时,这个数量很大可能不会正好=1000万,大概率会是1000万多一点;
为什么呢?因为由于redis是单进程多线程操作内存,当多个客户端并发的做redis查询时,相当于多个客户端共享内存,这样就可能出现接近1000万时,10个客户端同时调用hlen查询当前长度,都返回了9999999,那么就会同时给这10个客户端都下发A开关,所以就不能做到精准控制,由于redis查询效率太快,各个客户端之间几乎没办法做数据同步;
解决方案
使用Redis Lua Scripting
可以完美解决这个问题;如何解决呢?
local res = red:eval(script, 2, userSetKey, userPushKey, uid, 10000000, 86400)
if res[1] == 1 then
ngx.print("ok")
else
ngx.log(ngx.ERR, "do not push, reason:", res[2])
end
local script = [[
local userSetKey, userPushKey, uid, maxNum, expire = KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3]
maxNum = tonumber(maxNum) or 0
local res = redis.call("exists", userPushKey)
if res == 1 then
return {0, "has pushed today"}
end
local num = redis.call("hlen", userSetKey)
local res = redis.call("hexists", userSetKey, uid)
if num >= maxNum and res == 0 then
return {0, "exceed Maxnum"}
end
res = redis.call("setex", userPushKey, expire, 1)
res = redis.call("hset", userSetKey, uid, 1)
return {1, ""}
]]
通过定义script完成多次关联查询和业务逻辑,每个客户端打包执行redis查询,利用redis单线程的特性实现加锁的功能;
此外,利用script打包执行查询还可以节省网络请求;
以上是关于Redis Lua Scripting的主要内容,如果未能解决你的问题,请参考以下文章