Redis Sorted Set 实战案例分析

Redis Sorted Set 跳表的实现原理与分析 在 Redis 中,Sorted Set(有序集合)是一种非常重要且高效的数据结构。与普通的 Set 不同,Sorted Set 为每个元素关联了一个分数(score),并按照这个分数进行排序。在许多需要快速插入、删除、查找和范围查询的场景下,Sorted Set 提供了理想的解决方案。而其底层的核心数据结构之一便是跳表(Skip List),这种结构有效地支持了有序集合的高效操作。 阅读详情

没有最好的技术,只有最合适的技术。根据不同的业务场景,选用合适的技术实现,才是一个程序员应该做的事情。

本文通过引用企业中实际业务场景,记录设计思路和方案演进。此处不做具体技术讲解,重在系统设计思想和技术选型。

1. 需求背景

  • 用户端学员听课上传听课记录 和 管理端课件变更,都会引起学员班级听课率的变化,所以需要触发重新计算;

  • 上传听课记录 和 课件变更,带来的听课率重算,需要区分优先级,即优先处理学员听课导致的重算,次级处理课件变更导致的重算;

  • 管理端课件变更,重算数据需要去重,场景:多次变更同一班级下的多个课件,最终只需执行一次该班级-学员维度的数据重算即可,所以需要去重;

  • 重算听课率时,因业务逻辑要查询的数据量较多,为防止重算数据过多时,导致服务资源(CPU、内存等)和数据库压力过大,所以需要做消费限流。

功能实现总结:

  1. 数据优先级
  2. 重复数据去重
  3. 消费限流

2. 设计思路

2.1. 触发听课率重算

  1. 结合canal,监控课件表,有相关字段变化时触发重算;
  2. 在管理端课件变更的相关接口发送MQ消息,消费重算。
思考:
  1. 使用canal,监控相关数据库表,能够集中处理触发的条件,不用在众多的相关接口中添加代码发送消息,但是结合业务逻辑,所涉及的数据库表不一,所以不采用该方案;
  2. 在相关接口中发送MQ消息,虽然涉及的接口较多,但是更为直观,且不会频繁变更。
结论:
  1. 采用第二种,在相关接口中发送MQ消息,消费重算。

2.2. 数据优先级和消费限流

2.2.1. 数据优先级

a. 使用MySQL存储,单独设置一个字段表示优先级;

b. 使用Java自带的PriorityBlockingQueue优先队列;

c. 使用Redis的有序集合Sorted Set 或者 列表List。

思考:

a. MySQL存储,常规方案,虽然能实现,但是因为业务逻辑本身对MySQL的查询较多,所以会进一步增加MySQL的读写压力;

b. PriorityBlockingQueue,可以很容易的实现数据优先级,但是无法实现数据去重;

c. Redis的列表List,根据优先级使用List的LPush或RPush,可以实现数据优先级,且能分散该业务对MySQL的压力,但是无法实现数据去重;

d. Redis的有序集合Sorted Set,可以同时实现数据优先级和重复数据去重。

结论:
  1. 采用Redis的有序集合Sorted Set。
2.2.2. 消费限流

a. Semphore信号量

b. RabbitMQ配置 channel.basic-qos ,并且将 queue.auto-ack设置成false

思考:

a. Semphore在多线程访问时可以使用,但是此时是限制MQ消费速度,不适用。

结论:
  1. 使用RabbitMQ自身配置,设置basic-qos的数量。

3. 方案演进

3.1. MySQL实现

  • 新建数据待处理表,将管理端课件变更发送的MQ消息消费存储到待处理表中,再手动指定消费速度。
    MySQL实现

3.2. PriorityBlockingQueue

  • 采用Java优先队列,做数据优先级;
  • 数据存储在内存,服务重启会导致数据丢失,且存在内存溢出的风险,弃用。
    PriorityBlockingQueue

3.3. Redis Sorted Set

  • 采用redis的有序集合,做数据优先级和数据去重;
  • 计算听课率的数据存入redis,服务重启不会导致数据丢失;
  • 结合定时任务,从redis中取数据消费计算;
  • 定时任务加锁,保证不同节点不会取重复数据重复计算。
    Redis Sorted Set

4. 代码展示

  • 不同触发条件

