Redis zset 的一些使用

最近做排行信息的时候用到了 Redis 的 Sorted Set, 写篇文章来和大家分享一波。

Sorted Set (有序集合)

通常我们也称为 zset,指的是在 redis 中,通常以 zset add 等命令操作

zset 通常包含 3 个 关键字操作:

  • key (与我们 redis 通常操作的 key value 中的key 一致)
  • score (排序的分数,该分数是有序集合的关键,可以是双精度或者是整数)
  • member (指我们传入的 obj,与 key value 中的 value 一致)

下面我们来看具体的相关命令


ZADD

ZADD key score member [[score member] [score member] ...]
  • [[score member] [score member] ...] 在Redis2.4 之后可以添加多个个元素

添加一个或者多个元素到 指定的 key 中。 如果该 key 中已经有了相同的 member,则更新该 member 的 score 值,并重排序;如果 key 存在于 redis 中, 但不是 zset 类型,则返回错误。

示例

# 添加1个元素

redis> ZADD key_1 100 xiaoming
(integer) 1

# 添加多个元素

redis> ZADD key_1 100 xiaoming 20 xiaohong
(integer) 2

#查看元素 score值递增(从小到大)来排序。 如果需要 按score值递减(从大到小)来排列,使用ZREVRANGE命令。
WITHSCORES选项,来让成员和它的score值一并返回

redis> ZRANGE key_1 0 -1 WITHSCORES
1) "xiaohong"
2) "20"
3) "xiaoming"
4) "100"

可以从上面的例子看出 ZADD 命令还是很容易理解的


ZREM

    ZREM key member [member ...]

说完了添加,我们肯定要讲移除了, 与 ZADD 命令一样,也支持多个删除操作(当然也是在2.4版本之后)。这里有个要注意的点,在移除的过程中,如果 member 不存在,将被忽略。

key 存在但是不是 zset,同样会报错

示例

# 移除单个元素

redis> ZREM key_1 xiaohong
(integer) 1


# 移除多个元素

redis> ZREM key_1 xiaohong xiaoming
(integer) 2

# 移除不存在元素

redis> ZREM key_1 xiaolin
(integer) 0

ZCARD

    ZCARD key

返回 key 的成员个数。 key不存在时,返回0

# 添加一个 key 及其成员

127.0.0.1:6379> ZADD key_1 100 xiaoming
(integer) 1

# 查找 key 的成员个数

127.0.0.1:6379> ZCARD key_1
(integer) 1

# 添加 第二个成员

127.0.0.1:6379> ZADD key_1 20 xiaohong
(integer) 1

# 可以看到 key_1 的成员个数变成了 2 个

127.0.0.1:6379> ZCARD key_1
(integer) 2
 
# 查看不存在的 key

127.0.0.1:6379> ZCARD key_2
(integer) 0

ZCOUNT

    ZCOUNT key min max

返回指定 key 的 分数在 min 与 max 之间的 member 个数

# 分数在 50 到 100 之间的个数

127.0.0.1:6379> ZCOUNT key_1 50 100
(integer) 1

ZSCORE

    ZSCORE key member

返回指定 key 和 member 的 分数值

# 获取 key_1 的成员 xiaoming 的分数值

127.0.0.1:6379> ZSCORE key_1 xiaoming
"100"

ZINCRBY

    ZINCRBY key increment member

为指定 key 的 member 的分数值 加 increment,其中 increment 代表数值,increment 可以是 负数,代表减去。

如果 key 或者 member 不存在,代表 ZADD 操作

# 查看 key_1 的 成员 xiaoming  的分数

127.0.0.1:6379> ZSCORE key_1 xiaoming
"100"

# 给 key_1 的 成员 xiaoming 的分数 加上 100
127.0.0.1:6379> ZINCRBY key_1 100 xiaoming
"200"

# 查看 key_1 的 成员 xiaoming  的分数
127.0.0.1:6379> ZSCORE key_1 xiaoming
"200"

ZRANGE

    ZRANGE key start stop [WITHSCORES]

返回指定 key 的 指定下标的成员, start stop 代表下标区间。

返回的结果默认按照分数==从小到大==排列,如果需要 ==从大到==小排列,需要是用 ZREVRANGE 命令。

  • start 和 stop 都以 0 开始,比如,0 为第一个成员,1 为第二个成员。
  • 可以用 -1 表示最后一个成员, -2 表示倒数第二个成员
  • WITHSCORES 可以返回相关成员 及其分数
# 查看 第一个 和 第二个成员
127.0.0.1:6379> ZRANGE key_1 0 1
1) "xiaohong"
2) "xiaoming"

