首页 > 数据库 >阿里面试:如何解决Redis热点Key问题? (按此作答,拿 60W年薪)

阿里面试:如何解决Redis热点Key问题? (按此作答,拿 60W年薪)

时间:2024-10-16 10:25:53浏览次数:1  
标签:60W 缓存 Key Redis redis key 热点 MONITOR

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领

免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


阿里面试:如何解决Redis热点Key问题? (按此作答,拿 60W年薪)

尼恩特别说明: 尼恩的文章,都会在 《技术自由圈》 公号 发布, 并且维护最新版本。 如果发现图片 不可见, 请去 《技术自由圈》 公号 查找

尼恩说在前面:

在40岁老架构师 尼恩的100+读者交流群 中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:

  • 什么是热点key? 热点key会带来哪些问题?
  • 如何监测热点key?如何解决热点key?
  • 你们生产遇到过 hotkey吗? 怎么解决的?

最近有小伙伴在面试 阿里P7,又遇到了相关的面试题。小伙伴是按照尼恩的方案去答的。

没想到,面试官非常满意,小伙伴阿里P7 offer到手,年薪 60W,还特意来感谢了尼恩。

所以,尼恩给大家做一下系统化、体系化的梳理。

这里,把这个答案共享出来,帮大家内力猛增,让面试官爱到 “不能自已、口水直流”。

然后,帮大家 实现”offer直提”,都拿到阿里P7 offer,逆天改命。

当然,这道面试题 以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书

1 宏观总结:热点key的危害和解决方案

在这里插入图片描述

什么是HotKey?

在某个Key接收到的访问次数、显著高于其它Key时,我们可以将其称之为HotKey/热key。

在这里插入图片描述

从访问量上来说,常见的HotKey如:

  • 某Redis实例的每秒总访问量为10000,而其中一个Key的每秒访问量达到了7000(访问次数显著高于其它Key)
  • 对一个拥有上千个成员且总大小为1MB的HASH Key每秒发送大量的HGETALL(带宽占用显著高于其它Key)
  • 对一个拥有数万个成员的ZSET Key每秒发送大量的ZRANGE(CPU时间占用显著高于其它Key)

在这里插入图片描述

从业务上来说, 常见的HotKey如:

1 、MySQL等数据库会被频繁访问的热数据

如爆款商品的skuId。

2 、redis的被密集访问的key

如爆款商品的各维度信息,skuId、shopId等。

3 、机器人、爬虫、刷子用户

如用户的userId、uuid、ip等。

4 、某个接口地址

如/sku/query或者更精细维度的。

注意,我们的HotKey探测框架只关心key,其实就是一个字符串,

2 热点Key的巨大危害

2.1 HotKey对服务层和数据层的风险

在拥有大量并发用户的系统中,HotKey一直以来都是一个不可避免的问题。

  • 比如秒杀活动、热点微博、热评,某件商品被数万次点击浏览或购买时,就会造成热点问题
  • 比如大量发布、浏览的热点新闻、热点评论等读多写少场景也会产生热点问题
  • 比如是 瞬间大量开启的爬虫用户,
  • 突发大批机器人以远超正常用户的速度发起极其密集的请求,这些机器人只需要很小的代价,就能发出百倍于普通用户的请求量,从而大幅挤占正常用户的资源。

以京东为例的这些头部互联网公司,动辄某个爆品,会瞬间引入每秒上百万甚至数百万的请求,当然流量多数会在几秒内就消失。

但就是这短短的几秒的HotKey,就会瞬间造成其所在redis分片集群瘫痪。

原因也很简单,redis作为一个单线程的结构,所有的请求到来后都会去排队,当请求量远大于自身处理能力时,后面的请求会陷入等待、超时。

由于该redis分片完全被这个key的请求给打满,导致该分片上所有其他数据操作都无法继续提供服务,也就是HotKey不仅仅影响自己,还会影响和它合租的数据。

这样,redis 缓存没有响应之后,相当于 redis 击穿, 请求直接转向DB

DB的吞吐量,比如会低很多,DB 就会雪崩。

2.2 HotKey带来的常见问题

  • HotKey占用大量的Redis CPU时间,使其性能变差并影响其它请求;
  • Redis Cluster中各node流量不均衡造成Redis Cluster的分布式优势无法被Client利用,一个分片负载很高而其它分片十分空闲从而产生读/写热点问题;
  • 在抢购、秒杀活动中,由于商品对应库存Key的请求量过大,超出Redis处理能力造成超卖;
  • HotKey的请求压力数量超出Redis的承受能力造成缓存击穿,此时大量强求将直接指向后端存储,将后端存储打挂并影响到其它业务;

在这里插入图片描述

  • 流量过于集中,突破物理网卡的极限
  • 请求过多,缓存分片服务被打垮
  • 穿透DB

2.3 热点Key的巨大危害

当某热点Key请求在某一主机上超过该主机网卡上限时,由于流量过度集中,导致服务器中其它服务无法正常进行
=》
缓存服务崩溃:热点过于集中,热点Key缓存过多,超过目前的缓存容量,就会导致缓存分片服务被打垮
=》
DB雪崩: 此时再有请求产生,会缓存到后台DB,导致缓存穿透,进一步还会导致DB雪崩。

=》

于是 系统 雪崩。

在这里插入图片描述

3 HotKey的解决方案

通常的解决方案主要集中在对客户端和Server端进行改造。

3.1 本地缓存方案: 实现就近访问

在这里插入图片描述

