zset使用lua实现取最高分数中的随机成员
- 这种场景适用队列中不想要先入先出、先入后出
- 因为zset的命令都是带有排序功能的,所以取值时要不从大到小要不从小到大
- 所以我使用lua实现随机取成员
- 使用lua是因为可以保持原子性
- 在执行过程中Lua脚本是不会被其他命令或请求打断,因此可以保证每个任务的执行都是原子性的
- 当然还有一个方法你是可以通过一些随机算法做ID做成员,这样你就不需要使用lua脚本了
- 我这是为了旧数据兼容
zset的命令可以去看菜鸟文章
因为他的命令都会进行排序让我很懊恼,所以采用lua+limit+randomseed去解决
上代码
lua
-- 获取 zsetKey 中分数最高的成员
local zsetKey = KEYS[1]
local maxScoreData = redis.call('ZRANGE', zsetKey, -1, -1, 'WITHSCORES')
if #maxScoreData == 0 then
-- return 'Max score not found'
return nil
end
local maxScore = maxScoreData[2]
-- 获取最高分数的成员数量
local memberCount = redis.call('ZCOUNT', zsetKey, maxScore, maxScore)
if memberCount == 0 then
-- return 'No members with maxScore'
return nil
end
-- 生成一个随机索引
local time = redis.call('time')[1]
math.randomseed(time)
local randomIndex = math.random(0, memberCount - 1)
-- 使用 ZRANGEBYSCORE 获取随机成员
local member = redis.call('ZRANGEBYSCORE', zsetKey, maxScore, maxScore, 'LIMIT', randomIndex, 1)[1]
-- 删除选中的成员
redis.call('ZREM', zsetKey, member)
-- 返回选中的成员
return member
准备测试数据命令
添加15个成员
sh
ZADD tttt 1 "member1" 1 "member2" 1 "member3" 1 "member4" 1 "member5" 2 "member6" 2 "member7" 2 "member8" 2 "member9" 2 "member10" 3 "member11" 3 "member12" 3 "member13" 3 "member14" 3 "member15"
根据最高分数随机取值
sh
EVAL "local zsetKey = KEYS[1]; local maxScoreData = redis.call('ZRANGE', zsetKey, -1, -1, 'WITHSCORES'); if #maxScoreData == 0 then return 'Max score not found' end; local maxScore = maxScoreData[2]; local memberCount = redis.call('ZCOUNT', zsetKey, maxScore, maxScore); if memberCount == 0 then return 'No members with maxScore' end; math.randomseed(redis.call('time')[1]); local randomIndex = math.random(0, memberCount - 1); local member = redis.call('ZRANGEBYSCORE', zsetKey, maxScore, maxScore, 'LIMIT', randomIndex, 1)[1]; redis.call('ZREM', zsetKey, member); return member" 1 tttt
通过命令你可以观察到每次都是取的3分的成员,并且不是按照成员大小排序获取