Redis的zset集合

一、定义

        zset 是 set 的一个升级版本,它在 set 的基础上增加了一个顺序属性, 它和 set 一样,zset也是 string 类型元素的集合,且不允许重复的成员。

        不同的是每个元素都会关联一个 double类型的 score。 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 】

        集合中最大的成员数为 2的(32 - 1)次方 (4294967295, 每个集合可存储40多亿个成员)。 zset 最经典的应用场景就是排行榜。

二、ZSET的基本命令

1、ZADD  key   score1  member1  score2  member2  score3  member3

                将一个或多个member元素及其score值加入到有序集key当中。

2、ZRANGE  KEY  start  end   WITHSCORES

            返回 [start,end] 这个区间的所有值

本地:0>zadd lxh 1 a 2 b 3 c 4 d 5 e
"5"

本地:0>zrange lxh  0  -1 
 1)  "a"
 2)  "b"
 3)  "c"
 4)  "d"
 5)  "e"

本地:0>zrange lxh  0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "c"
 6)  "3"
 7)  "d"
 8)  "4"
 9)  "e"
 10)  "5"

本地:0>zrevrange lxh  0   -1
 1)  "e"
 2)  "d"
 3)  "c"
 4)  "b"
 5)  "a"


本地:0>zrevrange lxh 0 -1 withscores
 1)  "e"
 2)  "5"
 3)  "d"
 4)  "4"
 5)  "c"
 6)  "3"
 7)  "b"
 8)  "2"
 9)  "a"
 10)  "1"

3、zrem  key  member1  member2 ...

           移除有序集key中的一个或多个成员,不存在的成员将被忽略。

本地:0>zrange lxh 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "c"
 6)  "3"
 7)  "d"
 8)  "4"
 9)  "e"
 10)  "5"

本地:0>zrem lxh a
"1"

本地:0>zrange lxh 0 -1 withscores
 1)  "b"
 2)  "2"
 3)  "c"
 4)  "3"
 5)  "d"
 6)  "4"
 7)  "e"
 8)  "5"

4、zcard  key     

        返回有序集key的基数。

本地:0>zrange lxh 0 -1 withscores
 1)  "b"
 2)  "2"
 3)  "c"
 4)  "3"
 5)  "d"
 6)  "4"
 7)  "e"
 8)  "5"

本地:0>zcard lxh
"4"

5、zcount  key  min  max

        返回有序集key中,score值在min和max之间(默认包括score值等于min或max)的成员。

本地:0>zrange lxh 0 -1 withscores
 1)  "b"
 2)  "2"
 3)  "c"
 4)  "3"
 5)  "d"
 6)  "4"
 7)  "e"
 8)  "5"

本地:0>zcount lxh  1 5
"4"

本地:0>zcount lxh 2 3
"2"

6、zscore  key member 

                查找指定值,返回有序集key中,成员member的score值。

本地:0>zrange lxh 0 -1 withscores
 1)  "b"
 2)  "2"
 3)  "c"
 4)  "3"
 5)  "d"
 6)  "4"
 7)  "e"
 8)  "5"

本地:0>zscore  lxh 2
null

本地:0>zscore lxh b
"2"

本地:0>zscore lxh e
"5"

7、zincrby  key  increment   member

                为有序集key的成员member的score值加上增量increment。

本地:0>zrange lxh 0 -1 withscores
 1)  "b"
 2)  "2"
 3)  "c"
 4)  "3"
 5)  "d"
 6)  "4"
 7)  "e"
 8)  "5"

本地:0>zincrby lxh  10 b 
"12"

本地:0>zrange  lxh  0  -1 withscores
 1)  "c"
 2)  "3"
 3)  "d"
 4)  "4"
 5)  "e"
 6)  "5"
 7)  "b"
 8)  "12"

8、zrevrange   key start stop    [WITHSCORES]    

                       返回有序集key中,指定区间内的成员,降序。

        zrange   key start stop    [WITHSCORES]   

                       返回有序集key中,指定区间内的成员,升序。

本地:0>zrange  lxh  0  -1 withscores
 1)  "c"
 2)  "3"
 3)  "d"
 4)  "4"
 5)  "e"
 6)  "5"
 7)  "b"
 8)  "12"


本地:0>zrevrange lxh  0  -1 withscores
 1)  "b"
 2)  "12"
 3)  "e"
 4)  "5"
 5)  "d"
 6)  "4"
 7)  "c"
 8)  "3"

9、zrangebyscore  key   min  max   limit   offset   count

               返回指定集合【min ,max】区间的值,并进行分页-------返回值是升序排列

        

        zrevrangebyscore  key   min  max   limit   offset   count

               返回指定集合【min ,max】区间的值,并进行分页-------返回值是降序排列

本地:0>zrange  lxh  0  -1 withscores
 1)  "c"
 2)  "3"
 3)  "d"
 4)  "4"
 5)  "e"
 6)  "5"
 7)  "b"
 8)  "12"

本地:0>zrangebyscore lxh 0 10 limit 0 10
 1)  "c"
 2)  "d"
 3)  "e"