在应用层引入本地缓存(如Guava Cache、Caffeine、nginx share dict等),将热点数据缓存在本地内存中,减少对Redis的直接访问,从而降低Redis的压力。

优点:减少了对Redis的读请求,降低了热点Key带来的负载压力。

3.2 打撒热点:将热点Key分散到不同的服务器

在这里插入图片描述

通过改变Key的结构(如添加随机前缀),将同一个热点Key拆分成多个Key,使其分布在不同的Redis节点上,从而避免所有流量集中在一个节点上。

优点:有效避免了单点瓶颈,提高了Redis集群的整体吞吐量。

3.3 Redis 从节点扩容

在这里插入图片描述

如果是用 : redis 主从架构,可以通过增加Redis集群中的从节点,增加 多个读的副本。

通过对读流量进行 负载均衡, 将读流量 分散到更多的从节点 上,减轻单个节点的压力。

优点:通过水平扩展,Redis可以处理更大的负载,特别是针对高并发的读请求。

Redis 从节点扩容 方案非常受限: 如果是用 redis cluster 集群架构,这种方案就没辙了。

关于 redis 主从架构,redis cluster 集群架构 的区别,请看尼恩的下面深度文章:

希音面试:Redis脑裂,如何预防?你能解决吗?(看这篇就够了)

3.4 热点探测

热点探测是 可以帮助我们识别和处理那些突然变得非常热门的键(HotKey),这些键可能会对系统的性能和稳定性造成影响。

京东开源的JD-hotkey框架可以毫秒级探测热点数据,并将这些数据推送至服务器集群内存中,从而降低热数据对数据层的查询压力。

该框架适用于多种场景,如MySQL热数据本地缓存、Redis热数据本地缓存、黑名单用户本地缓存、爬虫用户限流等

3.5 本地缓存的预加载

本地缓存的预加载是一种优化策略,提前加载可能会用到的热点数据,以便在需要时能够快速访问。

4 HotKey的探测方案

监测 热点Key可以通过以下几种方法:

在这里插入图片描述

4.1 代理端探测:

如果系统中使用了Redis代理(如Twemproxy、Codis 等),可以在代理层添加统计功能,

对经过代理的Redis请求进行统计,记录每个Key的访问频率。

4.2 服务端探测:

可以实时监控Redis服务器执行一些相关命令(如MONITOR、redis-cli --hotkeys等),通过分析这些命令,可以观察到哪些Key被频繁访问,识别出热点Key。

4.3 客户端探测:

在Redis客户端(如Jedis、Lettuce)中添加统计代码,对每次对Redis的访问进行记录。可以统计每个Key的访问次数,并定期上报到监控系统。

4.4 凭经验判断:

根据经验判断哪些数据是访问频率最高的。

例如,电商系统中的商品详情页、社交平台上的热门帖子等数据通常容易成为热点Key。

5 服务端探测 HotKey:

可以实时监控Redis服务器执行一些相关命令(如MONITOR、redis-cli --hotkeys等),通过分析这些命令,可以观察到哪些Key被频繁访问,识别出热点Key。

第一大命令 MONITOR命令:

可以实时监控Redis服务器执行的所有命令。通过分析这些命令,可以观察到哪些Key被频繁访问,识别出热点Key。

MONITOR命令还可以 结合 第三方监控工具(如RedisInsight)可以实时监控Redis实例的性能数据,并通过设置监控指标和警报,自动检测出访问频率异常高的Key。

第二大命令 redis-cli --hotkeys命令:

在Redis 4.0及以上版本中,redis-cli提供了--hotkeys选项,可以帮助分析哪些Key是热点Key。

5.1 通过 MONITOR命令在服务端探测 HotKey:

MONITOR命令 可以实时监控Redis服务器执行的所有命令。

通过分析这些命令,可以观察到哪些Key被频繁访问,识别出热点Key。

不过,MONITOR命令的开销较大,所以,MONITOR命令 一般只在调试阶段使用。

要使用MONITOR命令监控hotkey,首先需要了解MONITOR命令的基本用途和使用方法。

5.1.1 MONITOR命令语法

到底,什么是MONITOR命令 ?

redis MONITOR命令 基本语法如下:

redis 127.0.0.1:6379> MONITOR 

Redis MONITOR命令用于实时打印出 Redis 服务器接收到的命令 。MONITOR 用来帮助我们知道数库正在做什么。 可以通过 redis-clitelnet 调用MONITOR 。

当 Redis 用做数据库或者分布式缓存时,MONITOR 可以帮助我们发现程序中的 bug 。

$ redis-cli monitor
1339518083.107412 [0 127.0.0.1:60866] "keys" "*"
1339518087.877697 [0 127.0.0.1:60866] "dbsize"
1339518090.420270 [0 127.0.0.1:60866] "set" "x" "6"
1339518096.506257 [0 127.0.0.1:60866] "get" "x"
1339518099.363765 [0 127.0.0.1:60866] "del" "x"
1339518100.544926 [0 127.0.0.1:60866] "get" "x"

通过 redis-cli 运行 MONITOR 时,可以发送 SIGINT (Ctrl+C) 信号来停止退出。

$ telnet localhost 6379
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
MONITOR
+OK
+1339518083.107412 [0 127.0.0.1:60866] "keys" "*"
+1339518087.877697 [0 127.0.0.1:60866] "dbsize"
+1339518090.420270 [0 127.0.0.1:60866] "set" "x" "6"
+1339518096.506257 [0 127.0.0.1:60866] "get" "x"
+1339518099.363765 [0 127.0.0.1:60866] "del" "x"
+1339518100.544926 [0 127.0.0.1:60866] "get" "x"
QUIT
+OK
Connection closed by foreign host.

