当前位置: 首页 > news >正文

缓存 --- Redis性能瓶颈和大Key问题

缓存 --- Redis性能瓶颈和大Key问题

  • 内存瓶颈
  • 网络瓶颈
  • CPU 瓶颈
  • 持久化瓶颈
  • 大key问题
    • 优化方案

  • Redis 是一个高性能的内存数据库,但在实际使用中,可能会在内存、网络、CPU、持久化、大键值对等方面遇到性能瓶颈。下面从这些方面详细分析 Redis 的性能瓶颈,并给出相应的解决方法。

内存瓶颈

问题

  • Redis 是一个基于内存的数据库,所有数据都存储在内存中,因此内存容量是 Redis 的主要限制。
  • 当数据量超过可用内存时,Redis 可能会触发内存淘汰策略(如 LRU、LFU),导致部分数据被删除。
  • 大键值对(如大哈希表、大列表)会占用大量内存,进一步加剧内存压力。

解决方法

  • 优化数据结构:使用更高效的数据结构(如压缩列表、整数集合)来减少内存占用。
  • 启用内存淘汰策略:根据业务需求配置合适的内存淘汰策略(如 volatile-lruallkeys-lru)。
  • 分片存储:使用 Redis Cluster 将数据分布到多个节点,分散内存压力。
  • 数据压缩:在客户端对数据进行压缩后再存储到 Redis 中。
  • 监控内存使用:定期监控 Redis 的内存使用情况,及时发现和解决内存问题。

网络瓶颈

问题

  • Redis 的性能高度依赖于网络,尤其是在高并发或跨地域部署的场景下。
  • 网络延迟和带宽限制会影响 Redis 的响应速度。
  • 大量小请求可能导致网络拥塞,增加 Redis 的负载。

解决方法

  • 减少网络请求:使用批量操作(如 MGETMSET)减少网络请求次数。
  • 优化Redis客户端连接池:配置合理的客户端连接池,避免连接数过多或过少。
  • 使用内存缓存: 可考虑将一些数据量不大,并且对一致性要求不高的数据移至内存缓存
  • 就近部署:将 Redis 部署在离客户端较近的位置,减少网络延迟。
  • 监控网络流量:定期监控 Redis 的网络流量,及时发现和解决网络瓶颈。

CPU 瓶颈

问题

  • Redis 的核心处理逻辑是单线程的(Redis 6.0 引入了多线程 I/O,但命令执行仍然是单线程的)。
  • 复杂命令(如 SORTZUNIONSTORE)或长耗时操作(如 KEYS *FLUSHALL)会占用大量 CPU 资源,阻塞其他命令的执行。

解决方法

  • 避免复杂命令:使用高效命令替代复杂命令(如 SCAN 替代 KEYS *)。
  • 使用多线程 I/O:在 Redis 6.0 及以上版本中,启用多线程 I/O 提升网络处理能力。
  • 分散计算逻辑:将复杂计算逻辑移到客户端或应用层,减少 Redis 的 CPU 负担。
  • 监控 CPU 使用率:定期监控 Redis 的 CPU 使用率,及时发现和解决 CPU 瓶颈。

持久化瓶颈

问题

  • Redis 提供了两种持久化方式:RDB(快照)和 AOF(追加日志)。
  • RDB 在生成快照时可能会占用大量 CPU 和内存,导致性能下降。
  • AOF 的日志追加操作会增加磁盘 I/O 压力,尤其是在高写入场景下。

解决方法

  • 选择合适的持久化方式:根据业务需求选择合适的持久化方式(如 RDB 适合备份,AOF 适合数据安全)。
  • 调整持久化配置:优化 RDB 和 AOF 的配置(如 save 参数、appendfsync 参数)以平衡性能和数据安全。
  • 使用混合持久化:在 Redis 4.0 及以上版本中,启用混合持久化(RDB + AOF)以兼顾性能和数据安全。
  • 监控持久化性能:定期监控 Redis 的持久化性能,及时发现和解决持久化瓶颈。

大key问题

大key定义:

  • 所谓的大key问题是某个key的value比较大,所以本质上是大value问题
  • String类型的Key,它的值为5MB(数据过大)
  • List类型的Key,它的列表数量为20000个(列表数量过多)
  • ZSet类型的Key,它的成员数量为10000个(成员数量过多)
  • Hash格式的Key,它的成员数量虽然只有1000个但这些成员的value总大小为100MB(成员体积过大)
  • 在实际业务中,大Key的判定仍然需要根据Redis的实际使用场景、业务场景来进行综合判断。通常都会以数据大小与成员数量来判定。
  • 一般认为string类型控制在10KB以内,hash、list、set、zset元素个数不要超过10000个。

大 Key 的问题

  • 内存占用过高:
    大 Key 会占用大量内存,可能导致 Redis 内存不足,触发内存淘汰策略(如 LRU、LFU),甚至导致 OOM(Out Of Memory)错误。
  • 网络以及操作性能下降:
    对大 Key 的操作(如 HGETALL、LRANGE、SMEMBERS)会消耗大量 CPU 和网络资源,导致 Redis 响应变慢。
  • 大 Key 的操作可能会阻塞 Redis 的单线程模型,影响其他命令的执行。
  • 持久化性能问题:
    在生成 RDB 快照或 AOF 日志时,大 Key 会导致持久化操作变慢,增加 Redis 的负载。
    数据迁移困难:
    在 Redis Cluster 中,大 Key 的数据迁移会占用大量网络带宽,影响集群的稳定性。
  • 故障恢复时间长:
    如果 Redis 实例发生故障,大 Key 的恢复时间会显著增加,影响服务的可用性。