-- 降序排列的话,  就不是【min,max】  而是 【max,min】
本地:0>zrevrangebyscore lxh  0 10 limit 0 10


本地:0>zrevrange lxh  0  -1 withscores
 1)  "b"
 2)  "12"
 3)  "e"
 4)  "5"
 5)  "d"
 6)  "4"
 7)  "c"
 8)  "3"


本地:0>zrevrangebyscore lxh 10 0 limit 0 10
 1)  "e"
 2)  "d"
 3)  "c"

-- 升序排列的话,  就不是【max,min】  而是 【min,max】
本地:0>zrangebyscore lxh 10 0 limit 0 10

10、zrank  key  member

                查看这个member 在key集合里面排名(升序的排名)

        

本地:0>zrange lxh  0 -1 withscores
 1)  "c"
 2)  "3"
 3)  "d"
 4)  "4"
 5)  "e"
 6)  "5"
 7)  "b"
 8)  "12"

-- 排序最开始值是 0开始的
本地:0>zrank lxh b
"3"

        zrevrank  key  member

                查看这个member 在key集合里面排名(降序的排名)

本地:0>zrevrange lxh 0 -1 withscores
 1)  "b"
 2)  "12"
 3)  "e"
 4)  "5"
 5)  "d"
 6)  "4"
 7)  "c"
 8)  "3"


本地:0>zrevrank lxh b
"0"

11、zremrangebyrank  key  start  end

                移除key集合里面【start,end】这个排名区间的值

本地:0>zrange lxh  0 -1 withscores
 1)  "c"
 2)  "3"
 3)  "d"
 4)  "4"
 5)  "e"
 6)  "5"
 7)  "b"
 8)  "12"


本地:0>zremrangebyrank lxh 0 1
"2"

本地:0>zrange lxh 0 -1 withscores
 1)  "e"
 2)  "5"
 3)  "b"
 4)  "12"

12、zremrangebyscore  key  min  max

                移除key集合里面【min  ,max】这个区间的值

本地:0>zadd lxh 1 a 2 b 3 c  10 d 100 e 500 f
"6"

本地:0>zrange lxh 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "c"
 6)  "3"
 7)  "d"
 8)  "10"
 9)  "e"
 10)  "100"
 11)  "f"
 12)  "500"

本地:0>zremrangebyscore lxh 1 100
"5"

本地:0>zrange lxh 0 -1 withscores
 1)  "f"
 2)  "500"

13、zinterstore  key  num  key1  key2  key3   

                取key1,key2,key3的集合的交集放到key集合里面,由于需要取3个集合的交集,则num是3。返回值是交集的数量。也就是 key   集合的数量。

                取交集的时候,会将相同的member的source值合并的。(求和)       

本地:0>zrange lxh 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "f"
 6)  "500"



本地:0>zrange lxh1 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"

本地:0>ZINTERSTORE lxh2 2 lxh lxh1
"2"


本地:0>zrange lxh2 0 -1 withscores 
 1)  "a"
 2)  "2"
 3)  "b"
 4)  "4"


zinterstore lxh3 3 lxh lxh1 lxh2 
"2"


14、zunionstore  key   key1  key2  key3

                取key1,key2,key3的集合的并集放到key集合里面,,由于需要取3个集合的并集,则num是3。返回值是并集的数量,也就是 key   集合的数量。    

        取交集的时候,会将相同的member的source值合并的。(求和)            

本地:0>zrange lxh 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "dd"
 6)  "25"
 7)  "f"
 8)  "500"
本地:0>zrange lxh2 0 -1 withscores
 1)  "a"
 2)  "2"
 3)  "b"
 4)  "4"
本地:0>zrange lxh1 0 -1 withscores
 1)  "a"
 2)  "1"
 3)  "b"
 4)  "2"
 5)  "dd"
 6)  "25"
本地:0>zunionstore lxh8 3 lxh lxh1 lxh2
"4"
本地:0>zrange lxh8 0 -1 withscores
 1)  "a"
 2)  "4"
 3)  "b"
 4)  "8"
 5)  "dd"
 6)  "50"
 7)  "f"
 8)  "500"

三、ZSET的代码实例

需求一:完成热搜排行帮查询功能。

实现步骤:

        1、当前毫秒数 / (1000*60*60)  =时间块

        2、获取得到每个热搜的时间块-----(时间块,热搜热度,热搜id),热搜热度 = 点赞+转发+阅读。

        3、每隔1个小时,将从现在开始得到的时间块,和前23个小时时间块的24个集合取交集,将交集的结果给  到一个新集合,这个新集合就每日热搜的集合。

核心代码:

        初始化数据+刷新数据

package com.redis.zset;
/*
 * 本类用来实现微博热搜
 *
 * 1、微博的榜单分为  小时榜,日榜,周榜,月榜
 * 2、我们现在需要做的是两件事情,一个是造出微博的数据
 * 3、查看指定榜单
 *
 *
 * */

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("WB")
@Slf4j
public class WBRedisController {
    private static final String REDIS_WB_HOUR = "redisWB_hour";
    private static final String REDIS_WB_DAY = "redisWB_day";
    private static final String REDIS_WB_WEEK = "redisWB_week";
    private static final String REDIS_WB_MONTH = "redisWB_month";