通过 telnet 运行MONITOR时,可以发送 QUIT来停止退出。

5.1.2 使用MONITOR命令监控hotkey

MONITOR命令主要用于实时打印出Redis服务器接收到的命令,这包括所有对Redis数据库的操作,如读取和写入操作。

通过监控这些操作,我们可以分析出哪些键(key)被频繁访问,从而识别出hotkey。

以下是使用MONITOR命令监控hotkey的步骤:

  1. 启动Redis客户端:首先,你需要连接到Redis服务器。这可以通过redis-cli命令行工具完成。

  2. 执行MONITOR命令:在Redis客户端中输入MONITOR命令。这将启动监控模式,实时输出所有接收到的命令。例如:

redis 127.0.0.1:6379> MONITOR
   OK
   1410855382.370791 [0 127.0.0.1:60581] "info"
   1410855404.062722 [0 127.0.0.1:60581] "get" "a"

这里,OK表示命令执行成功,随后的输出显示了时间戳、客户端信息和执行的命令。

  1. 分析输出:监控模式下,Redis服务器会输出所有接收到的命令。你需要分析这些输出,找出被频繁访问的键。这些频繁访问的键可能就是hotkey。

  2. 停止监控:当你完成监控任务后,可以通过发送SIGINT信号(通常是Ctrl+C)来停止监控,或者在客户端输入QUIT命令退出监控模式。

需要注意的是,MONITOR命令虽然非常有用,但它会对Redis服务器的性能产生一定影响。

因为MONITOR需要记录并输出所有命令,这会占用CPU资源和网络带宽。在高负载环境中,使用MONITOR命令可能会导致吞吐量显著下降。因此,在生产环境中应谨慎使用,并尽量缩短监控时间。

顺便说一下: 和monitor命令一起,Redis 提供了一组内置的命令来获取 服务器 的状态信息,这些命令可以用来进行基本的监控。

  • INFO:提供关于 Redis 服务器的各种信息,包括 客户端 连接数、内存使用情况、持久化状态等。

    INFO all
    

    你也可以指定不同的部分,如 INFO clientsINFO memory

  • MONITOR:实时显示所有到达 Redis 服务器的命令。这在调试时非常有用,但在生产环境中应谨慎使用,因为它会显著影响性能。

    MONITOR
    
  • SLOWLOG:记录执行时间超过配置阈值的命令。这对于识别慢查询非常有用。

    SLOWLOG GET
    

使用SLOWLOG 命令可以读取或重置 Redis 慢速查询日志。

通俗讲就是 redis 可以把执行时间超过我们设定值的命令记录下来,slowlog 是记录到内存中的哦,所以非常快。

这里的执行时间不包括 I/O 操作, 比如与客户端, 发送应答等等 , 就是实际执行命令所需的时间(命令唯一执行的阶段,线程被阻塞且不能同时处理其他请求)。

5.1.3 通过 redis-faina 工具实现 MONITOR 的分析与定位

redis-faina 通过解析 Redis 的 MONITOR 命令输出,帮助用户对 Redis 实例进行性能诊断。

Facebook Instagram 开源的 redis-faina(Python),提供了对 Monitor 的一些分析与定位。

redis-faina 是由 Instagram 开发并开源的一个 Redis 查询分析工具。

这个工具使用简单,但功能强大,尤其适合用于定位线上 Redis 性能问题。

安装 redis-faina

你可以通过以下命令来安装 redis-faina

git clone https://github.com/facebookarchive/redis-faina.git

然后进入下载的目录中。

redis-faina 的使用也非常简单,你可以通过以下两种方式之一来使用它:

5.1.4 通过管道从 stdin 读取 N 条命令进行分析处理:

redis-cli -p 6379 MONITOR | head -n |  redis-faina.py [options] 

这里,redis-cli 命令连接到 Redis 服务器,并执行 MONITOR 命令。

head -n 用于限制分析的命令数量,而 redis-faina.py 是分析工具的 Python 脚本。

如果你不指定 head -n 的参数,它将默认输出前 10 行。head -n 1000 显示 Redis 服务器接收到的前 1000 条命令。

[options] 表示选型,比如: --prefix-delimiter 前缀字符串

下面是一个列子

redis-cli  -p 6379  monitor | head -n  1000 | redis-faina.py 

输出如下,类似如下结果:

Overall Stats
========================================
Lines Processed      7904
Commands/Sec         256.80

Top Prefixes
========================================
n/a

Top Keys
========================================
970125      3952    (50.00%)
aaa850      2       (0.03%)
aaa919      2       (0.03%)
aaa852      2       (0.03%)
aaa853      2       (0.03%)
aaa678      2       (0.03%)
aaa679      2       (0.03%)
aaa856      2       (0.03%)

Top Commands
========================================
AUTH      3952    (50.00%)
set       3950    (49.97%)

Command Time (microsecs)
========================================
Median      1920.0
75%     2016.25
90%         2092.0
99%         2840.0

Heaviest Commands (microsecs)
========================================
AUTH      22542297.5
set       8236623.75

