Redis中惰性策略的启发和流量包应用设计

引言

        在技术领域,许多中间件之所以获得巨大成功,部分原因在于它们所采用的思想之先进。这些思想解决了一个个世纪难题,接下来我将讲述一个我学习到的思想,并将其应用至工作中的案例。

        惰性策略在日常编码中随处可见,但究竟什么是惰性策略呢?简而言之,惰性策略是一种优化方法,即在不需要进行计算或操作时,不会真正进行相关的处理,而是仅仅记录相关信息或轨迹。只有在需要执行行动操作时,才会触发从头到尾的真正计算。这种机制大大减少了不必要的资源消耗,提高了程序的效率。惰性策略的使用有很多,其中比较常见的便是Redis了,从中学习这些思想可以在我们日后遇到难题时得到帮助。

中间件设计思想:Redis过期Key淘汰策略

        在早些年作为编程小白的我,在使用Redis时常会想一些问题,例如:Redis的Key配置了过期时间,这个是怎么被删除的?Redis数据明明过期了,怎么还占用着内存?

主动策略和惰性策略

        对于这些问题,曾设想过他们的设计思路,例如对于如何清除过期的 Key ,很自然的可以想到就是可以给每个 key 加一个定时器,这样当时间到达过期时间的时候就自动删除 key,这种定时策略也叫主动策略。

        但从辩证角度来看这种方式使之有过期时间的 Key都需要一个定时器,那么这对 CPU 是极不友好的,会占用较多的CPU资源。后来在不断探究过程中,Redis同样也使用了惰性策略,即不用定时器,采取被动的方式,在访问一个 key 的时候去判断这个 key 是否到达过期时间了,过期了则删除掉。

        这种定期删除+惰性删除的Key过期策略,使得不会立即从内存中删除,当过期key未被客户端调用且未达到执行主动策略的时间,此Key依旧存在内存中。通过配合使用这两种删除策略,服务器可以很好地在合理使用CPU时间和避免浪费内存空间之间取得平衡。如果定期删除漏掉了很多过期 Key的同时也没及时去查,没走惰性删除,就是造成大量过期 key 堆积在内存里,最终会导致 redis 内存块耗尽,那么Redis此时会走内存淘汰机制。

如何淘汰过期的keys

       通过redis命令行运行set name xdclass 3600后,每个设置了过期时间的Key都会放入到一个独立的容器中。

定期删除

        隔一段时间,就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除,这种定期删除的方式可能会导致很多过期 Key 到了时间并没有被删除掉。

      摘自官方文档:EXPIRE | Redis

Redis 会每秒进行10次过期扫描,过期扫描不会遍历容器中所有的 key,而是采用一种特殊策略

        1)从容器中随机 20 个 key;
        2)删除这 20 个 key 中已经过期的 key;
        3)如果过期的 key 比率超过 1/4,那就重复步骤 1;

惰性删除

        当某个客户端试图访问key时,发现该key已超时会把此key从内存中删除。

主从架构Key删除策略

       从节点不会让key过期,而是主节点的key过期删除后,成为del命令传输到从节点进行删除
主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。指令同步是异步进行的,所以主库过期的 key 的 del 指令没有及时同步到从库的话,会出现主从数据的不一致,主库没有的数据在从库里还存在。

架构中的启发

        类似于Redis的这种思想其实在主流的中间架构中几乎随处可见,例如Spring中bean创建懒加载(延迟加载)、设计模式中单例创建的懒汉式、Mybatis的懒加载,借助于这种思想在工作中解决了许多数据更新问题,也延伸出了许多方案。例如我再在实际工作中流量包更新维护需求,免费流量包:业务为了拉新,鼓励新用户注册,赠送一个免费流量包,每天允许有一定次免费创建短链的次数。

        采用惰性策略解决方案,不用每天更新全部流量包,用的时候再更新即可。这样使得只要用户有使用,流量包都是可以得到更新,没使用的用户流量包不会去更新,避免了海量数据下更新维护的问题,如果采用定时更新,几千万用户更新记录都是会有不少时间的延迟。
