首页 > 数据库 >Redis 热 Key 发现以及解决办法

Redis 热 Key 发现以及解决办法

时间:2023-04-21 09:56:23浏览次数:41  
标签:解决办法 缓存 bakHotKey Redis 实例 Key 请求

内容转自:https://joyspace.jd.com/sheets/YZxilLHtAc98E1k5kHDK

一、背景介绍

    最近在技术交流微信群里看大家讨论技术,其中有谈到 Redis 热 Key 的一些问题解决方案,我也仔细思考了一下我们目前系统中 Redis 的使用场景,我们是不是也存在热 Key 问题,或者说如果我们也出现了热 Key 问题会怎么去解决。

    目前我在京东做一款 APP (京东读书)的业务量不是特别大,说一个指标就可以了,DAU 在 20W 左右,熟悉这个指标的朋友马上就可以感受什么叫量不是特别大了。所以我的工作中遇到热 Key 的问题可能印象中没有过,如果硬要说出现过,也算是有过,不过那种情况不能算是简单的热 Key 问题,还有一些功能设计以及 Redis 数据结构选择方面的一些问题,这些可以后面专门总结一下,这篇还是只聊热 Key

目录

二、热 Key 带来问题

    其实,按我理解来说,没有什么所谓的热 Key 问题,所谓的热 Key 问题都是由于对某个 Key 的访问量过大所产生的一些衍生问题。

    由于某个 Key 的数据一定是存储到后端某台服务器的 Redis 单个实例上,如果对这个 Key 突然出现大量的请求操作,这样就会造成流量过于集中,达到 Redis 单个实例处理上限,可能会导致 Redis 实例 CPU 使用率 100%,或者是网卡流量达到上限等,对系统的稳定性和可用性造成影响,或者更为严重出现服务器宕机,无法对外提供服务;更有甚者在出现 Redis 服务不可用之后,大量的数据请求全部落地数据库查询上,Redis 都已经顶不住了,数据库也是分分钟挂掉的节奏,这个是所谓的热 Key 问题。

  • 流量集中,达到服务器处理上限(CPU、网络 IO 等);

  • 会影响在同一个 Redis 实例上其他 Key 的读写请求操作;

  • 热 Key 请求落到同一个 Redis 实例上,无法通过扩容解决;

  • 大量 Redis 请求失败,查询操作可能打到数据库,拖垮数据库,导致整个服务不可用。

    通过上面的描述,其实热 key 问题不仅是对某个 Key 请求流量过于集中的问题,也和服务器性能有很大的关联,如果是一个普通的 Docker 容器(4C8G4CPU8G内存,大家不要笑,我们系统好多这种规格服务器),和一台性能强劲的物理机,这两种不同的服务器规格配置对热 Key 的认定估计也不会相同,这个可能是我对热 key 问题的一点不同理解。

三、如何发现热 Key

    通过上面的分析,出现热 Key 的危害还是很大的,我们不可能等到热 Key 出现已经拖垮了服务再去处理,那个时候业务一定已经收到影响,损失也是不言而喻的;那么能够在热 Key 出现前通过一些手段提前监控到热 Key 的出现,对于保证业务系统的稳定性是非常重要的,那么我们都有哪些手段提前观测到热 Key 的出现呢?

1)凭借业务经验,预估热 Key 出现

    根据业务系统上线的一些活动和功能,我们是可以在某些场景下提前预估热 Key 的出现的,比如业务需要进行一场商品秒杀活动,秒杀商品信息和数量一般都会缓存到 Redis 中,这种场景极有可能出现热 Key 问题的。

  • 优点:简单,凭经验发现热 Key,提早发现提早处理;

  • 缺点:没有办法预测所有热 Key 出现,比如某些热点新闻事件,无法提前预测。

2)客户端进行收集

     一般我们在连接 Redis 服务器时都要使用专门的 SDK(比如:Java 的客户端工具 JedisRedisson),我们可以对客户端工具进行封装,在发送请求前进行收集采集,同时定时把收集到的数据上报到统一的服务进行聚合计算。

  • 优点:方案简单

  • 缺点:

    • 对客户端代码有一定入侵,或者需要对 SDK 工具进行二次开发;

    • 没法适应多语言架构,每一种语言的 SDK 都需要进行开发,后期开发维护成本较高。3)

3)在代理层进行收集

如果所有的 Redis 请求都经过 Proxy(代理)的话,可以考虑改动 Proxy 代码进行收集,思路与客户端基本类似。

  • 优点:对使用方完全透明,能够解决客户端 SDK 的语言异构和版本升级问题;

  • 缺点:

    • 开发成本会比客户端高些;

    • 并不是所有的 Redis 集群架构中都有 Proxy 代理(使用这种方式必须要部署 Proxy)。4)