Slowest Calls
========================================
20718202.0      "AUTH" "970125"
8456.0          "set" "aaa82" "aaaaaaaa82"
6624.0          "set" "aaa103" "aaaaaaaa103"
6506.0          "set" "aaa817" "aaaaaaaa817"
6105.0          "set" "aaa2024" "aaaaaaaa2024"
6081.0          "set" "aaa1057" "aaaaaaaa1057"
6074.75         "set" "aaa1948" "aaaaaaaa1948"
6067.0          "set" "aaa576" "aaaaaaaa576"


从这个结果中,我们可以清晰的看到各种指标。

5.1.5 直接从文件中读取 N 条命令进行分析处理:

redis-cli -h xx.xx.xx.xx -p 6379 -a pwd monitor | head -n > /tmp/outfile.txt
redis-faina.py /tmp/outfile.txt

首先,将 MONITOR 命令的输出重定向到一个文件中,然后使用 redis-faina.py 脚本分析该文件。

redis-faina 会输出包括总命令数、每秒命令数(QPS)、最频繁使用的前缀、最频繁使用的键、最频繁执行的命令、命令执行时间的统计分布、最耗时的命令以及最慢的调用等统计信息。这些信息对于分析 Redis 性能和识别热点键(hotkey)非常有用。

需要注意的是,由于 redis MONITOR 输出的只有请求开始的时间,所以在一个非常繁忙的 Redis 实例中,根据该请求的开始时间以及下一个请求的开始时间,可以大概估算出一个请求的执行时间。

由此可以看出,redis-faina 统计的时间并不是十分精确的,尤其在分析一个非常闲的 Redis 实例时,分析的结果可能差异较大

在这里插入图片描述

5.1.6 MONITOR 的消耗

因为MONITOR流返回所有命令,所以用起来会有一定的消耗。

下面是一个基准测试对比:

不带MONITOR命令:

$ src/redis-benchmark -c 10 -n 100000 -q
PING_INLINE: 101936.80 requests per second
PING_BULK: 102880.66 requests per second
SET: 95419.85 requests per second
GET: 104275.29 requests per second
INCR: 93283.58 requests per second

带 MONITOR 命令 (redis-cli monitor > /dev/null):

$ src/redis-benchmark -c 10 -n 100000 -q
PING_INLINE: 58479.53 requests per second
PING_BULK: 59136.61 requests per second
SET: 41823.50 requests per second
GET: 45330.91 requests per second
INCR: 41771.09 requests per second

通过上面的例子可以看到运行一个MONITOR命令降低了超过 50% 的吞吐量。

运行多个MONITOR会进一步降低性能。

MONITOR 不记录的命令

处于安全方面的考虑,所有的管理相关的命令不会记录到MONITOR的输出者。

下面几个命令也不会记录:

5.2 通过 Redis的 hotkey 分析工具在服务端探测 HotKey:

Redis自带的分析工具:在Redis 4.0及以上版本中,redis-cli提供了--hotkeys选项,可以帮助分析哪些Key是热点Key。

5.2.1 redis-cli提供了--hotkeys的用法

redis-cli --hotkeys 是一个Redis命令行工具的选项, 可以监视和查看当前活动的热点键(hot keys)。

下面是使用 redis-cli --hotkeys 的示例:

  1. 打开终端或命令提示符。
  2. 输入以下命令以启动 redis-cli 并使用 --hotkeys 选项:
redis-cli --hotkeys

执行上述命令后,redis-cli 将连接到本地Redis服务器(默认端口为 6379)。

在连接成功后,它将显示当前活动的热点键及其相关信息,例如键的名称、执行命令的类型和执行的次数。

$./redis-cli --hotkeys

# Scanning the entire keyspace to find hot keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Hot key 'counter:000000000002' found so far with counter 87
[00.00%] Hot key 'key:000000000001' found so far with counter 254
[00.00%] Hot key 'mylist' found so far with counter 107
[00.00%] Hot key 'key:000000000000' found so far with counter 254
[45.45%] Hot key 'counter:000000000001' found so far with counter 87
[45.45%] Hot key 'key:000000000002' found so far with counter 254
[45.45%] Hot key 'myset' found so far with counter 64
[45.45%] Hot key 'counter:000000000000' found so far with counter 93

-------- summary -------

Sampled 22 keys in the keyspace!
hot key found with counter: 254    keyname: key:000000000001
hot key found with counter: 254    keyname: key:000000000000
hot key found with counter: 254    keyname: key:000000000002
hot key found with counter: 107    keyname: mylist
hot key found with counter: 93    keyname: counter:000000000000
hot key found with counter: 87    keyname: counter:000000000002
hot key found with counter: 87    keyname: counter:000000000001
hot key found with counter: 64    keyname: myset

可以看到,排在前几位的即是热点key。

请注意,redis-cli --hotkeys 选项仅适用于Redis 6.2版本或更高版本。

如果使用的是早期版本的Redis,该选项可能不可用。

此外,请确保在执行 redis-cli --hotkeys 命令之前已经安装和正确配置了Redis,并且Redis服务器正在运行。

5.2.2 redis-cli --hotkeys 的缺点

  • 性能影响:由于它是一个全量的Hotkey数据,特别是存在大量hotkey的场景下会对性能产生较大影响,因此不推荐在生产环境频繁执行;
  • 局限性:该命令返回的结果是基于Redis自身内部的采样与统计算法,根据机器资源的或预期场景的不同,该结果可能并不是100%符合预期的;
  • 完整性:该命令只提供了热点键的基本信息,无法知道更详细的统计和分析信息,需要向业务侧确认;

6 代理端探测探测 HotKey:

如果系统中使用了Redis代理(如Twemproxy、Codis 等),可以在代理层添加统计功能,

在代理层,如Twemproxy或Codis,增加key使用情况的收集。对经过 proxy 代理的Redis请求进行统计,记录每个Key的访问频率。

这种方法适用于代理架构,如果使用的redis cluster 集群架构,这种方式可以忽略。

在这里插入图片描述

7 在redis client 客户端探测 HotKey

在Redis客户端(如Jedis、Lettuce)中添加统计代码,对每次对Redis的访问进行记录。

在Redis客户端可以统计每个Key的访问次数,并定期上报到监控系统。

在这里插入图片描述

7.1 大厂方案1: 透明多级缓存 (TMC) 架构设计

在这里插入图片描述

7.1.1 透明多级缓存 (TMC)简介

TMC ,即“透明多级缓存( Transparent Multilevel Cache )”,是有赞 PaaS 团队给公司内应用提供的整体缓存解决方案。

TMC 在通用“分布式缓存解决方案(本地缓存+ 如 Codis Proxy + Redis)”基础上,增加了以下功能:

  • 应用层热点探测
  • 应用层本地缓存
  • 应用层缓存命中统计

以帮助应用层解决缓存使用过程中出现的热点访问问题。

有赞 为什么要做 TMC

使用有赞服务的电商商家数量和类型很多,商家会不定期做一些“商品秒杀”、“商品推广”活动,导致“营销活动”、“商品详情”、“交易下单”等链路应用出现 缓存热点访问 的情况:

  • 活动时间、活动类型、活动商品之类的信息不可预期,导致 缓存热点访问 情况不可提前预知;
  • 缓存热点访问 出现期间,应用层少数 热点访问 key 产生大量缓存访问请求:冲击分布式缓存系统,大量占据内网带宽,最终影响应用层系统稳定性;

为了应对以上问题,需要一个能够 自动发现热点将热点缓存访问请求前置在应用层本地缓存 的解决方案,这就是 TMC 产生的原因。

多级缓存解决方案的痛点

基于上述描述,我们总结了下列 多级缓存解决方案 需要解决的需求痛点:

  • 热点探测:如何快速且准确的发现 热点访问 key
  • 数据一致性:前置在应用层的本地缓存,如何保障与分布式缓存系统的数据一致性?
  • 效果验证:如何让应用层查看本地缓存命中率、热点 key 等数据,验证多级缓存效果?
  • 透明接入:整体解决方案如何减少对应用系统的入侵,做到快速平滑接入?

TMC 聚焦上述痛点,设计并实现了整体解决方案。

以支持“热点探测”和“本地缓存”,减少热点访问时对下游分布式缓存服务的冲击,避免影响应用服务的性能及稳定性。

7.1.2 透明多级缓存 (TMC)整体架构

在这里插入图片描述

TMC 整体架构如上图,共分为三层:

  • 存储层:提供基础的kv数据存储能力,针对不同的业务场景选用不同的存储服务( codis / zankv / aerospike );
  • 代理层:为应用层提供统一的缓存使用入口及通信协议,承担分布式数据水平切分后的路由功能转发工作;
  • 应用层:提供统一客户端给应用服务使用,内置“热点探测”、“本地缓存”等功能,对业务透明;

7.1.3 透明多级缓存 (TMC)本地SDK缓存

如何实现透明 ?

(TMC)本地SDK 是如何减少对业务应用系统的入侵,做到透明接入的?

对于公司 Java 应用服务,在缓存客户端使用方式上分为两类:

  • 基于spring.data.redis包,使用RedisTemplate编写业务代码;
  • 基于youzan.framework.redis包,使用RedisClient编写业务代码;

不论使用以上那种方式,最终通过JedisPool创建的Jedis对象与缓存服务端代理层做请求交互。

在这里插入图片描述

TMC 对原生jedis包的JedisPoolJedis类做了改造,

在JedisPool初始化过程中, 集成TMC“热点发现”+“本地缓存”功能 Hermes-SDK 包的初始化逻辑,

使Jedis客户端与缓存服务端代理层交互时, 先与Hermes-SDK交互,从而完成 “热点探测”+“本地缓存”功能的透明接入。

对于 Java 应用服务,只需使用特定版本的 jedis-jar 包,无需修改代码,即可接入 TMC 使用“热点发现”+“本地缓存”功能,做到了对应用系统的最小入侵。

7.1.4 透明多级缓存 (TMC)本地SDK缓存 整体结构

在这里插入图片描述

7.1.5 透明多级缓存 (TMC)本地SDK 模块划分

TMC 本地缓存整体结构分为如下模块:

  • Jedis-Client: Java 应用与缓存服务端交互的直接入口,接口定义与原生 Jedis-Client 无异;
  • Hermes-SDK:自研“热点发现+本地缓存”功能的SDK封装, Jedis-Client 通过与它交互来集成相应能力;
  • Hermes服务端集群:接收 Hermes-SDK 上报的缓存访问数据,进行热点探测,将热点 key 推送给 Hermes-SDK 做本地缓存;
  • 缓存集群:由代理层和存储层组成,为应用客户端提供统一的分布式缓存服务入口;
  • 基础组件: etcd 集群、 Apollo 配置中心,为 TMC 提供“集群推送”和“统一配置”能力;

7.1.6 透明多级缓存 (TMC)本地SDK 基本流程