/**
 * 保存或更新班级课件模块听课率
 *
 * @param consumerStatisticsBO 消费统计bo
 * @return boolean
 */
@Override
public boolean saveOrUpdate(CcConsumerStatisticsBO consumerStatisticsBO) {
    log.info("计算模块听课率 - saveOrUpdate -> consumerStatisticsBO:[{}]", consumerStatisticsBO);
    if (consumerStatisticsBO == null || consumerStatisticsBO.getConsumerTypeEnum() == null) {
        return false;
    }

    switch (consumerStatisticsBO.getConsumerTypeEnum()) {
        // 管理端课件变动
        case COURSEWARE_CHANGE:
            if (CollectionUtils.isEmpty(consumerStatisticsBO.getCoursewareIds())) {
                return false;
            }
            boolean noData = this.refreshModuleRateByCoursewareChange(consumerStatisticsBO);
            if (noData) {
                return true;
            }
            break;

        // 用户端听课记录上传
        case PLAY_RECORD:
            this.refreshModuleRateByPlayRD(consumerStatisticsBO);
            break;

        // 重算班级听课率
        case REFRESH_CLASS_RATE:
            this.refreshClassRate(consumerStatisticsBO);
    }

    return true;
}
  • 存入redis
/**
 * 添加计算数据到redis
 *
 * @param classModuleUserInfoBOList 班级模块用户详情bo集合
 */
private void addCalculateDataToRedis(List<CcClassModuleUserInfoBO> classModuleUserInfoBOList) {
    Set<DefaultTypedTuple> typedTupleSet = classModuleUserInfoBOList.parallelStream()
            .map(classModuleUserInfoBO -> {
                // 课件id置为null,用来在set中去重,实现去重计算模块听课率的数据(屌不屌0_0)
                classModuleUserInfoBO.setCoursewareId(null);
                return new DefaultTypedTuple(classModuleUserInfoBO, classModuleUserInfoBO.getPriority());
            })
            .collect(Collectors.toSet());
    redisTemplate.opsForZSet().add(RedisConst.CALCULATE_MODULE_STATS, typedTupleSet);
}
  • 定时任务取数据

/**
 * redis中单次取模块听课率计算数据的size
 */
@Value("${redis.calculate.module.stats.range.size}")
private Integer rangeSize;

/**
 * 计算模块统计数据
 * 每10秒执行一次,每次从redis取100条数据
 */
@Scheduled(cron = "0/10 * * * * ?")
public void calculateModuleStats() {
    log.info("定时任务 - CalculateModuleStatsSchedule - 开始执行");

    List<CcClassModuleUserInfoBO> classModuleUserInfoBOList = this.getClassModuleUserInfoFromRedis();
    if (!CollectionUtils.isEmpty(classModuleUserInfoBOList)) {
        classModuleUserInfoBOList.parallelStream()
                .forEach(classModuleUserInfoBO -> {
                    // 计算模块统计数据
                    try {
                        userClassSubdivisionStatisticsService.againCalculateClassModuleCourseRate(classModuleUserInfoBO);
                        // 在redis中移除该数据
                        Long remove = redisTemplate.opsForZSet().remove(RedisConst.CALCULATE_MODULE_STATS, classModuleUserInfoBO);
                        log.info("定时任务 - CalculateModuleStatsSchedule - 移除重算过的数据 -> redisTemplate.opsForZSet().remove:{}", remove);
                    } catch (Exception e) {
                        log.error("定时任务 - CalculateModuleStatsSchedule - 调用计算模块统计数据方法或redis.remove方法出错 -> 异常信息:{}",
                                ExceptionUtils.getStackTrace(e));
                    }
                });
    }
}

/**
 * 从redis获取计算班级模块听课率的数据
 *
 * @return {@link List<CcClassModuleUserInfoBO>}
 */
