RedisTemplate操作ZSet有序集合方法详解

0 次阅读

RedisTemplate是Spring Data Redis提供的核心操作工具,可以方便地对Redis中的各种数据结构进行读写。其中,ZSet(Sorted Set,有序集合)是一种非常常用的数据类型,它在集合的基础上增加了排序能力,每个成员都会关联一个score值,Redis会根据score自动完成排序。

在实际项目开发中,ZSet经常被用于排行榜、热门数据统计、延迟任务、优先级队列等业务场景。通过RedisTemplate操作ZSet,可以快速实现数据添加、查询、排名计算以及范围获取等功能。

Redis ZSet数据结构特点

Redis ZSet由两部分组成:

  • member:集合中的元素,具有唯一性。

  • score:与member关联的排序分值,可以是整数或浮点数。

例如:

key: user_rank

member       score
Tom          100
Jack         90
Lucy         80

Redis会按照score从小到大排序,如果score相同,则按照member的字典顺序排列。

与普通Set相比,ZSet不仅可以判断元素是否存在,还可以快速获取元素排名和分数,因此非常适合需要排序的数据场景。

RedisTemplate配置Redis环境

使用RedisTemplate操作ZSet之前,需要先完成RedisTemplate配置。

示例:

Java
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(factory);

        return template;
    }
}

业务代码中注入RedisTemplate:

Java
@Autowired
private RedisTemplate<String, Object> redisTemplate;

RedisTemplate针对不同数据类型提供了对应的操作接口:

Java
redisTemplate.opsForValue();  // String
redisTemplate.opsForList();   // List
redisTemplate.opsForSet();    // Set
redisTemplate.opsForZSet();   // ZSet

操作ZSet主要使用:

Java
redisTemplate.opsForZSet()

添加ZSet数据

使用add方法添加元素

RedisTemplate中添加ZSet数据主要使用ZSetOperations的add方法。

示例:

Java
redisTemplate.opsForZSet()
        .add("rank", "Tom", 100);

执行后:

rank:

Tom -> 100

如果添加多个用户:

Java
redisTemplate.opsForZSet()
        .add("rank", "Jack", 90);

redisTemplate.opsForZSet()
        .add("rank", "Lucy", 80);

Redis中的数据:

Lucy  80
Jack  90
Tom   100

score越小,默认排名越靠前。

批量添加ZSet数据

实际开发中通常需要一次添加多个数据,可以使用ZSetOperations.TypedTuple。

示例:

Java
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();

tuples.add(
    new DefaultTypedTuple<>("Tom", 100D)
);

tuples.add(
    new DefaultTypedTuple<>("Jack", 90D)
);

redisTemplate.opsForZSet()
        .add("rank", tuples);

这种方式适合初始化排行榜数据。

修改ZSet元素分数

ZSet中的member唯一,如果再次添加相同member,Redis会更新score。

例如:

Java
redisTemplate.opsForZSet()
        .add("rank", "Tom", 120);

原来的:

Tom 100

更新为:

Tom 120

排行榜业务中,用户积分增加通常直接调用add即可。

获取ZSet全部成员

获取所有元素

使用range方法:

Java
Set<Object> members =
        redisTemplate.opsForZSet()
        .range("rank",0,-1);

返回:

Lucy
Jack
Tom

参数说明:

  • 0:开始索引

  • -1:获取全部数据

倒序获取数据

如果希望按照score从高到低排序:

Java
Set<Object> members =
        redisTemplate.opsForZSet()
        .reverseRange("rank",0,-1);

结果:

Tom
Jack
Lucy

排行榜展示通常使用这种方式。

根据score范围查询

ZSet支持按照分数范围获取数据。

例如查询score在80到100之间的数据:

Java
Set<Object> result =
        redisTemplate.opsForZSet()
        .rangeByScore(
            "rank",
            80,
            100
        );

结果:

Lucy
Jack

如果需要限制返回数量:

Java
Set<Object> result =
        redisTemplate.opsForZSet()
        .rangeByScore(
            "rank",
            80,
            100,
            0,
            10
        );

表示获取前10条数据。

获取成员score值

通过score方法可以获取指定member的分数。