大key的产生:

  • 大key的产生往往是业务方设计不合理,没有预见vaule的动态增长问题
  • redis数据结构使用不合理,易造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据
  • 业务上线前规划设计不足,没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多
  • 没有对无效数据进行定期清理,造成如HASH类型Key中的成员持续不断的增加。即一直往value塞数据,却没有删除机制,value只会越来越大
  • 在实际业务中,可能会发生的大key场景:
  • 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey;
  • 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey;
  • 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需要注意,第一,是不是有必要把所有字段都缓存,第二,有没有相关关联的数据。

优化方案

删除大key

  • 首先考虑删除大key,如果发现某些大key并非热key就可以在DB中查询使用,则可以在Redis中删掉:
  • Redis 4.0及之后版本:您可以通过UNLINK命令安全地删除大Key甚至特大Key,该命令能够以非阻塞的方式,逐步地清理传入的Key。 Redis UNLINK 命令类似与 DEL 命令,表示删除指定的 key,如果指定 key 不存在,命令则忽略。 UNLINK 命令不同与 DEL
    命令在于它是异步执行的,因此它不会阻塞。 UNLINK 命令是非阻塞删除,非阻塞删除简言之,就是将删除操作放到另外一个线程去处理。
  • Redis 4.0之前的版本:建议先通过SCAN命令读取部分数据,然后进行删除,避免一次性删除大量key导致Redis阻塞。 Redis Scan 命令用于迭代数据库中的数据库键。 SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标,
    用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。

压缩大key

  • 可以采用压缩法, 考虑到使用合适的序列化框架、压缩算法:
  • 当vaule是string时,比较难拆分,则使用序列化、压缩算法将key的大小控制在合理范围内,但是序列化和反序列化都会带来更多时间上的消耗, 如果压缩之后仍然是大key,则需要考虑进行拆分

拆分大key

  • 将一个Big Key拆分为多个key-value这样的小Key,并确保每个key的成员数量或者大小在合理范围内,然后再进行存储,通过get不同的key或者使用mget批量获取。
  • 当value是list/set等集合类型时,根据预估的数据规模来进行分片,不同的元素计算后分到不同的片

本地缓存(Caffeine )

  • 可以考虑本地缓存(Caffeine )》Redis》数据库

Case Study
某电商平台的商品评论数据存储在 Redis 中,使用列表(List)数据结构存储每个商品的评论 ID。由于某些热门商品的评论数量高达数百万条,导致这些商品的评论列表成为大 Key,引发以下问题:

  • 内存占用过高,导致 Redis 内存不足。
  • 获取评论列表(LRANGE)时,响应时间过长。
  • 持久化操作变慢,影响 Redis 的性能。

将每个商品的评论列表拆分为多个小列表

  • 例如:将商品 A 的评论列表拆分为 product:A:comments:1、product:A:comments:2、…、product:A:comments:N,每个小列表存储 1 万条评论。
  • 在客户端或应用层实现分页逻辑,按需获取评论列表
  • 将评论列表从列表(List)改为有序集合(ZSet),以支持按时间排序和分页查询

限制 Key 的大小:

  • 在写入评论时,检查评论列表的大小,如果超过阈值(如 1 万条),则创建新的小列表。

定期清理大 Key:

  • 使用 SCAN 命令定期扫描 Redis 中的大 Key,并进行清理或拆分。

相关文章:

  • Python内存管理之隔代回收机制详解
  • windows docker desktop 无法访问容器端口映射
  • 线程安全总结
  • 关于编译树莓派内核系统的总结
  • 【MySQL】MySQL的基础语法及其语句的介绍
  • 【微知】服务器如何获取服务器的SN序列号信息?(dmidecode -t 1)
  • 游戏引擎学习第237天:使用 OpenGL 显示图像
  • IP数据报
  • GA 。。。
  • 在统信UOS上修改sudo权限以免输入密码
  • 【数据结构 · 初阶】- 带环链表
  • 蓝桥杯题目:卡牌
  • 无意间发现的宝藏项目:开源世界中的演示项目精选合集
  • AutoSAR从概念到实践系列之MCAL篇(二)——Mcu模块配置及代码详解(上)
  • 【AI训练环境搭建】在Windows11上搭建WSL2+Ubuntu22.04+Tensorflow+GPU机器学习训练环境
  • skywalking agent 关联docker镜像
  • 软考高级系统架构设计师-第16章 数学与经济管理
  • 【网络篇】从零写UDP客户端/服务器:回显程序源码解析
  • 【nginx】服务的信号控制
  • 精益数据分析(9/126):如何筛选创业路上的关键数据指标
  • 海南医科大学继续开展部门正职竞聘上岗,致力营造“谁有本事谁来”
  • 为什么要研制大型水陆两栖飞机?AG600总设计师给出答案
  • 玉渊谭天丨先爆视频再爆订单,美关税影响下企业因短视频火出圈
  • 城事|2小时40分42秒,天工夺冠!全球首个人形机器人半马开跑
  • 今年1-3月全国吸收外资2692.3亿元人民币
  • 十四届全国人大常委会第十五次会议将于4月27日至30日举行