private List<CcClassModuleUserInfoBO> getClassModuleUserInfoFromRedis() {
    String lockKey = RedisConst.MODULE_STATS_SCHEDULE_LOCK;
    log.info("定时任务 - CalculateModuleStatsSchedule - 抢占锁 -> lockKey:{}, timeMillis:{}", lockKey, System.currentTimeMillis());
    RLock lock = redissonClient.getLock(lockKey);
    try {
        boolean tryLock = lock.tryLock(10, 30, TimeUnit.SECONDS);
        if (!tryLock) {
            log.error("定时任务 - CalculateModuleStatsSchedule - 获取锁失败 -> lockKey:{}, timeMillis:{}", lockKey, System.currentTimeMillis());
            return Collections.emptyList();
        }
    } catch (InterruptedException e) {
        log.error("定时任务 - CalculateModuleStatsSchedule - 获取锁失败 -> 异常信息:{}", ExceptionUtils.getStackTrace(e));
    }
    try {
        // 查看redis中是否有数据
        String redisKey = RedisConst.CALCULATE_MODULE_STATS;
        log.info("定时任务 - CalculateModuleStatsSchedule - redisKey:{}, timeMillis:{}", redisKey, System.currentTimeMillis());
        Long zCard = redisTemplate.opsForZSet().zCard(redisKey);
        log.info("定时任务 - CalculateModuleStatsSchedule - redis重算模块数据剩余数量zCard:{}, timeMillis:{}", zCard, System.currentTimeMillis());

        if (zCard == null || zCard <= 0) {
            return Collections.emptyList();
        }

        // 查看redis中是否有多节点不重复取数据标识,如果有,表示同一时刻只会有一个节点取数据
        String notRepeatRangeKey = RedisConst.MODULE_STATS_NOT_REPEAT_RANGE;
        Object notRepeatRange = redisTemplate.opsForValue().get(notRepeatRangeKey);
        if (!ObjectUtils.isEmpty(notRepeatRange)) {
            log.info("定时任务 - CalculateModuleStatsSchedule - notRepeatRange:{}, timeMillis:{}", notRepeatRange, System.currentTimeMillis());
            return Collections.emptyList();
        }

        // 获取从redis中range的开始下标
        String rangeStartKey = RedisConst.MODULE_STATS_RANGE_START;
        Object rangeStart = redisTemplate.opsForValue().get(rangeStartKey);
        long start = 0;
        if (rangeStart != null) {
            start = ((Integer) rangeStart).longValue();
        }
        long end = start + rangeSize - 1;
        if (end < start) {
            log.error("定时任务 - CalculateModuleStatsSchedule - 从redis中取数据的range下标错误 -> start:[{}], end:[{}], size:[{}]",
                    start, end, rangeSize);
        }
        log.info("定时任务 - CalculateModuleStatsSchedule - 从redis中取数据 -> start:[{}], end:[{}], size:[{}]", start, end, rangeSize);

        // 取数据
        Set range = redisTemplate.opsForZSet().range(redisKey, start, end);
        String jsonStr = JSONUtil.toJsonStr(range);
        List<CcClassModuleUserInfoBO> classModuleUserInfoBOList = JSONUtil.toList(jsonStr, CcClassModuleUserInfoBO.class);

        // 更新下次取数据的开始下标
        Long card = redisTemplate.opsForZSet().zCard(redisKey);
        long nextStart = 0;
        long remainNumber = 0;
        if (card != null && (remainNumber = card - end - 1) > 0) {
            nextStart = end + 1;
        }
        // 本次取出数据后,剩余0条数据,则其他节点不再重复取
        log.info("定时任务 - CalculateModuleStatsSchedule - 设置不同节点不重复取数据标识 -> remainNumber:[{}], remainNumber <= 0:{}",
                remainNumber, remainNumber <= 0);
        if (remainNumber <= 0) {
            log.info("定时任务 - CalculateModuleStatsSchedule - 设置不同节点不重复取数据标识 -> notRepeatRangeKey:[{}], timeMillis:{}",
                    notRepeatRangeKey, System.currentTimeMillis());
            redisTemplate.opsForValue().set(notRepeatRangeKey, 1, 8, TimeUnit.SECONDS);
        }
        log.info("定时任务 - CalculateModuleStatsSchedule - redis中还剩数据量:{},下次取数据的开始下标:{}",
                remainNumber < 0 ? 0 : remainNumber, nextStart);
        redisTemplate.opsForValue().set(rangeStartKey, nextStart);

        return classModuleUserInfoBOList;
    } finally {
        lock.unlock();
        log.info("定时任务 - CalculateModuleStatsSchedule - 释放锁 -> timeMillis:{}", System.currentTimeMillis());
    }
}

5. 其他

