作者:一个Java菜鸟

1、背景介绍

1.1、现象

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

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

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

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

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算法模型如下图:




redis流量大 redis流量削峰_修改本地host立即生效


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

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

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

2.2、LRU-K算法设计

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

主要目的

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

核心思想

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

命中率

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

LRU-K模型如下图:


redis流量大 redis流量削峰_缓存_02


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

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

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

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

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

子模块LRU存储模型:

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


redis流量大 redis流量削峰_修改本地host立即生效_03


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


redis流量大 redis流量削峰_数据_04


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

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

3、缓存框架

3.1、系统数据存储组成

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


redis流量大 redis流量削峰_数据_05


3.2、数据查询流程

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


redis流量大 redis流量削峰_redis流量大_06


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

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

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

3.3、数据更新流程


redis流量大 redis流量削峰_redis_07


删除缓存数据后,会再次从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)


redis流量大 redis流量削峰_redis_08


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

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

5.2、效果(2)


redis流量大 redis流量削峰_缓存_09


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

demo源代码见:

参考方法:

com.example.liuyaohua.cache.lruk.LrukCacheTest