1) key 值获取

  • Java 应用调用 Jedis-Client 接口获取key的缓存值时,Jedis-Client 会询问 Hermes-SDK 该 key 当前是否是 热点key;
  • 对于 热点key ,直接从 Hermes-SDK 的 热点模块 获取热点 key 在本地缓存的 value 值,不去访问 缓存集群 ,从而将访问请求前置在应用层;
  • 对于非 热点key ,Hermes-SDK 会通过Callable回调 Jedis-Client 的原生接口,从 缓存集群 拿到 value 值;
  • 对于 Jedis-Client 的每次 key 值访问请求,Hermes-SDK 都会通过其 通信模块 将 key访问事件 异步上报给 Hermes服务端集群 ,以便其根据上报数据进行“热点探测”;

2)key值过期

  • Java 应用调用 Jedis-Clientset() del() expire()接口时会导致对应 key 值失效,Jedis-Client 会同步调用 Hermes-SDKinvalid()方法告知其“ key 值失效”事件;
  • 对于 热点keyHermes-SDK 的 热点模块 会先将 key 在本地缓存的 value 值失效,以达到本地数据强一致。同时 通信模块 会异步将“ key 值失效”事件通过 etcd集群 推送给 Java 应用集群中其他 Hermes-SDK 节点;
  • 其他Hermes-SDK节点的 通信模块 收到 “ key 值失效”事件后,会调用 热点模块 将 key 在本地缓存的 value 值失效,以达到集群数据最终一致

3)热点发现

  • Hermes服务端集群 不断收集 Hermes-SDK上报的 key访问事件,对不同业务应用集群的缓存访问数据进行周期性(3s一次)分析计算,以探测业务应用集群中的热点key列表;
  • 本地预热:对于探测到的热点key列表,Hermes服务端集群 将其通过 etcd集群 推送给不同业务应用集群的 Hermes-SDK 通信模块 ,通知其对热点key列表进行本地缓存 预热;

4)配置读取

  • Hermes-SDK 在启动及运行过程中,会从 Apollo配置中心 读取其关心的配置信息(如:启动关闭配置、黑白名单配置、etcd地址...);
  • Hermes服务端集群 在启动及运行过程中,会从 Apollo配置中心 读取其关心的配置信息(如:业务应用列表、热点阈值配置、 etcd 地址...);

7.1.7 透明多级缓存 (TMC)本地SDK 稳定性

TMC本地缓存稳定性表现在以下方面:

  • 数据上报异步化:Hermes-SDK 使用rsyslog技术对“ key 访问事件”进行异步化上报,不会阻塞业务;
  • 通信模块线程隔离Hermes-SDK 的 通信模块 使用独立线程池+有界队列,保证事件上报&监听的I/O操作与业务执行线程隔离,即使出现非预期性异常也不会影响基本业务功能;
  • 缓存管控:Hermes-SDK 的 热点模块 对本地缓存大小上限进行了管控,使其占用内存不超过 64MB(LRU),杜绝 JVM 堆内存溢出的可能;

7.1.8 透明多级缓存 (TMC)本地SDK 一致性

TMC 本地缓存一致性表现在以下方面:

  • Hermes-SDK 的 热点模块 仅缓存 热点key 数据,绝大多数非热点 key 数据由 缓存集群 存储;
  • 热点key 变更导致 value 失效时,Hermes-SDK 同步失效本地缓存,保证 本地强一致;
  • 热点key 变更导致 value 失效时,Hermes-SDK 通过 etcd集群 广播事件,异步失效业务应用集群中其他节点的本地缓存,保证 集群最终一致;

7.1.9 TMC热点探测流程

TMC热点探测 整体流程

在这里插入图片描述

TMC 热点发现流程分为四步:

  • 数据收集:收集 Hermes-SDK 上报的 key访问事件
  • 热度滑窗:对 App 的每个 Key ,维护一个时间轮,记录基于当前时刻滑窗的访问热度;
  • 热度汇聚:对 App 的所有 Key ,以<key,热度>的形式进行 热度排序汇总
  • 热点探测:对 App ,从 热Key排序汇总 结果中选出 TopN的热点Key ,推送给 Hermes-SDK

7.1.9.1 数据收集

Hermes-SDK 通过本地rsyslogkey访问事件 以协议格式放入 kafkaHermes服务端集群 的每个节点消费 kafka 消息,实时获取 key访问事件

访问事件协议格式如下:

  • appName:集群节点所属业务应用
  • uniqueKey:业务应用 key访问事件 的 key
  • sendTime:业务应用 key访问事件 的发生时间
  • weight:业务应用 key访问事件 的访问权值

Hermes服务端集群 节点将收集到的 key访问事件 存储在本地内存中,

内存数据结构为Map<String, Map<String, LongAdder>>

对应业务含义映射为Map< appName , Map< uniqueKey , 热度 >>

7.1.9.2 热度滑窗

在这里插入图片描述

1: 时间滑窗

Hermes服务端集群 节点,对每个App的每个 key ,维护了一个 时间轮

  • 时间轮中共10个 时间片,每个时间片记录当前 key 对应 3 秒时间周期的总访问次数;
  • 时间轮10个时间片的记录累加即表示当前 key 从当前时间向前 30 秒时间窗口内的总访问次数;
2. 映射任务

Hermes服务端集群 节点,对每个 App 每3秒 生成一个 映射任务 ,交由节点内 “缓存映射线程池” 执行。