欢迎大家关注微信公众号 “金日成长”,不定期分享 企业实战案例分析、Java技术 和 其他小知识等
金日成长

Redis实战 | 使用Redis 的有序集合(Sorted Set)实现排行榜功能,和Spring Boot集成 Redis 的有序集合(Sorted Set)是一个基于分数(score)排序的数据结构,它在 Redis 中非常重要,常用于实现排行榜、近似计数器等功能。Redis 的有序集合(Sorted Set)是基于跳跃表(Skip List)实现的。跳跃表是一种高效的数据结构,其插入、删除和查找操作的平均时间复杂度都是 O(log n),相对于平衡树(如红黑树)的实现要简单很多。跳跃表的结构类似于链表,每个节点除了保存元素值外,还包含一个指针数组,分别指向对应层次的下一个节点。 阅读详情

相关推荐

《深入 UFS Explorer:自定义 RAID 配置指南》

UFS Explorer支持以虚拟模式组装任何受支持的RAID配置,以便后续访问其内容或恢复丢失数据。启动RAID构建器;向RAID构建器主内容区域的列表中添加RAID组件;为不可用组件插入占位符(需当前RAID级别支持且软件兼容);调整组件的准确顺序;在“虚拟RAID配置”区域设置RAID参数;以虚拟模式组装阵列各部分。程序主窗口“存储设备导航面板”中打开的任何存储设备(包括驱动器、逻辑卷、磁盘镜像、虚拟磁盘及其他复杂存储设备)均可作为RAID组件。

melonbo的专栏 1405

支持优先队列操作的TreeSet

支持优先队列操作的TreeSet 去重优先级队列

kiu000的专栏 910

YOLOv8实战火焰和烟雾检测 (视频教程)

本课程将手把手地教大家使用YOLOv8结合可变形卷积(deformable convolution)训练火焰和烟雾数据集,完成一个多目标检测实战项目,可实时检测图像、视频、摄像头和流媒体(http/rtsp)中火焰和烟雾,并提供可视化演示界面 。

bai666ai的博客 1918

Redis有序集合(sortSet)的底层实现

转载自:http://www.cnblogs.com/WJ5888/p/4516782.html Redis中支持的数据结构比Memcached要多,如基本的字符串、哈希表、列表、集合、可排序集,在这些基本数据结构上也提供了针对该数据结构的各种操作,这也是Redis之所以流行起来的一个重要原因,当然Redis能够流行起来的原因,远远不只这一个,如支持高并发的读写、数据的持久化、高效的内存管理及淘...

DoubleWeiWei001 3万+

RK3568开发笔记- (TX/RX) RGMII delayline 功能调试

本文详细介绍了RK3568芯片RGMII接口delayline功能的调试过程。针对千兆以太网通信不稳定问题,通过分析RGMII时序要求,利用RK3568内置的delayline功能补偿信号延迟。调试方法包括扫描delayline窗口、测试中间值配置、优化硬件设计等步骤。文章还总结了常见问题解决方案,如PHY选型建议和自动扫描功能配置。最终实现了千兆速率下丢包率降至0.01%以下的目标,为类似开发场景提供了实用参考。

flypig has a dream! 318

Python无法拒绝的表白界面(完整代码)

Python制作一个无法拒绝的表白界面的完整代码。

AIGC 5万+

java redis 去重队列_【请求去重】java基于分布式锁解决重复请求问题

问题:开发app时,app快速连续点击会向服务器连续发起请求,导致数据库出现重复数据。解决思路:对用户唯一标示+请求uri+请求参数进行去重。1、利用jvm BlockingQueue堵塞队列,来一条请求判断是否存在队列,不存在添加,存在去除。优点:消耗资源较小。缺点:在分布式下,请求会分发在不同服务器上。2、利用分布式锁,redis、zokeeper等,进行加锁。优点:解决分布式下请求分发问题。...

weixin_29038345的博客 1083

EPO: Hierarchical LLM Agents with Environment PreferenceOptimization

长期决策和规划任务依然是基于大型语言模型(LLM)的智能体面临的一大挑战(Valmeekam 等,2023;Liu 等,2023;Silver 等,2024)。这些任务需要智能体进行多步骤的规划,同时保持行为的一致性和目标导向,这对于通常被设计用于即时和局部预测的大型语言模型来说颇为困难。此外,针对具身智能体的大型语言模型微调面临的一个关键难题是大规模标注数据的需求(Reed 等,2022)。