示例:

Java
Double score =
        redisTemplate.opsForZSet()
        .score("rank","Tom");

返回:

120

该方法常用于查询用户当前积分。

获取元素排名

ZSet最大的特点就是可以快速获取排名。

正序排名

Java
Long rank =
        redisTemplate.opsForZSet()
        .rank("rank","Tom");

返回:

2

排名从0开始。

倒序排名

排行榜通常按照分数从高到低展示,需要使用:

Java
Long rank =
        redisTemplate.opsForZSet()
        .reverseRank("rank","Tom");

例如:

Tom 第一名

rank返回:

0

删除ZSet元素

根据member删除

Java
redisTemplate.opsForZSet()
        .remove(
            "rank",
            "Tom"
        );

执行后Tom会从排行榜中移除。

批量删除

Java
redisTemplate.opsForZSet()
        .remove(
            "rank",
            "Tom",
            "Jack"
        );

适用于清理无效数据。

获取ZSet元素数量

统计集合大小:

Java
Long size =
        redisTemplate.opsForZSet()
        .size("rank");

例如:

3

也可以使用count统计指定score范围:

Java
Long count =
        redisTemplate.opsForZSet()
        .count(
            "rank",
            80,
            100
        );

ZSet实现排行榜案例

用户积分排行榜是ZSet最经典的应用。

业务逻辑:

用户获得积分:

Java
redisTemplate.opsForZSet()
        .incrementScore(
            "user_rank",
            "user1001",
            10
        );

incrementScore可以在原score基础上增加指定值。

例如:

原积分:

user1001 100

执行:

+10

结果:

user1001 110

获取排行榜前10名:

Java
Set<Object> topUsers =
        redisTemplate.opsForZSet()
        .reverseRange(
            "user_rank",
            0,
            9
        );

查询用户排名:

Java
Long rank =
        redisTemplate.opsForZSet()
        .reverseRank(
            "user_rank",
            "user1001"
        );

这种实现方式性能远高于数据库实时排序。

ZSet实现延迟任务

Redis ZSet还可以实现简单的延迟队列。

核心思路:

  • member保存任务内容。

  • score保存执行时间戳。

添加任务:

Java
long timestamp =
        System.currentTimeMillis()
        + 60000;

redisTemplate.opsForZSet()
        .add(
            "delay_queue",
            "order_1001",
            timestamp
        );

定时扫描:

Java
Set<Object> tasks =
        redisTemplate.opsForZSet()
        .rangeByScore(
            "delay_queue",
            0,
            System.currentTimeMillis()
        );

获取已经到期的任务,然后执行删除。

RedisTemplate操作ZSet注意事项

1. 注意score类型

Redis中的score必须是double类型。

Java中:

Java
100

和:

Java
100D

虽然效果类似,但建议明确使用Double。

2. 注意排名索引

Redis排名从0开始:

第一名 -> 0
第二名 -> 1

展示给用户时通常需要:

Java
rank + 1

3. 避免大范围查询

不要频繁执行:

Java
range(key,0,-1)

如果排行榜数据达到百万级,会产生较大的网络和内存压力。

推荐:

Java
reverseRange(key,0,99)

只查询需要的数据。

4. 合理设置Key名称

建议统一命名:

业务名称:功能名称

例如:

game:user_rank

article:hot_rank

方便维护和管理。

RedisTemplate操作ZSet常用方法总结

方法功能
add添加元素
remove删除元素
score获取score
rank获取正序排名
reverseRank获取倒序排名
range获取指定范围成员
reverseRange倒序获取成员
rangeByScore根据分数查询
incrementScore增加分数
size获取数量
count统计范围数量

总结

RedisTemplate操作ZSet提供了一套完整的有序集合管理能力,可以轻松实现数据排序、排行榜、积分系统以及延迟任务等功能。

开发过程中,opsForZSet()是操作ZSet的核心入口,通过add、range、reverseRange、score、rank、incrementScore等方法,可以满足大部分业务需求。

合理使用Redis ZSet不仅能够降低数据库压力,还能提升高并发场景下的数据访问效率,是Java后端开发中非常重要的Redis应用方式。