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

文摘   科技   2024-10-15 22:08   湖北  
FSAC未来超级架构师

架构师总动员
实现架构转型,再无中年危机


尼恩说在前面

在40岁老架构师 尼恩的100+读者交流群 中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
  • 什么是热点key? 热点key会带来哪些问题?
  • 如何监测热点key?如何解决热点key?
  • 你们生产遇到过 hotkey吗? 怎么解决的?
最近有小伙伴在面试 阿里P7,又遇到了相关的面试题。小伙伴是按照尼恩的方案去答的。
没想到,面试官非常满意,小伙伴阿里P7 offer到手,年薪 60W,还特意来感谢了尼恩。
所以,尼恩给大家做一下系统化、体系化的梳理。
这里,把这个答案共享出来,帮大家内力猛增,让面试官爱到 “不能自已、口水直流”。
然后,帮大家 实现”offer直提”,都拿到阿里P7 offer,逆天改命。
当然,这道面试题 以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书

本文目录

尼恩说在前面:

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

 - 什么是HotKey?

2  热点Key的巨大危害

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

 - 2.2 HotKey带来的常见问题

 - 2.3 热点Key的巨大危害

3 HotKey的解决方案

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

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

 - 3.3 Redis 从节点扩容

 - 3.4 热点探测

 - 3.5 本地缓存的预加载

4 HotKey的探测方案

 - 4.1 代理端探测:

 - 4.2 服务端探测:

 - 4.3 客户端探测:

 - 4.4 凭经验判断: 

5 服务端探测  HotKey:

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

 - 5.1.1 MONITOR命令语法

 - 5.1.2 使用`MONITOR`命令监控hotkey

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

    -安装 `redis-faina`

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

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

 - 5.1.6 MONITOR 的消耗

 - MONITOR 不记录的命令

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

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

 - 5.2.2 redis-cli --hotkeys 的缺点

6 代理端探测探测  HotKey:

7 在redis client 客户端探测 HotKey 

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

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

    -有赞 为什么要做 TMC
    -多级缓存解决方案的痛点

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

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

    -如何实现透明 ?

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

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

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

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

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

 - 7.1.9   TMC热点探测流程

    -TMC热点探测 整体流程

 - 7.1.9.1  数据收集

 - 7.1.9.2 热度滑窗

    -1:  时间滑窗
    -2. 映射任务

 - 7.1.9.3 热度汇聚

 - 7.1.9.4 热点探测

 - 7.1.10 TMC热点探测 特性总结

    -1. 实时性
    -2. 准确性
    -3.扩展性

 - 7.1.11 TMC实战效果

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

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

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

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

 - 7.1.12 TMC功能的总结

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

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


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-cli 和 telnet 调用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`表示命令执行成功,随后的输出显示了时间戳、客户端信息和执行的命令。3. 分析输出:监控模式下,Redis服务器会输出所有接收到的命令。
接下来,需要分析这些输出,找出被频繁访问的键。这些频繁访问的键可能就是hotkey。
4. 停止监控:当你完成监控任务后,可以通过发送`SIGINT`信号(通常是Ctrl+C)来停止监控,或者在客户端输入`QUIT`命令退出监控模式。

需要注意的是,`MONITOR`命令虽然非常有用,但它会对Redis服务器的性能产生一定影响。
因为MONITOR需要记录并输出所有命令,这会占用CPU资源和网络带宽。在高负载环境中,使用`MONITOR`命令可能会导致吞吐量显著下降。因此,在生产环境中应谨慎使用,并尽量缩短监控时间。

顺便说一下: 和monitor命令一起,Redis 提供了一组内置的命令来获取 服务器 的状态信息,这些命令可以用来进行基本的监控。
   INFO:提供关于 Redis 服务器的各种信息,包括 客户端 连接数、内存使用情况、持久化状态等。

INFO all
你也可以指定不同的部分,如 INFO clients 或 INFO 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的输出者。
下面几个命令也不会记录:
  • AUTH
  • EXEC
  • HELLO
  • QUIT

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-Client 的set() del() expire()接口时会导致对应 key 值失效,Jedis-Client 会同步调用 Hermes-SDK 的invalid()方法告知其“ key 值失效”事件;
  • 对于 热点key ,Hermes-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 通过本地rsyslog将 key访问事件 以协议格式放入 kafka ,Hermes服务端集群 的每个节点消费 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自由” 。


被裁之后, 空窗1年/空窗2年, 如何  起死回生  ? 


案例1:42岁被裁2年,天快塌了,急救1个月,拿到开发经理offer,起死回生


案例2:35岁被裁6个月, 职业绝望,转架构急救上岸,DDD和3高项目太重要了

案例3:失业15个月,学习40天拿offer, 绝境翻盘,如何实现?


 被裁之后,100W 年薪 到手, 如何 人生逆袭? 


100W案例,100W年薪的底层逻辑是什么? 如何实现年薪百万? 如何远离  中年危机?

100W案例240岁小伙被裁6个月,猛卷3月拿100W年薪 ,秘诀:首席架构/总架构

环境太糟,如何升 P8级,年入100W?

如何  逆天改命,包含AI、大数据、golang、Java  等      


职业救助站

实现职业转型,极速上岸


关注职业救助站公众号,获取每天职业干货
助您实现职业转型、职业升级、极速上岸
---------------------------------

技术自由圈

实现架构转型,再无中年危机


关注技术自由圈公众号,获取每天技术千货
一起成为牛逼的未来超级架构师

几十篇架构笔记、5000页面试宝典、20个技术圣经
请加尼恩个人微信 免费拿走

暗号,请在 公众号后台 发送消息:领电子书

如有收获,请点击底部的"在看"和"",谢谢

技术自由圈
疯狂创客圈(技术自由架构圈):一个 技术狂人、技术大神、高性能 发烧友 圈子。圈内一大波顶级高手、架构师、发烧友已经实现技术自由;另外一大波卷王,正在狠狠卷,奔向技术自由
 最新文章