    @Autowired
    private RedisTemplate redisTemplate;


    @PostConstruct
    public void init() {
        new Thread(() -> {
            try {
                initWbData();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }


    @PostMapping("refreshWBData")
    public String getDayWBData() {
        // 每小时统计一次每天的数据
        refreshDayData();
        // 每小时统计一次每周的数据
        refreshWeekData();
        // 每小时统计一次每月的数据
        refreshMonthData();
        return "数据刷新成功";
    }

    private void initWbData() throws InterruptedException {
        // 由于没有热搜事件,通过26个字母来代替热搜事件,同时存储每个小时每个热搜(字母)的数据
        // 获取当前小时的时间块
        long key = System.currentTimeMillis() / (1000 * 60 * 60);
        // 获取当月的时间块,组装为key值
        for (int i = 0; i < 24 * 30; i++) {
            String redisKey = REDIS_WB_HOUR + (key - i);
            for (int j = 0; j < 26; j++) {
                int source = new Random().nextInt(10);
                // 添加的是 key , member,source(member这个属性对应的数值--double类型)
                redisTemplate.opsForZSet().add(redisKey, String.valueOf((char)(96+i)), source);
            }
        }
        log.info("" +
                "一个月内的数据初始化完成");

        // 开始定时刷新数据
        while (true) {
            // 每隔5秒钟开始刷新数据
            // 初始化数据完成之后,每隔5秒刷新一次数据,模拟用户的访问
            Thread.sleep(5000);
            refreshData();
            log.info("数据刷新成功");
        }
    }

    /*
     * 刷新数据----统计每小时的数据之和
     * 定时统计数据---每次要统计的单位是一个小时
     * */
    private void refreshDayData() {
        long key = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> list = new ArrayList<>();
        // 获取当天内的所有的集合
        for (int i = 0; i < 24; i++) {
            String redisKey = REDIS_WB_HOUR + (key - i);
            this.redisTemplate.expire(redisKey, 40, TimeUnit.DAYS);
            list.add(redisKey);
        }
        redisTemplate.opsForZSet().unionAndStore(null, list, REDIS_WB_DAY);

    }


    /*
     * 刷新数据----统计每周的数据之和
     *
     * */
    private void refreshWeekData() {
        long key = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> list = new ArrayList<>();
        // 获取当天内的所有的集合
        for (int i = 0; i < 168; i++) {
            String redisKey = REDIS_WB_HOUR + (key - i);
            list.add(redisKey);
        }
        redisTemplate.opsForZSet().unionAndStore(null, list, REDIS_WB_WEEK);

    }


    /*
     * 刷新数据----统计每月的数据之和
     *
     * */
    private void refreshMonthData() {
        long key = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> list = new ArrayList<>();
        // 获取当天内的所有的集合
        for (int i = 0; i < (24 * 30); i++) {
            String redisKey = REDIS_WB_HOUR + (key - i);
            list.add(redisKey);
        }
        redisTemplate.opsForZSet().unionAndStore(null, list, REDIS_WB_MONTH);

    }


    /*
     * 定时刷新数据---模拟用户的请求
     * */
    private void refreshData() throws InterruptedException {
        long key = System.currentTimeMillis() / (1000 * 60 * 60);
        for (int i = 0; i < 26; i++) {
            // 添加的是 key , member,source(member这个属性对应的数值--double类型)
            redisTemplate.opsForZSet().incrementScore(REDIS_WB_HOUR + key, String.valueOf((char)(96+i)), new Random().nextInt(5));
        }
    }


}

获取排行榜数据信息

package com.redis.zset;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Set;

/*
 * 本类用来查询热搜排行榜的展示
 *
 * */
@RestController
@RequestMapping("WB")
@Slf4j
public class GetRdisWB {

    private static final String REDIS_WB_HOUR = "redisWB_hour";
    private static final String REDIS_WB_DAY = "redisWB_day";
    private static final String REDIS_WB_WEEK = "redisWB_week";
    private static final String REDIS_WB_MONTH = "redisWB_month";


    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("getHourWBData")
    public Set getHourWBData() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        Set set = redisTemplate.opsForZSet().rangeWithScores(REDIS_WB_HOUR+hour, 0, -1);
        return set;


    }

    @PostMapping("getDayWBData")
    public Set getDayWBData() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        Set set = redisTemplate.opsForZSet().rangeWithScores(REDIS_WB_DAY, 0, -1);
        return set;
    }

    @PostMapping("getWeekWBData")
    public Set getWeekWBData() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        Set set = redisTemplate.opsForZSet().rangeWithScores(REDIS_WB_WEEK, 0, -1);
        return set;
    }

    @PostMapping("getMonthWBData")
    public Set getMonthWBData() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        Set set = redisTemplate.opsForZSet().rangeWithScores(REDIS_WB_MONTH, 0, -1);
        return set;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值