qq_52053775的博客 143

Java容器笔记

Java容器CollectionListVectorArrayListLinkedListSetSortedSetHashSetLinkedHashSetQueueDequeArrayDequeBlockingDequeBlockingQueueArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueueLinkedTransferQueueConcurrentLinkedQueueDelayQueueMapHashtableHashMapSynchr

yuyangbaby的博客 410

一篇文章足够你学习Bluez,提供史上最全的Bluez教程,让你告别Bluez从入门到放弃

Bluez是真的难,说“入门到放弃”真的毫不夸张,Bluez的学习曲线陡峭,要求对Linux 系统/驱动概念、D-Bus 通信和蓝牙协议都有要求,加上网络上其实没有一个完整的介绍Bluez系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员,都有不小的难度。

朝气蓬勃 2543

出现 RuntimeError: adaptive_avg_pool2d_backward_cuda does not have a deterministic implementation的解决方法

大致就是在执行主函数的训练函数中,在反向传播这一块出现了问题。解决方法就是添加如下代码块。找到提示bug的行数。

码农研究僧的博客 5534

JDK8 PriorityBlockingQueue(Collection<? extends E> c)构造器 源码解析

PriorityBlockingQueue的这个(Collection<? extends E> c)重载版本构造器的源码有几个地方有点难懂,但本文讲述内容不是PriorityBlockingQueue的重点理解内容,所以本文单独讲解。

anlian523的博客 527

猫群优化算法(CSO)详解:从生物灵感到智能优化

猫群优化算法(CSO)是一种模拟猫科动物行为的群体智能优化算法。该算法基于猫的两种主要行为模式:搜寻状态(全局探索)和跟踪状态(局部开发)。CSO通过混合率(MR)动态调整两种模式的比例,实现探索与开发的平衡。算法核心包括搜寻模式的变异操作和跟踪模式的速度-位移模型,具有收敛性能好、全局搜索能力强等特点。相比传统优化算法,CSO在高维复杂问题中表现优异,已成功应用于函数优化、图像处理等多个领域。算法性能受混合率、变化域等参数影响,自适应参数策略可进一步提升优化效果。

qq_42212808的博客 620

redis详解 (最开始写博客是写redis 纪念日在写一篇redis)

Redis是一个开源的高性能内存数据库,支持多种数据结构,包括字符串、哈希、列表、集合和有序集合。其核心特性包括内存存储、持久化、原子操作和事务支持,适用于缓存、会话存储、消息队列等场景。相比Memcached,Redis支持数据持久化和复杂数据类型;相比MySQL,Redis读写速度更快但存储容量有限。Redis还提供HyperLogLog用于近似基数统计,适合独立访客统计等场景。

qq_62374596的博客 1107

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【redis】数据类型之sorted_set

sorted_set:有序集合,在set的基础上增加score属性用来排序,在redis中,数据类型对应的命令一般以数据类型的首字母开头,但是单词s已经被string类型使用了,所以sorted_set类型的相关命令只能使用26个英文字母中的最后一个字母z来开头,所以有些人也把sorted_set称为zset。有关sorted_set类型的命令可以通过命令来查看。有关命令的使用可以通过help 命令来查看,例如help zadd。

morris 9044

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Redis的sorted_set的用法,看完这一篇就够了

各位小伙伴们,我们上次讲过Redis中Set的相关操作,以及适用的一些场景,例如抽奖等。今天要讲的sorted_set,其实也是set的一种,也具有set去重的特点。和set不同的是,sorted_set是有序的,从字面意思也可以看得出来。 那sorted_set是怎么排序的,根据什么规则排序呢?往下看。 我们先来看下sored_set提供了哪些命令。 127.0.0.1:6379> help @sorted_set BZPOPMAXkey[key...]timeout summ...

积跬步,成千里。 3896

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Redis之sorted set 命令

Redis之sorted set 命令

weixin_52690231的博客 1148

Redis Sorted Set 深度解析:从原理到实战应用