4)使用 Redis 自带命令

hotkeys 参数

Redis 在 4.0.3 版本中添加了 hotkeys 查找特性,可以直接利用 redis-cli --hotkeys 获取当前 keyspace 的热点 key,实现上是通过 scan + object freq 完成的。

  • 优点:无需进行二次开发,能够直接利用现成的工具;

  • 缺点:

    • 由于需要扫描整个 keyspace,实时性上比较差;

    • 扫描时间与 key 的数量正相关,如果 key 的数量比较多,耗时可能会非常长。

monitor 命令

monitor 命令可以实时抓取出 Redis 服务器接收到的命令,通过 redis-cli monitor 抓取数据,同时结合一些现成的分析工具,比如 redis-faina,统计出热 Key。

  • 优点:无需进行二次开发,能够直接利用现成的工具;

  • 缺点:该命令在高并发的条件下,有内存增暴增的隐患,还会降低 Redis 的性能。

5)Redis 节点抓包分析

Redis 客户端使用 TCP 协议与服务端进行交互,通信协议采用的是 RESP 协议。自己写程序监听端口,按照 RESP 协议规则解析数据,进行分析。或者我们可以使用一些抓包工具,比如 tcpdump 工具,抓取一段时间内的流量进行解析。

  • 优点:对 SDK 或者 Proxy 代理层没有入侵;

  • 缺点:

    • 有一定的开发成本;

    • 热 Key 节点的网络流量和系统负载已经比较高了,抓包可能会导致情况进一步恶化。

四、热 Key 问题解决方案

1)增加 Redis 实例复本数量

对于出现热 Key 的 Redis 实例,我们可以通过水平扩容增加副本数量,将读请求的压力分担到不同副本节点上。

2)二级缓存(本地缓存)

当出现热 Key 以后,把热 Key 加载到系统的 JVM 中。后续针对这些热 Key 的请求,会直接从 JVM 中获取,而不会走到 Redis 层。这些本地缓存的工具很多,比如 Ehcache,或者 Google Guava 中 Cache 工具,或者直接使用 HashMap 作为本地缓存工具都是可以的。

使用本地缓存需要注意两个问题:

  • 如果对热 Key 进行本地缓存,需要防止本地缓存过大,影响系统性能;

  • 需要处理本地缓存和 Redis 集群数据的一致性问题。

3)热 Key 备份

通过前面的分析,我们可以了解到,之所以出现热 Key,是因为有大量的对同一个 Key 的请求落到同一个 Redis 实例上,如果我们可以有办法将这些请求打散到不同的实例上,防止出现流量倾斜的情况,那么热 Key 问题也就不存在了。

那么如何将对某个热 Key 的请求打散到不同实例上呢?我们就可以通过热 Key 备份的方式,基本的思路就是,我们可以给热 Key 加上前缀或者后缀,把一个热 Key 的数量变成 Redis 实例个数 N 的倍数 M,从而由访问一个 Redis Key 变成访问 N * M 个 Redis Key。 N * M 个 Redis Key 经过分片分布到不同的实例上,将访问量均摊到所有实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// N 为 Redis 实例个数,M 为 N 的 2倍
const M = N * 2
//生成随机数
random = GenRandom(0, M)
//构造备份新 Key
bakHotKey = hotKey + "_" + random
data = redis.GET(bakHotKey)
if data == NULL {
    data = redis.GET(hotKey)
    if data == NULL {
        data = GetFromDB()
        // 可以利用原子锁来写入数据保证数据一致性
        redis.SET(hotKey, data, expireTime)
        redis.SET(bakHotKey, data, expireTime + GenRandom(0, 5))
    } else {
        redis.SET(bakHotKey, data, expireTime + GenRandom(0, 5))
    }
}

在这段代码中,通过一个大于等于 1 小于 M 的随机数,得到一个 bakHotKey,程序会优先访问 bakHotKey,在得不到数据的情况下,再访问原来的 hotkey,并将 hotkey 的内容写回 bakHotKey。值得注意的是,bakHotKey 的过期时间是 hotkey 的过期时间加上一个较小的随机正整数,这是通过坡度过期的方式,保证在 hotkey 过期时,所有 bakHotKey 不会同时过期而造成缓存雪崩。

五、总结

