基于LRU-K算法设计本地缓存实现流量削峰

1、背景介绍

1.1、现象

QPS突然增长2倍以上(45w~60w每分钟) 生下面一些问题

1)响应接口响应时长增加了5(qps增加了2)

2机房局域网交换机带宽报警(1kM带宽使用了900M)

3redis获取数据接口响应时长增加等

1.2、原因

1某业务线对有限的产品进行推广

2在短时间内有大量重复数据查询请求

3)短时间从redis获取大量数据

1.3、解决方案

大量请求获取同一份数据,在本地存储这些数据

其优点如下:

1)直接从内存取数据,降低响应时间;

2不走redis减少服务与redis之间的交互流量

3)最终实现流量削峰

2、LRU-K模型设计

2.1、LRU算法介绍

Least recently used(LRU最近最少使用)根据数据的历史访问记录淘汰数据

核心思想

如果数据最近被访问过,那么将来被访问的几率更高

命中率

当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中急剧下降,缓存污染情况比较严重。

LRU算法模型如下图:

1)新数据插入到链表头部;

2)每当缓存命中(即缓存数据被访问),则将数据移到链表头部;

3)当链表满的时候,将链表尾部的数据丢弃。

2.2、LRU-K算法设计

LRU-K中的K代表最近使用的次数

主要目的

解决LRU算法“缓存污染”的问题。

核心思想

“最近使用过1次”的判断标准扩展为“最近使用过K次”

命中率

LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高

LRU-K模型如下图:

1)数据第一次被访问,加入到访问历史记录表(简称记录表);在记录表中对应的K单元中设置最后访问时间=new(),且设置访问次数为1;

2)如果数据访问次数没有达到K次,则访问次数+1。最后访问时间与当前时间间隔超过预设的值(如30秒),访问次数清0并加1;

3)当数据访问计数超过(>=)K次后,则访问次数+1。将数据保存到LRU缓存队列中,缓存队列重新按照时间排序;

4)LRU缓存队列中数据被再次访问后,重新排序;

5)LRU缓存队列需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。

子模块LRU存储模型:

类似ConcurrentHashMap,大致由二维数组+链表+访问队列三部分组成

 

Segment数组每个节点包含访问队列,访问队列模型如下图:

Segment数组每个节点都包含一个访问队列,通过这个队列来实现lru算法;

访问队列是一个环状双向链表,LRU算法由访问队列实现

3、缓存框架

3.1、系统数据存储组成

数据存储使用DB+本地缓存(LocalCache)+Redis三层结构,如下图:

3.2、数据查询流程

先从本地缓存取,本地缓存没有从redis取(同时更新本地缓存),redis没有从DB取(同时更新Redis)。具体步骤如下图:

1)先计算该数据获取总次数

2)未达到K访问记录时直接从redis取数据

3)达到K次访问记录时,从本地缓存取,本地缓存不存在时从redis获取数据(同时放入本地缓存中)

3.3、数据更新流程

删除缓存数据后,会再次从redis获取并更新缓存

4、调优过程

4.1、参数动态配置

配置参数如果放在Java类或配置文件中,每次调整都需要重启服务,执行不方便。

配置参数包括 K次访问统计数据清0时长(5分钟->30)K次访问阀值参数、调优日志开关(调优时打开,平时关闭)、本地缓存最大数量等等。

动态调整+实时生效:配置参数放在可实时更新的组件中(如apollo),每次修改后会立即生效。

4.2、性能调优

4.2.1、多机器本地缓存同步增加原来的业务响应时长

优化方案:同步缓存操作改成异步

4.2.2、服务发布时接口抖动

优化方案:

1)服务启动时执行比较耗时初始化操作:如jdbc初始化,K次统计结构初始化。

2)模拟核心dubbo接口,提前生成本地机器码。

5、实际效果

5.1、效果(1)

1)优化参数前QPS增长时,响应时间未见明显变短。如左右第1

2)参数调优上线后。QPS明显增加后,redis请求响应时间增加的情况下,整体响应时间未见变化。见左右最后1

5.2、效果(2)

QPS增加4倍,响应时间未见变化,跟平时一样

demo源代码见:https://download.csdn.net/download/love254443233/10672814

参考方法:LrukCacheTest

 

参考:

1)缓存淘汰算法--LRU算法

2)LRU算法四种实现方式介绍

3)Java实现的高效计数器

4)Efficient Counter in Java

5)基数排序

6)Guava Cache的缓存管理与使用

7)[Google Guava] 3-缓存

8)guava Cache源码分析(二)

9)Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装

10)https://gitee.com/liuyaohua/guava

 

沟通交流请关注公众号。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
LRU(Least Recently Used)是一种常见的缓存淘汰算法,它的基本思想是:将最不常用的数据最先淘汰掉。 具体实现方式通常是将缓存空间划分为若干个桶(bucket),每个桶中存储一组数据,同时记录它们最后一次被访问的时间。当缓存空间满了,需要淘汰一些数据时,LRU算法会根据数据最近使用的频率和时间进行淘汰。 算法的核心思想是通过计数器(例如最近访问计数器)和哈希表(或排序列表)来实现。计数器用于记录每个数据项最后一次被访问的时间,哈希表或排序列表用于快速查找和删除数据项。 具体实现步骤如下: 1. 当缓存空间满了,需要淘汰一些数据时,遍历缓存中的所有数据项,并记录它们最后一次被访问的时间。 2. 根据时间戳和计数器的值,将数据项按照最近使用的频率进行排序。 3. 将排名最靠后的数据项从缓存中删除,并释放相应的空间。 4. 如果需要继续淘汰数据,重复步骤1-3,直到缓存空间不再满为止。 这种算法的优点是实现简单,易于理解和实现,并且具有较好的性能和效率。但是,它也有一些缺点,例如当缓存命中率较低时,需要频繁地进行淘汰和替换操作,导致缓存命中率进一步下降。此外,如果需要支持高并发访问,还需要考虑并发控制和线程安全等问题。 总之,LRU算法是一种常用的缓存淘汰算法,适用于需要快速响应和低延迟的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值