映射任务 内容如下:

  • 对当前 App ,从Map< appName , Map< uniqueKey , 热度 >>中取出 appName 对应的Map Map< uniqueKey , 热度 >>
  • 遍历Map< uniqueKey , 热度 >>中的 key ,对每个 key 取出其热度存入其 时间轮 对应的时间片中;

7.1.9.3 热度汇聚

在这里插入图片描述

完成第二步“热度滑窗”后,映射任务 继续对当前 App 进行“热度汇聚”工作:

  • 遍历 App 的 key ,将每个 key 的 时间轮 热度进行汇总(即30秒时间窗口内总热度)得到探测时刻 滑窗总热度
  • < key , 滑窗总热度 > 以排序集合的方式存入 Redis存储服务 中,即 热度汇聚结果

7.1.9.4 热点探测

  • 在前几步,每3秒 一次的 映射任务 执行,对每个 App 都会产生一份当前时刻的 热度汇聚结果
  • Hermes服务端集群 中的“热点探测”节点,对每个 App ,只需周期性从其最近一份 热度汇聚结果 中取出达到热度阈值的 TopN 的 key 列表,即可得到本次探测的 热点key列表

TMC 热点发现整体流程如下图:

在这里插入图片描述

7.1.10 TMC热点探测 特性总结

1. 实时性

Hermes-SDK基于rsyslog + kafka 实时上报 key访问事件。

映射任务 3秒一个周期完成“热度滑窗” + “热度汇聚”工作,当有 热点访问场景 出现时最长3秒即可探测出对应 热点key。

2. 准确性

key 的 热度汇聚结果 由“基于时间轮实现的滑动窗口”汇聚得到,相对准确地反应当前及最近正在发生访问分布。

3.扩展性

Hermes服务端集群 节点无状态,节点数可基于 kafka 的 partition 数量横向扩展。

“热度滑窗” + “热度汇聚” 过程基于 App 数量,在单节点内多线程扩展。

7.1.11 TMC实战效果

7.1.11.1 快手商家某次商品营销活动

有赞商家通过快手直播平台为某商品搞活动,造成该商品短时间内被集中访问产生访问热点,

活动期间 TMC 记录的实际热点访问效果数据如下:

图. 某核心应用的缓存请求&命中率曲线图

在这里插入图片描述

  • 上图蓝线为应用集群调用get()方法访问缓存次数
  • 上图绿线为获取缓存操作命中 TMC 本地缓存的次数

在这里插入图片描述

  • 上图为本地缓存命中率曲线图

可以看出活动期间缓存请求量及本地缓存命中量均有明显增长,本地缓存命中率达到近 80% (即应用集群中 80% 的缓存查询请求被 TMC 本地缓存拦截)。

7.1.11.2 热点缓存对应用访问的加速效果

在这里插入图片描述

  • 上图为应用接口QPS曲线

在这里插入图片描述

  • 上图为应用接口RT曲线

可以看出活动期间应用接口的请求量有明显增长,由于 TMC 本地缓存的效果应用接口的 RT 反而出现下降。

7.1.11.3 双十一期间部分应用 TMC 效果展示

1. 商品域核心应用效果

在这里插入图片描述

5-2-2. 活动域核心应用效果

在这里插入图片描述

图片描述

7.1.12 TMC功能的总结

在有赞, TMC 目前已为商品中心、物流中心、库存中心、营销活动、用户中心、网关&消息等多个核心应用模块提供服务,后续应用也在陆续接入中。

TMC 在提供“热点探测” + “本地缓存”的核心能力同时,也为应用服务提供了灵活的配置选择,应用服务可以结合实际业务情况在“热点阈值”、“热点key探测数量”、“热点黑白名单”维度进行自由配置以达到更好的使用效果。

配合三级缓存的使用,需要进行 热key的 探测,有赞平台通过 热key的探测和 支持,

其中:活动期间,本地缓存命中率达到近 80%的命中率, 并且, 响应时间,和平峰时段,没有变化。

Canal使用场景2: 三级缓存的 数据一致性 通知

在100W qps 三级缓存组件 的架构中,也需要通过 Canal 进行 binlog 的 订阅, 进行无入侵的 缓存数据维护

7.2 大厂方案2: 结合开源hotkey,做热点探测和预加载

基于开源hotkey进行 热点探测,有很多小伙伴,在生产系统进行了 缓存系统的重构, 整体的架构图如下:

在这里插入图片描述

没有hotkey探测和有 hotkey探测的缓存组件做对比,来说明这套机制的优缺点。

特性 没有hotkey探测和预加载的缓存 使用hotkey探测和预加载的缓存
机器资源 高配物理机/虚拟机 普通物理机/虚拟机/容器
管控复杂 无法控制热点,不易监控 热点数据可以监控统计,可以手动刷新
资源利用率 资源利用率低,无论是否是热点数据都占用资源 资源利用率高,大部分热点数据持有资源
突发流量 无法弹性应对突发流量 弹性应对突发流量
预发流量 预设所有数据 只提前预设热点数据
数据一致性 集群内数据不一致情况时常发生,出现“横跳”现象 集群内数据一致性高,极少或不发生不一致性情况

以上内容的视频介绍,在尼恩的 《第26章 百万qps 三级缓存 组件实操》视频 中 有介绍,具体参见视频。

在这里插入图片描述

这里涉及的内容很复杂,也很重要,具体请参见视频,这里不做赘述。

说在最后:有问题找老架构取经‍

Redis hotkey 问题,按照尼恩的梳理,进行 深度回答,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

