+-
解决了 Redis 大 key 问题,同事们都夸他牛皮

前言

前几天端午节,小黑准时下班回到家,吃着粽子,看着电视,好生惬意!忽然,手机叮咣叮咣响个不停报警,看了下是某个服务调用 Redis 异常了。

放下饭碗,小黑打开电脑一顿排查,最终定位到是 Redis 有大 key 问题。寻思一时半会儿也解决不了,明天到公司再搞,先继续看电视吧。



1. 什么是大 key


很多朋友肯定在想 Redis 的 key 能有多大呀?


这里就有个误区,所谓的大 key 问题是某个 key 的 value 比较大,所以本质上是大 value 问题


这样就对上了,key 往往是程序可以自行设置的,value 往往不受程序控制,因此可能导致 value 很大。


设想一种场景:


在线音乐 App 中,某个歌单有很多用户收藏,假如有这样的数据结构:


  • 歌单和用户之间的映射关系采用redis存储;
  • Redis 的 key 是歌单 ID,长度可控且很小;
  • Redis 的 value 是个 list,list 包含了用户 ID;
  • 用户可能很多,就导致 list 长度不可控。

  • 这下明白啥是大 key 问题了吧。



    Redis 中有常见的几种数据结构,每种结构对大 key 的定义不同,比如:


  • value 是 String 类型时,size 超过 10KB 为大 key;
  • value 是 ZSET、Hash、List、Set等集合类型时,它的成员数量超过 1 万个为大 key。

  • 上述的定义并不绝对,主要是根据 value 的成员数量和字节数来确定,业务可以根据自己的场景也确定标准。


    2. 大 key 有什么影响


    我们都知道,Redis 的一个典型特征就是:核心工作线程是单线程。


    单线程中请求任务的处理是串行的:如果前面完不成,后面就处理不了。同时也导致分布式架构中内存数据和 CPU 的不平衡。


  • 执行大 key 命令的客户端本身,耗时明显增加,甚至超时;
  • 执行大 key 相关读取或者删除操作时,会严重占用带宽和CPU,影响其他客户端;
  • 大 key 本身的存储带来分布式系统中分片数据不平衡,CPU使用率也不平衡;
  • 大 key 有时候也是热 key,读取操作频繁,影响面会很大;
  • 执行大 key 删除时,在低版本 Redis 中可能阻塞线程。

  • 这样看来大 key 的影响还是很明显的,最典型的就是阻塞线程,并发量下降,导致客户端超时,服务端业务成功率下降。


    3. 大 key 是如何产生的


    大 key 的产生往往是业务方设计不合理,没有预见 vaule 的动态增长问题:


  • 一直往 value 塞数据,没有删除机制,迟早要爆炸;
  • 数据没有合理做分片,将大 key 变成小 key。

  • 4. 如何找到大 key


  • 增加内存、流量、超时等指标监控

  • 由于大 key 的 value 很大,执行读取时可能阻塞线程。导致 Redis 整体 QPS 下降,并且客户端超时会增加,网络带宽会上涨。配置这些报警可以让我们发现大 key 的存在。


  • bigkeys 命令

  • 使用 bigkeys 命令以遍历的方式分析 Redis 实例中的所有 key,并返回整体统计信息与每个数据类型中排名第一的大 key。



  • redis-rdb-tools

  • 使用redis-rdb-tools离线分析工具来扫描RDB持久化文件,虽然实时性略差,但是完全离线对性能无影响。


    redis-rdb-tools 是用 Python 编写的 Redis rdb 快照文件分析工具,它可以把 rdb 快照文件生成 JSON 文件,或者生成报表用来分析 Redis 的使用详情。


  • 集成化可视化工具

  • 基于某些公有云或者公司内部架构中,Redis 一般都会有可视化的页面和分析工具,来帮助定位大 key,当然页面底层也可能是基于 bigkey s或者 rdb 文件离线分析的结果。



    4. 如何解决大 key 问题


    根据大 key 的实际用途可以分为两种情况:可删除和不可删除。



    删除大 key


    如果发现某些大 key 并非热 key,可以在 DB 中查询使用,则可以在 Redis 中删掉:


  • 当 Redis 版本大于4.0时,可使用 UNLINK 命令安全地删除大 key,该命令能够以非阻塞的方式,逐步地清理传入的 key。

  • Redis UNLINK 命令类似 DEL 命令,表示删除指定的 key。如果指定 key 不存在,命令会对此忽略。UNLINK 命令不同于 DEL 命令之处在于,它是异步执行的,因此不会阻塞。UNLINK 命令是非阻塞删除,非阻塞删除简言之,就是将删除操作放到另外一个线程去处理。


  • 当 Redis 版本小于 4.0 时,避免使用阻塞式命令 KEYS。而是建议通过 SCAN 命令执行增量迭代扫描 key,然后判断进行删除。

  • Redis Scan 命令用于迭代数据库中的数据库键。SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。



    压缩和拆分 key


  • 当 vaule 是 string 时,比较难拆分。可使用序列化、压缩算法将 key 的大小控制在合理范围内,但是序列化和反序列化都会带来更多时间上的消耗;

  • 当 value 是 string,且压缩之后仍然是大 key,则需要进行拆分。一个大 key 分为不同的部分,记录每个部分的 key,使用 multiget 等操作实现事务读取;

  • 当 value 是 list/set 等集合类型时,根据预估的数据规模来进行分片。不同的元素计算后分到不同的片。


  • 小结


    Redis 的大 key 问题,无论在面试还是工作中都很常见,非常值得好好理解一波。

    祝各位老铁深夜无报警,线上无 Bug!



    - EOF -

    推荐阅读   点击标题可跳转

    1、Redis 突然变慢了如何排查并解决?

    2、选 Redis 做 MQ 的人,是水平欠缺么?

    3、面试题详解:用 Redis 实现分布式锁的血泪史


    看完本文有收获?请转发分享给更多人

    关注「ImportNew」,提升Java技能

    点赞和在看就是最大的支持❤️