Redis有序集合(SortedSet)的POP实现方法

最近在项目中遇到一个场景需要使用分布式的优先级队列,第一反应就是通过redissortedset数据结构来实现。但是阅读其API发现其没有类似ListLPOPRPOP指令,但是可以根据其提供的ZRANGZREVRANGEZREM组合命令来实现POP的操作。

POP的动作可以拆解成两步:

  • 获取队首元素
  • 删除队首元素

为保证执行的原子性,我们可以通过定义LUA脚本来执行组合指令:

local result = redis.call('ZRANGE', KEYS[1], 0, 0)
local element = result[1]
if element then
    redis.call('ZREM', KEYS[1], element)
    return element
else
    return nil
end"

将上面脚本中ZRANG替换为ZREVRANGE就可以实现从对尾POP

相关Java代码的实现,使用了spring-data-redisRedisTemplate

private static final String ZSET_LPOP_SCRIPT = "local result = redis.call('ZRANGE', KEYS[1], 0, 0)\n" +
            "local element = result[1]\n" +
            "if element then\n" +
            "    redis.call('ZREM', KEYS[1], element)\n" +
            "    return element\n" +
            "else\n" +
            "    return nil\n" +
            "end";

/**
  * 有序集合按得分的升序提取元素,提取完成后并该删除
  * @param key zset的key名字
  * @param resultClass 返回值类型
  * @return 提取的元素,集合为空时返回null
  */
public <T> T zsetLPop(String key, Class<T> resultClass){
    return redisTemplate.execute(new DefaultRedisScript<>(ZSET_LPOP_SCRIPT, resultClass), Collections.singletonList(key));
}

测试代码:

@Test
public void testZsetLpop() {
    String zsetKey = "test_sorted";
    Set<ZSetOperations.TypedTuple<String>> initValues = new HashSet<>();
    initValues.add(new DefaultTypedTuple<>("zhangsan", 1d));
    initValues.add(new DefaultTypedTuple<>("lisi", 2d));
    initValues.add(new DefaultTypedTuple<>("zhaoliu", 4d));
    initValues.add(new DefaultTypedTuple<>("wangwu", 3d));
    Long count = stringRedisTemplate.boundZSetOps(zsetKey).add(initValues);

    System.out.println("成功添加" + count + "条初始化数据");

    String element;
    do {
        element = redisUtil.zsetLPop(zsetKey, String.class);
        if (Objects.nonNull(element)) {
            System.out.println("Get element: " + element);
        }
    }
    while (element != null);
}

// 控制台输出
// 成功添加4条初始化数据
// Get element: zhangsan
// Get element: lisi
// Get element: wangwu
// Get element: zhaoliu

   转载规则


《Redis有序集合(SortedSet)的POP实现方法》 Angus_Lu 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Spring MVC中@RequestBody、@ResponseBody如何接收Abstract或Interface类型的参数? Spring MVC中@RequestBody、@ResponseBody如何接收Abstract或Interface类型的参数?
在使用Spring-MVC对外发布Rest接口时,在某些场景中,入参可能会希望是一个接口类型或者抽象类型。SpingMVC的序列化默认采用的是Jackson来实现入参与出参的序列化,在调用方传递一个json字符串时,如何将json字符串转换
2018-06-01 10:53:53
下一篇 
GraphQL入门介绍(一) GraphQL入门介绍(一)
什么是GraphQLGraphQL是由FaceBook提出的一种基于API的查询语言(尽管它也支持修改数据)。它能够根据描述按需获取字段数据,不会有任何冗余信息。也能够通过一个请求一次获取多个资源。GraphQL最早的实现是由FaceBoo
2018-01-31 19:39:21
  目录