和前文提到的小伙伴一样,回答到这个水平,阿里P7 offer到手,年薪 60W,还特意来感谢了尼恩。

在这里插入图片描述

在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。

很多小伙伴刷完后, 吊打面试官, 大厂横着走。

在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。

另外,如果没有面试机会, 可以找尼恩来改简历、做帮扶。前段时间,刚指导一个27岁 被裁小伙,拿到了一个年薪45W的JD +PDD offer,逆天改命

狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。

技术自由的实现路径:

实现你的 架构自由:

吃透8图1模板,人人可以做架构

10Wqps评论中台,如何架构?B站是这么做的!!!

阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了

峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?

100亿级订单怎么调度,来一个大厂的极品方案

2个大厂 100亿级 超大流量 红包 架构方案

… 更多架构文章,正在添加中

实现你的 响应式 自由:

响应式圣经:10W字,实现Spring响应式编程自由

这是老版本 《Flux、Mono、Reactor 实战(史上最全)

实现你的 spring cloud 自由:

Spring cloud Alibaba 学习圣经》 PDF

分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)

一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)

实现你的 linux 自由:

Linux命令大全:2W多字,一次实现Linux自由

实现你的 网络 自由:

TCP协议详解 (史上最全)

网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!

实现你的 分布式锁 自由:

Redis分布式锁(图解 - 秒懂 - 史上最全)

Zookeeper 分布式锁 - 图解 - 秒懂

实现你的 王者组件 自由:

队列之王: Disruptor 原理、架构、源码 一文穿透

缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)

缓存之王:Caffeine 的使用(史上最全)

Java Agent 探针、字节码增强 ByteBuddy(史上最全)

实现你的 面试题 自由:

4800页《尼恩Java面试宝典 》 40个专题

免费获取11个技术圣经PDF:

标签:60W,缓存,Key,Redis,redis,key,热点,MONITOR
From: https://www.cnblogs.com/crazymakercircle/p/18469232

相关文章

  • 【推荐 - 源码安装】Redis - 安装
    准备查看操作系统位数[root@lab10~]#getconfLONG_BIT64查看gcc编译环境[root@lab10~]#gcc-vUsingbuilt-inspecs.COLLECT_GCC=gccCOLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapperTarget:x86_64-redhat-linuxConfiguredwith:......
  • docker-compose安装mysql/redis/nacos环境
    dockerdocker-compose安装查看上一篇文章1.新建目录并创建docker-compose.yaml文件文件内容services:mysql:image:mysql:8.2.0container_name:mysqlenvironment:MYSQL_ROOT_PASSWORD:1qaz@WSXMYSQL_DATABASE:nacosMYSQL_USER:......
  • OpenCV高级图形用户界面(11)检查是否有键盘事件发生而不阻塞当前线程函数pollKey()的
    操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:VisualStudioCode编程语言:C++11算法描述轮询已按下的键。函数pollKey无等待地轮询键盘事件。它返回已按下的键的代码或如果没有键自上次调用以来被按下则返回-1。若要等待按键被按下,请使用waitKey。注意waitKey......
  • Redis如何实现高性能和高可用
    目录第一章Redis高性能和高可用概述1.1Redis简介1.1.1Redis基本概念1.1.2Redis特点1.1.3Redis应用场景1.2Redis高性能原理1.2.1内存数据结构优化1.2.2单线程模型与I/O多路复用1.2.3多线程异步I/O1.2.4数据持久化技术1.3Redis高可用架构1.3.1主从复制......
  • 【油猴脚本】00027 案例 Tampermonkey油猴脚本, 仅用于学习,不要乱搞。添加标题为网页数
    前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦......
  • Scala连接数据库(mysql,redis)
    1、Scala连接mysql数据库:importjava.sql.{Connection,DriverManager,PreparedStatement,ResultSet}objectmysqlConn{defmain(args:Array[String]):Unit={Class.forName("com.mysql.jdbc.Driver")valconn:Connection=DriverManager.getCon......
  • 【Azure Cloud Service】使用Key Vault Secret添加.CER证书到Cloud Service Extended
    问题描述因为KeyVault的证书上传功能中,只支持pfx格式的证书,而中间证书,根证书不能转换为pfx格式,只能是公钥证书格式cet或者crt,能通过文本工具直接查看base64编码内容。如一个证书链文件中可以看见中间证书,根证书: 当把包含完成证书链的证书PFX上传到KeyVaultcertificate......
  • redis 常用指令
    原文链接:redis常用指令–每天进步一点点(longkui.site)1.登录相关redis-cli-h地址-p端口比如:redis-cli-h127.0.0.1-p9379windows下直接执行redis-cli.exe就行。linux下直接执行上面的命令如果报错:bash:redis-cli:commandnotfound…首先你要确定你安装了r......
  • 面试题:Redis(五)
    1.面试题面试问记录对集合中的数据进行统计 在移动应用中,需要统计每天的新增用户数和第2天的留存用户数;在电商网站的商品评论中,需要统计评论列表中的最新评论;在签到打卡中,需要统计一个月内连续打卡的用户数;在网页访问记录中,需要统计独立访客(UniqueVisitor,UV)量。......
  • 使用 Lua 脚本批量获取制定目录下的所有 Redis 键的值
    /***使用Lua脚本批量获取Redis键的值**@paramprefixRedis中的键列表*@return键对应的值的列表*/public<E>List<E>getAllListValuesByPrefix(Stringprefix,Class<?>classType){//获取所有以给定前缀开头的键Set<String>keys=redisTempl......