Redis 的 Sorted Set 数据结构以其独特的有序性和高效的操作性能,在众多实际应用场景中发挥着重要作用。通过掌握其原理、常用操作及优化技巧,开发者能够充分利用 Sorted Set 的优势,解决数据排序、范围查询等复杂问题。无论是构建实时交互的应用功能,还是优化数据存储与检索逻辑,Sorted Set 都值得深入研究和灵活运用。在实际开发中,你是否也遇到过适合使用 Sorted Set 的场景?欢迎在评论区分享你的经验和见解,一起探讨 Redis 数据结构的更多可能性!

weixin_52755040的博客 1223

Redis高级数据类型解析(二)——Set、Sorted Set与Geo实战指南

通过合理使用Set、Sorted Set和Geo类型,开发者能够高效解决标签系统、实时排行榜、地理位置服务等复杂需求。:海量数据基数统计(如UV计算)。Stream:消息队列与事件溯源。Lua脚本:组合命令保证原子性。掌握Redis高级数据类型,将极大提升系统的性能和扩展性,助力构建高并发、低延迟的实时应用。

java2cy 1073

Redis:数据结构与基础操作(String、List、Hash、Set、Sorted Set)

String 是 Redis 中最基础、最常用的数据结构。它是二进制安全的,也就是说可以存储任意类型的数据,例如:普通字符串整数、浮点数图片(Base64 编码后)JSON / 序列化对象 / 二进制内容。

m0_73341242的博客 1313

Redis基础数据结构之 Sorted Set 有序集合 源码解读

Redis 的有序集合(Sorted Set)是一种数据结构,它可以存储唯一的字符串成员,并为每个成员关联一个分数(score),以此来对成员进行排序。有序集合结合了集合的唯一性和分数排序的功能,使其非常适合需要按分数排序的应用场景。在Redis内部,有序集合通常使用跳跃表(skiplist)来实现,这使得插入、删除和查找操作的时间复杂度接近O(log N)。跳跃表的使用使得有序集合在处理大量数据时依然能够保持高效的性能。

无休居士的博客 1756

Redis 集合(Sorted Set)方法使用详解

本文主要是介绍集合(SortedSet)中常见方法的使用,包括对应的java使用方式,ZADD、ZREM、ZSCORE、ZINCRBY、ZCARD、ZRANK、ZREVRANK、ZRANGE、ZREVRANGE、ZRANGEBYSCORE、ZREVRANGEBYSCORE、ZCOUNT、ZREMRANGEBYRANK、ZREMRANGEBYSCORE、ZINTERSTORE、ZUNIONSTORE、ZPOPMAX、ZPOPMIN、BZPOPMAX、BZPOPMIN

嘉禾嘉宁papa 9443

Redis 的命令详解 - Sorted Set 篇

Sorted Set 命令详解 跟 SET 相关的命令一共有 25 种,这里只介绍常用的 ZADD : 添加成员 起始版本 : 1.2.0 时间复杂度 : O(log(N)),其中N是有序集合中的元素数。 向有序集合(sorted set)中,添加 分数/成员(score/member)对,可以同时指定多个分数/成员(score/member)对。 分数值是一个带符号带双精度浮点型数...

编程爬阶小马的博客 1228

aredis数据结构实战:列表、集合与Sorted Set完整操作示例

**aredis** 是一个高效易用的 Python asyncio Redis 异步客户端,完整支持 Redis 单节点、Sentinel 哨兵与 Cluster 集群模式。本文面向新手,带你快速掌握 aredis 中三大核心数据结构——**列表(List)**、**集合(Set)** 与**有序集合(Sorted Set)** 的实战用法,包含阻塞队列、成员判断、排行榜等经典场景的完整操作示例

gitblog_00891的博客 833

Redis中Sorted-Set时间复杂度和实战

一、概述: Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Sets中的成员必须是唯一的,但是分数(score)却是可以重复的。 在S...

FeelTouch Labs 7307

Redis篇-9--数据结构篇1--五种基本结构(String,List,Set,Sorted Set,Hash,BLPOP阻塞逻辑)

Redis 是一个高性能的键值存储系统,支持多种数据结构。每种数据结构都有其独特的特点和适用场景。

weisian的博客 3万+
下一篇: springboot maven deploy后服务拉取jar报“程序包XXX不存在”错误
Wildwolf_
博客等级 码龄12年 0粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值