整体步骤如下:

        1)查询用户全部可用流量包
        2)遍历用户可用流量包,判断是否更新-用日期判断(要么都更新过,要么都没更新,根据gmt_modified)。没更新的流量包后加入【待更新集合】中,增加【今天剩余可用总次数】;已经更新的判断是否超过当天使用次数,如果没超过则增加【今天剩余可用总次数】,超过则忽略;
        3)更新用户今日流量包相关数据;
        4)扣减使用的某个流量包使用次数;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UseTrafficVO {/*** 天剩余可用总次数 = 总次数-已用*/private   Integer dayTotalLeftTimes;/*** 当前使用流量包*/private   TrafficDO currentTrafficDO ;/*** 没过期,且今天没更新的流量包*/private  List<Long> unUpdatedTrafficIds = new ArrayList<>();}@Override@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)public JsonData useTraffic(UseTrafficRequest trafficRequest) {Long accountNo = trafficRequest.getAccountNo();//处理流量包,筛选未更新流量包、当前使用流量包UseTrafficVO useTrafficVO = processTrafficList(accountNo);log.info("今天可用总次数:{}, 当前使用的流量包:{}",useTrafficVO.getDayTotalLeftTimes(),useTrafficVO.getCurrentTrafficDO());//如果当前流量包为空,则没有可用流量包if(useTrafficVO.getCurrentTrafficDO() == null){return JsonData.buildResult(BizCodeEnum.TRAFFIC_REDUCE_FAIL);}log.info("待更新流量包列表:{}",useTrafficVO.getUnUpdatedTrafficIds());if(useTrafficVO.getUnUpdatedTrafficIds().size() >0) {//更新今日流量包trafficManager.batchUpdateUsedTimes(accountNo, useTrafficVO.getUnUpdatedTrafficIds());}//先更新,再增加此次流量包扣减int rows = trafficManager.addDayUsedTimes( accountNo,  useTrafficVO.getCurrentTrafficDO().getId(),1);if(rows !=1){throw new BizException(BizCodeEnum.TRAFFIC_REDUCE_FAIL);}return JsonData.buildSuccess();}/*** 处理流量包,筛选未更新流量包、当前使用流量包* @param accountNo*/private  UseTrafficVO processTrafficList(Long accountNo){//全部流量包List<TrafficDO> list = trafficManager.selectAvailableTraffics(accountNo);if (list == null || list.size() == 0) { throw new BizException(BizCodeEnum.TRAFFIC_EXCEPTION);}//天剩余可用总次数 = 总次数-已用Integer dayTotalLeftTimes = 0;//当前使用流量包TrafficDO currentTrafficDO = null;//没过期,且今天没更新的流量包List<Long> unUpdatedTrafficIds = new ArrayList<>();//今天日期String todayStr = TimeUtil.format(new Date(),"yyyy-MM-dd");for(TrafficDO trafficDO : list){//判断是否更新,用日期判断,不能用时间String trafficUpdateDate = TimeUtil.format(trafficDO.getGmtModified(),"yyyy-MM-dd");if(todayStr.equalsIgnoreCase(trafficUpdateDate)){//已经更新   剩余可用 = 天总次数-已用次数int dayLeftTimes = trafficDO.getDayLimit()-trafficDO.getDayUsed();dayTotalLeftTimes = dayTotalLeftTimes + dayLeftTimes;//选取 当次流量包if(dayLeftTimes>0 && currentTrafficDO == null){currentTrafficDO = trafficDO;}}else {//未更新dayTotalLeftTimes = dayTotalLeftTimes + trafficDO.getDayLimit();//记录未更新流量包  剩余可用 = 天总次数unUpdatedTrafficIds.add(trafficDO.getId());//选取 当次流量包if(currentTrafficDO == null){currentTrafficDO = trafficDO;}}}UseTrafficVO useTrafficVO =new UseTrafficVO(dayTotalLeftTimes,currentTrafficDO,unUpdatedTrafficIds);return useTrafficVO;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/786511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STL容器的一些操作(常用的,不全)

目录 string 1.string的一些创建 2.string 的读入和输出&#xff1a; 3.string的一些操作 4.彻底清空string 容器的函数 vector 1.vector的一些创建&#xff1a; 2.vector的一些操作&#xff1a; 3.vector的彻底清空并释放内存&#xff1a; queue 循环队列&#xff1…

兑换码生成算法

兑换码生成算法 兑换码生成算法1.兑换码的需求2.算法分析2.重兑校验算法3.防刷校验算法 3.算法实现 兑换码生成算法 兑换码生成通常涉及在特定场景下为用户提供特定产品或服务的权益或礼品&#xff0c;典型的应用场景包括优惠券、礼品卡、会员权益等。 1.兑换码的需求 要求如…

【DevOps工具篇】安装 LDAP 管理 GUI PhpLdapAdmin

【DevOps工具篇】安装 LDAP 管理 GUI PhpLdapAdmin 目录 【DevOps工具篇】安装 LDAP 管理 GUI PhpLdapAdmin启用远程管理功能安装 phpLDAPadmin对 phpLDAPadmin 进行补丁Apache 的配置编辑配置文件推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速…

Pointnet++分类和分割数据集准备和实验复现

5.分类数据集Modelnet40及可视化 Modelnet40分类数据集 原始的modelnet40是off文件&#xff0c;是cad模型 OFF文件是一种用于存储三维对象信息的文件格式&#xff0c;全称为"Object File Format"。它主要用于存储几何体的顶点、边和面信息&#xff0c;以及可能的颜…

面对复杂多变的网络攻击,企业应如何守护网络安全

企业上云&#xff0c;即越来越多的企业把业务和数据&#xff0c;迁移到云端。随着云计算、大数据、物联网、人工智能等技术的发展&#xff0c;用户、应用程序和数据无处不在&#xff0c;企业之间的业务边界逐渐被打破&#xff0c;网络攻击愈演愈烈&#xff0c;手段更为多。 当前…

未来的发展趋势-无服务架构-即将到来-让我们欢呼吧

无服务架构&#xff08;Serverless Architecture&#xff09;是一种颠覆性的云计算架构范式&#xff0c;旨在简化应用程序开发和部署过程&#xff0c;提高开发效率和降低成本。在传统的基础设施即服务&#xff08;IaaS&#xff09;和平台即服务&#xff08;PaaS&#xff09;模型…

uni app 扫雷

闲来无聊。做个扫雷玩玩吧&#xff0c;点击打开&#xff0c;长按标记&#xff0c;标记的点击两次或长按取消标记。所有打开结束 <template><view class"page_main"><view class"add_button" style"width: 100vw; margin-bottom: 20r…

Pytorch实用教程:torch.from_numpy(X_train)和torch.from_numpy(X_train).float()的区别

在PyTorch中&#xff0c;torch.from_numpy()函数和.float()方法被用来从NumPy数组创建张量&#xff0c;并可能改变张量的数据类型。两者之间的区别主要体现在数据类型的转换上&#xff1a; torch.from_numpy(X_train)&#xff1a;这行代码将NumPy数组X_train转换为一个PyTorch张…

Docker容器监控之CAdvisor+InfluxDB+Granfana

介绍&#xff1a;CAdvisor监控收集InfluxDB存储数据Granfana展示图表 目录 1、新建3件套组合的docker-compose.yml 2、查看三个服务容器是否启动 3、浏览cAdvisor收集服务&#xff0c;http://ip:8080/ 4、浏览influxdb存储服务&#xff0c;http://ip:8083/ 5、浏览grafan…

如何利用CSS实现文字滚动效果

1. 使用CSS3的animation属性 CSS3的animation属性可以让元素在一段时间内不停地播放某个动画效果。我们可以利用这个特性来实现文字滚动效果。 我们需要定义一个包含所有需要滚动的文本的容器元素。比如&#xff1a; <div class"scroll-container"><p>…

【每日一道算法题】有序数组的平方、长度最小的子数组

文章目录 有序数组的平方写在前面题目思路解析暴力解法双指针法 我的代码暴力解法双指针法 参考答案解法暴力方法双指针法 长度最小的子数组原题思路解析暴力法滑动窗口法 我的代码官方题解滑动窗口法 有序数组的平方 写在前面 本人是一名在java后端寻路的小白&#xff0c;希…

JAV八股--redis

如何保证Redis和数据库数据一致性 关于异步通知中消息队列和Canal的内容。 redisson实现的分布式锁的主从一致性 明天继续深入看这个系列问题 介绍IO复用模型

【机器学习300问】59、计算图是如何帮助人们理解反向传播的?

在学习神经网络的时候&#xff0c;势必会学到误差反向传播&#xff0c;它对于神经网络的意义极其重大&#xff0c;它是训练多层前馈神经网络的核心算法&#xff0c;也是机器学习和深度学习领域中最为重要的算法之一。要正确理解误差反向传播&#xff0c;不妨借助一个工具——计…

代码随想录算法训练营第24天|理论基础 |77. 组合

理论基础 jia其实在讲解二叉树的时候&#xff0c;就给大家介绍过回溯&#xff0c;这次正式开启回溯算法&#xff0c;大家可以先看视频&#xff0c;对回溯算法有一个整体的了解。 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法&#xff08;理…

第22章-路由协议概述

1. 定义 2. 路由协议的功能 3. 路由协议的分类 1. 定义 1)概述: 路由协议(Routing Protocol):计算、维护路由信息的协议; 功能:根据相应的算法产生路由,确定路由的有效性,维护路由; 常见路由协议:RIP、OSPF、BGP等; 2)路由协议与可路由协议 路由协议:实现路由选择的…