# 查看所有的成员
127.0.0.1:6379> ZRANGE key_1 0 -1
1) "xiaohong"
2) "xiaoming"

# 查看成员 以及分数

127.0.0.1:6379> ZRANGE key_1 0 -1 WITHSCORES
1) "xiaohong"
2) "20"
3) "xiaoming"
4) "200"

ZREVRANGE

    ZREVRANGE key start stop [WITHSCORES]

用法和 ZRANGE 相同,只是排序是按照 分数 从大到小

# 按照分数从大到小排列
127.0.0.1:6379> ZREVRANGE key_1 0 -1 WITHSCORES
1) "xiaoming"
2) "200"
3) "xiaohong"
4) "20"

ZRANGEBYSCORE

    ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

返回指定分数的成员。分数在 min max 之间,返回的成员按照 分数 从小到大排列

  • LIMIT 指定返回结果的区间和 数量,与 sql 中的 limit 一样
# 查看分数在 0 到 200 之间的 成员
127.0.0.1:6379> ZRANGEBYSCORE key_1 0 200
1) "xiaohong"
2) "xiaoming"
  • min 和 max 可以带入 开区间和闭区间的概念
# 查看分数在 0 到 199 之间的成员
127.0.0.1:6379> ZRANGEBYSCORE key_1 0 (200
1) "xiaohong"

ZREVRANGEBYSCORE

    ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

与ZRANGEBYSCORE用法一样,只是返回的成员按照 分数从大到小排列

# 注意 分数要按照 max min 写,否则结果是空值
127.0.0.1:6379> ZREVRANGEBYSCORE key_1 200 0
1) "xiaoming"
2) "xiaohong

# 错误示例
127.0.0.1:6379> ZREVRANGEBYSCORE key_1 0 200
(empty list or set)

ZRANK

    ZRANK key member

返回指定key 的成员排名,按照分数 从小到大排列,其中==返回的排名是以 0 开始==

# 查看 key_1 的成员
127.0.0.1:6379> ZRANGE key_1 0 -1
1) "xiaohong"
2) "xiaoming"

# 查看xiaoming 的排名
127.0.0.1:6379> ZRANK key_1 xiaoming
(integer) 1

# 查看xiaohong 的排名
127.0.0.1:6379> ZRANK key_1 xiaohong
(integer) 0

ZREVRANK

    ZREVRANK key member

与 ZRANK 的用法相同,区别就是按照分数 从大到小排列

127.0.0.1:6379> ZRANGE key_1 0 -1
1) "xiaohong"
2) "xiaoming"

# 反转排序
127.0.0.1:6379> ZREVRANK key_1 xiaohong
(integer) 1

ZREMRANGEBYRANK

    ZREMRANGEBYRANK key start stop

移除指定 key 的指定排名介于 start 和 stop 之间的成员,同样排名以 0 开始。

# 查看排名
127.0.0.1:6379> ZRANGE key_1 0 -1
1) "xiaohong"
2) "xiaoming"

# 移除 排名 为0 的成员
127.0.0.1:6379> ZREMRANGEBYRANK key_1 0 0
(integer) 1

# 查看排名,已移除
127.0.0.1:6379> ZRANGE key_1 0 -1
1) "xiaoming"

ZREMRANGEBYSCORE

    ZREMRANGEBYSCORE key min max

移除指定key的 分数介于 min 和 max 之间的成员

# 查看成员
127.0.0.1:6379> ZRANGE key_1 0 -1 WITHSCORES
1) "xiaohong"
2) "20"
3) "xiaoming"
4) "200"

# 移除分数为 0 到 100 之间的成员
127.0.0.1:6379> ZREMRANGEBYSCORE key_1 0 100
(integer) 1

# 查看成员
127.0.0.1:6379> ZRANGE key_1 0 -1 WITHSCORES
1) "xiaoming"
2) "200"

ZINTERSTORE

    ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

上面的命令这么多,其实讲明了就是下面这几个

  • destination 给定的一个新的集合
  • numkeys 计算的几个集合
  • 之后的就是集合到的key了 举例
# 查看 key_1 的成员及其 分数
127.0.0.1:6379> ZRANGE key_1 0 -1 WITHSCORES
1) "xiaohong"
2) "50"
3) "xiaoming"
4) "200"

# 查看 key_2 的成员及其分数
127.0.0.1:6379> ZRANGE key_2 0 -1 WITHSCORES
1) "xiaohong"
2) "70"
3) "xiaoming"
4) "100"