在这一篇文章中我们首先分析了在 Redis 中热 Key 带来的一些问题,同时也介绍了在海量的 Redis Key 中找到热 Key 的一些方法,最后也提到了在解决热 Key 问题中我们常用的一些办法;总结来说,Redis 热 Key 问题首先是请求流量过大造成的,但是更深层次原因还是出现了流量倾斜,单个 Redis 实例承担的流量过大造成的,了解到了本质原因,解决的思路也就简单了,就是要想尽一切办法将单个实例承担的流量打散,让每个机器均衡承担热 Key 的流量,不要出现流量倾斜,保证系统的稳定性。

 

标签:解决办法,缓存,bakHotKey,Redis,实例,Key,请求
From: https://www.cnblogs.com/wangcp-2014/p/17339261.html

相关文章

  • 非关系型数据库安装-redis安装
    linux安装redis最新稳定版本原创 PHP星 编程经验共享 2023-03-1608:00 发表于广东收录于合集#linux18个#redis5个在安装redis之前我们需要提前安装编译安装需要的扩展库,例如:gcc,make等。但是最新版本要求需要python3的支持,所以我们还需要安装python3.1.安装......
  • Redis - 数据类型映射底层结构
    简介从数据类型上体现就是,同一个数据类型,在不同的情况下会使用不同的编码类型,底层所使用的的数据结构也不相同。字符串对象字符串对象的编码可以是int、raw和embstr三者之一。embstr编码是专门用于保存简短字符串的一种优化编码方式,与raw编码会调用两次内存分配函数分......
  • Spring中Redis存取数据示例
    1.导入StringRedisTemplate类importorg.springframework.data.redis.core.StringRedisTemplate;2.自动装配@AutowiredprivateStringRedisTemplatestringRedisTemplate;3.存数据(设置5分钟过期)Stringtoken=UUID.randomUUID().toString();Stringkey=RedisPrefix......
  • Redis 缓存失效问题
    目录Redis缓存缓存击穿场景解决方案:缓存穿透场景解决方案缓存雪崩场景解决方案大量数据同时过期Redis故障宕机Redis缓存引入了缓存层,就会有缓存异常的三个问题,分别是缓存雪崩、缓存击穿、缓存穿透。它们的区别如下:缓存击穿场景高并发流量场景下,大量请求同时访问一个热点......
  • redis
    今日内容1GEO地理位置信息#GEO(地理信息定位):存储经纬度,计算两地距离,范围等 -根据经纬度---》确定具体地址的---》高德开放api---》返回具体地址#redis可以存储经纬度,存储后可以做运算, 比如:两个经纬度之间距离(直线距离)比如:统计某个经纬度范围内有哪些好......
  • redis03 持久化方案 主从复制原理和方案 哨兵高可用
    今日内容详细目录今日内容详细1持久化方案1.1RDB1.2AOF方案1.3混合持久化2主从复制原理和方案3哨兵高可用1持久化方案#什么是持久化redis的所有数据保存在内存中,把内存中的数据同步到硬盘上这个过程称之为持久化#持久化的实现方式 快照:某时某刻数据的一个完成备份......
  • redis高级-day4——redis持久化方案、主从复制原理和方案、哨兵高可用
    目录一、持久化方案1、什么是持久化2、持久化的实现方式3、RDB4、aof方案5、RDB和AOF的选择6、混合持久化二、主从复制原理和方案1、为什么要用主从复制2、主从复制介绍3、redis主从赋值流程,原理三、哨兵高可用1、什么是高可用2、哨兵高可用3、高可用搭建步骤一、持久化方案1、......
  • redis----day04()
    昨日回顾#悲观乐观锁: django中如何实现 -悲观锁:mysql行锁表锁-乐观锁:真正修改时,加入限制条件django中事务如何开启 -原生sql如何开启事务:begin;commit; -django中如何开事务:atomic()commit()for_update是锁表还是锁行如果查......
  • redisson lock的使用
    1.现在错误的用法:RLocklock=redisson.getLock(String.format(LOCK_KEY,2));try{if(lock.tryLock()){//处理logger.info("aaaaaaaaaaaaaaaaaa");}catch(Exceptione){//处理异常}finally{if(lock.isLocked()){lock.unlock();}}测试......
  • redis desktop manager 怎么用-redis desktop manager 下载分享
    redisdesktopmanager怎么用呢?小编为大家的带来了redisdesktopmanager下载安装包。RedisDesktopManager是一款开源的Redis数据库管理工具,由RedisDesktop开发。redisdesktopmanager下载 以下是RedisDesktopManager的一些主要特点:1.直观的用户界面:RedisDesktop......