gpt的构造和原理

gpt是序列预测模型。 问答是通过确定问答格式样本训练出来的&#xff01;比如“Q&#xff1a;xxxx.A:xxx"本质还是根据前面的序列预测后面的序列。在自回归训练过程中&#xff0c;文本序列&#xff08;可能包含问题和紧随其后的答案&#xff09;被视为一个整体输入到模型…

深入理解数据结构——堆

前言&#xff1a; 在前面我们已经学习了数据结构的基础操作&#xff1a;顺序表和链表及其相关内容&#xff0c;今天我们来学一点有些难度的知识——数据结构中的二叉树&#xff0c;今天我们先来学习二叉树中堆的知识&#xff0c;这部分内容还是非常有意思的&#xff0c;下面我们…

前端秘法番外篇----学完Web API,前端才能算真正的入门

目录 一.引言 二.元素的获取和事件 1.获取元素 2.各种事件 2.1点击事件 2.2键盘事件 三.获取&修改操作 1.获取修改元素属性 2.修改表单属性 2.1暂停播放键的转换 2.2计数器的实现 2.3全选的实现 3.样式操作 3.1行内样式操作 3.2类名样式操作 四.节点 1.创…

记录Xshell使用ed25519公钥免密链接SSH

试了半天&#xff0c;Xshell好像没办法导入linux生成的ssh公钥,因此需要以下步骤实现免密登录 结论&#xff0c;在linux公钥文件中&#xff0c;将客户端生成的ed25519公钥加上去即可(一个公钥单独一行) 1.使用Linux生成秘钥文件(不需要输入私钥密码passphrase)或者直接创建一…

【Servlet】继承关系以及service方法

文章目录 一、继承关系二、相关方法 一、继承关系 Servlet接口下有一个GenericServlet抽象类。在GenericServlet下有一个子类HttpServlet&#xff0c;它是基于http协议。 继承关系 javax.servlet.Servlet接口​ javax.GenericServlet抽象类​ javax.servlet.http.HttpServ…