# 把 key_1 和 key_2 根据 成员 把相关的 分数加起来到一个新的集合 sum_key
127.0.0.1:6379> ZINTERSTORE sum_key 2 key_1 key_2
(integer) 2
127.0.0.1:6379> ZRANGE sum_key 0 -1 WITHSCORES
1) "xiaohong"
2) "120"
3) "xiaoming"
4) "300"

ZUNIONSTORE

    ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

这个命令和上面的命令用法基本相同 只是有个算法因子的参数,比如 key_1 key_2 WEIGHTS 2 2 那么key_1 的分数和key_2 的分数 各自乘以 2

ZUNIONSTORE 用于计算给定的一个或多个有序集的==并集==。 ZINTERSTORE 则用于计算给定的一个或多个有序集的==交集==。

# 查看 key_2 的成员及其分数
127.0.0.1:6379> ZRANGE key_2 0 -1 WITHSCORES
1) "xiaohong"
2) "70"
3) "xiaoming"
4) "100"

# 查看 key_3 的成员及其分数
127.0.0.1:6379> ZRANGE key_3 0 -1 WITHSCORES
1) "xiaoli"
2) "80"
3) "xiaoxia"
4) "180"

# UNION key_2 key_3 没有写 乘法因子 默认是 1
127.0.0.1:6379> ZUNIONSTORE union0 2 key_2 key_3
(integer) 4
127.0.0.1:6379> ZRANGE union0 0 -1 WITHSCORES
1) "xiaohong"
2) "70"
3) "xiaoli"
4) "80"
5) "xiaoming"
6) "100"
7) "xiaoxia"
8) "180"

# UNION key_2 key_3 key_2 和 key_3 各自乘以2 
127.0.0.1:6379> ZUNIONSTORE union1 2 key_2 key_3 WEIGHTS 2 2
(integer) 4
127.0.0.1:6379> ZRANGE union1 0 -1 WITHSCORES
1) "xiaohong"
2) "140"
3) "xiaoli"
4) "160"
5) "xiaoming"
6) "200"
7) "xiaoxia"
8) "360"

下面则是 根据RedisTemplate 写的一个通用类,仅供参考

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    public <T> Boolean setIfAbsent(String key, T value) {
        ValueOperations<String, T> valueOperations = redisTemplate.opsForValue();
        return valueOperations.setIfAbsent(key, value);
    }

    public Boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    public <T> Boolean expireAt(T key, final Date date) {
        return redisTemplate.expireAt(key, date);
    }

    public void delete(final String key) {
        redisTemplate.delete(key);
    }

    public <T> void set(String redisKey, T obj, double score) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        //zset内部是按分数来排序的
        zSetOperations.add(redisKey, obj, score);
    }


    public <T> Double score(String redisKey, T obj) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.score(redisKey, obj);
    }

    /**
     * 移除key相关的成员
     *
     * @param redisKey
     * @param start
     * @param end
     * @param <T>
     * @return
     */
    public <T> Long removeRange(String redisKey, long start, long end) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.removeRange(redisKey, start, end);
    }

    public <T> Long remove(String redisKey, Object... values) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.remove(redisKey, values);
    }

    /**
     * 移除的是成员value
     *
     * @param redisKey
     * @param min
     * @param max
     * @param <T>
     * @return
     */
    public <T> Long removeRangeByScore(String redisKey, double min, double max) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.removeRangeByScore(redisKey, min, max);
    }

    public <T> Long zCard(String redisKey) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.zCard(redisKey);
    }

    public <T> Set range(String redisKey, long start, long end) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.range(redisKey, start, end);
    }

    /*
     * 按照【分数】排序对指定区间取值和分数
     */
    public <T> Set rangeByScoreWithScores(String score, double min, double max) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.rangeByScoreWithScores(score, min, max);
    }

    /**
     * 获取下标
     *
     * @param redisKey
     * @param v
     * @param <T>
     * @return
     */
    public <T> Long rank(String redisKey, Object v) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.rank(redisKey, v);
    }

    /**
     * 获取区间的个数
     *
     * @param redisKey
     * @param min
     * @param max
     * @param <T>
     * @return
     */
    public <T> Long count(String redisKey, double min, double max) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.count(redisKey, min, max);
    }

    public <T> Set rangeByLex(String redisKey, RedisZSetCommands.Range range) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.rangeByLex(redisKey, range);
    }

    public <T> Set rangeByScore(String key, double min, double max) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.rangeByScore(key, min, max);
    }

    public <T> Set rangeByScore(String key, double min, double max, long offset, long count) {
        ZSetOperations<String, T> zSetOperations = this.redisTemplate.opsForZSet();
        return zSetOperations.rangeByScore(key, min, max, offset, count);
    }

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券