【巡检问题分析与最佳实践】Redis内存高问题

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 作为内存型数据库,阿里云数据库Redis的内存使用率是一个非常重要的监控指标,当实例内存不足时会导致数据库响应缓慢,甚至导致写入错误等不利影响。

往期分享

RDS MySQL

RDS MySQL 实例空间问题

RDS MySQL 内存使用问题

RDS MySQL 活跃线程数高问题

RDS MySQL 慢SQL问题

RDS MySQL 实例IO高问题

RDS MySQL 小版本升级最佳实践

RDS PostgreSQL

RDS PostgreSQL 实例IO高问题

RDS PostgreSQL 慢SQL问题

RDS PostgreSQL CPU高问题

RDS SQL Server

RDS SQL Server 磁盘IO吞吐高问题

RDS SQL Server CPU高问题

RDS SQL Server 空间使用问题

Redis

Redis 流控问题

概述

作为内存型数据库,阿里云数据库Redis的内存使用率是一个非常重要的监控指标,当实例内存不足时会导致数据库响应缓慢,甚至导致写入错误等不利影响。

云数据库Redis具有内置保护功能,用户申请规格中的内存与配置文件中的maxmemory保持一致,即该实例可以使用的内存上限。如果达到此限制,Redis将开始以写命令的错误答复(但将继续接受只读命令),或者您可以将其配置为在达到最大内存限制时采用key逐出的方式以保证Redis的正常运行。

查看内存使用

部署架构为主备模式下,只提供当前主节点的内存使用。通过监控图查看内存的总体使用率非常方便,点击控制台"性能监控"后一目了然。

1.png

部署架构为Redis集群/读写分离模式下,由于多个物理节点组成了一个逻辑实例,且可能通过多个Proxy节点做路由转发和负载均衡,通过监控图查看内存时需要注意以下几点

  • 数据节点聚合指标:在集群模式下,Used Memory显示值为“该集群下所有子Shard的内存使用总和”,这里的子Shard内存不包括备用物理节点和只读实例;在读写分离模式下,Used Memory显示值为“该读写分离实例下各个物理节点内存的平均值”,基本上与单个物理节点内存使用量相同
  • 数据节点:单个Shard节点的内存使用与主备模式相同,但需要人为选择需要查看的物理节点。
  • Proxy节点和Proxy节点聚合:由于Proxy节点只做请求转发和请求的负载均衡,不会实际存储数据,所以基本上不会存在内存瓶颈,所以不提供Proxy组件的内存使用。

2.png

内存使用详细分析

   通过阿里云控制台提供的监控图可以大概确认当前实例的内存使用情况,在大部分场景下,绝大部分的内存均为实际的数据存储需求,当观察到内存超过一定阈值后扩容即可。然而在一些特定场景下,我们可能认为Redis的内存使用不符合预期,这时就需要对Redis实例的内存使用做详细的分析了。

   Redis官方提供有info memory和memory模块做更为精细的内存分析。相比较而言,memory模块的内存详情分析更为细致,本文以memory模块的结果解读作为Redis内存使用详情分析。memory模块一共提供有5个命令,通过memory help指令可以查看各命令得使用方式和概述,我们重点讲解memory stats命令。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory help
1) "MEMORY DOCTOR                        - Outputs memory problems report"
2) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
3) "MEMORY STATS                         - Show memory usage details"
4) "MEMORY PURGE                         - Ask the allocator to release memory"
5) "MEMORY MALLOC-STATS                  - Show allocator internal stats"

memory stats

   MEMORY STATS命令返回有关服务器内存使用情况详情,主要报告了以下指标内容,解读参考注释部分

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory stats
 1) "peak.allocated" //Redis进程启动以来消耗的峰值内存(以字节为单位)
 2) (integer) 79492312
 3) "total.allocated" //Redis使用其分配器分配的总字节数,即当前的总内存使用量
 4) (integer) 79307776
 5) "startup.allocated" //Redis启动时消耗的初始内存量(以字节为单位)
 6) (integer) 45582592
 7) "replication.backlog" //复制积压缓冲区的大小(以字节为单位),在主备复制断开重连时使用
 8) (integer) 33554432
 9) "clients.slaves" //主从复制中所有slave的读写缓冲区大小(以字节为单位)
10) (integer) 17266
11) "clients.normal" //除slave外所有其他客户端的读写缓冲区(以字节为单位)
12) (integer) 119102
13) "aof.buffer" //aof持久化使用的缓存和aofrewrite时产生的缓存
14) (integer) 0
15) "db.0"  //有多少个业务db,这里就会展示多少个
16) 1) "overhead.hashtable.main" //当前db的hash链表开销内存总和,可以认为是元数据内存
    2) (integer) 144
    3) "overhead.hashtable.expires" //当前db存储“设置了key的expire时间”消耗的内存
    4) (integer) 0
    5) "overhead.hashtable.inmem_keys"
    6) (integer) 80
17) "overhead.total" //数值=startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+db.X
18) (integer) 79273616
19) "keys.count" //当前Redis实例的key总数
20) (integer) 2
21) "keys.bytes-per-key" //当前Redis实例每个key的平均大小,数值=(total.allocated-startup.allocated)/keys.count
22) (integer) 16862592
23) "dataset.bytes" //纯业务数据占用的内存大小
24) (integer) 34160
25) "dataset.percentage" //纯业务数据占用的内存比例,数值=dataset.bytes*100/(total.allocated-startup.allocated)
26) "0.1012892946600914"
27) "peak.percentage" //当前总内存与历史峰值的比例,数值=total.allocated*100/peak.allocated
28) "99.767860412597656"
29) "fragmentation" //内存的碎片率
30) "0.45836541056632996"

从上述的指标解读可以看出,正常情况下消耗一个Redis实例内存的绝大部分是纯业务数据,但是也有一些其他方面的内存开销,比如主备复制的积压缓冲区,Redis进程初始化消耗的内存,Redis内部维护Key-Value链表消耗的内存等等。当这些非dataset消耗的内存不多时,我们建议您直接忽略,重点分析dataset的内存使用情况是否符合预期;如果非dataset消耗的内存也占据了相当大的比例,需要根据上述指标解读一一查看内存消耗的根因。

更多memory stats的内存请参考

https://redis.io/commands/memory-stats

memory doctor

   memory docto命令可以提供当前Redis实例关于内存使用方面的建议,在不同的允许状态下会有不同的分析结果,仅供用户参考。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory doctor
Hi Sam, I can't find any memory issue in your instance. I can only account for what occurs on this base

除了上述范例以外,memory doctor还会从其他多个维度给出当前Redis实例的内存诊断建议,主要包括以下方面,您可以根据指令结果制定相应的优化策略。

    int empty = 0;          /* Instance is empty or almost empty. */
    int big_peak = 0;       /* Memory peak is much larger than used mem. */
    int high_frag = 0;      /* High fragmentation. */
    int high_alloc_frag = 0;/* High allocator fragmentation. */
    int high_proc_rss = 0;  /* High process rss overhead. */
    int high_alloc_rss = 0; /* High rss overhead. */
    int big_slave_buf = 0;  /* Slave buffers are too big. */
    int big_client_buf = 0; /* Client buffers are too big. */
    int many_scripts = 0;   /* Script cache has too many scripts. */

memory usage

   MEMORY USAGE命令展示了指定Key消耗的内存,单位为Byte,表示在Redis中存储该Key所需的用于数据和管理开销的内存分配的总和。

   对于嵌套数据类型,提供可选的SAMPLES选项,其中count是采样的嵌套值的数量。默认情况下,此选项设置为5。要对所有嵌套值进行采样,使用SAMPLES 0。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory usage hudao
(integer) 252
r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory usage hudao_not_exit
(nil)

更多关于memory usage的内容参考

https://redis.io/commands/memory-usage

内存数据逐出和expire策略

   前文提到,当云数据库Redis内存不足时,如果用户配置了对应的数据逐出策略,则数据会做自动淘汰以保证Redis实例能够正常运行。在阿里云数据库Redis实例的默认逐出策略是volatile-lru, 如需修改,可以登录控制台在系统参数中修改。

3.png

阿里云Redis支持的数据逐出策略

  • volatile-lru
    按照LRU算法逐出原有数据,但仅逐出设置了过期时间的数据。
  • volatile-ttl
    仅逐出设置了过期时间的数据,并且是按照TTL由小到大的顺序进行逐出。
  • allkeys-lru
    按照LRU算法逐出原有数据。
  • volatile-random
    随机逐出原有数据,但仅逐出设置了过期时间的数据。
  • allkeys-random
    随机逐出原有数据。
  • noeviction
    不逐出任何数据,当内存已满时新数据的写入会得到一个错误信息(DEL和某些其他的命令除外)。
  • volatile-lfu
    按照LFU算法逐出原有数据,只从设置了过期时间的key中选择最不常用的key进行删除。
  • allkeys-lfu
    按照LFU算法优先逐出最不常用的key。

阿里云Redis的expire策略

针对设置了expire属性的key,Redis提供有两种过期删除策略,一种为惰性删除,一种为主动删除。惰性删除即当key过期时并不删除,每次从数据库获取该key的时候再去检查是否过期,如果过期则删除,并返回NULL。主动删除指的是通过修改hz参数的值,您可以调整Redis执行定期任务的频率,从而改变Redis清除过期key、清理超时连接等操作的效率,执行过程如下:

  1. 从设置了过期时间的key的集合中随机检查20个key。
  2. 删除检查中发现的所有过期key。
  3. 如果检查结果中25%以上的key已过期,则开始新一轮任务。

如果过期key数量很多或者增加速度很快,而Redis的主动清除频率较低,过期key将占用大量的内存空间,可能会影响Redis服务的性能。适当调整hz参数的值,提高清除频率,能够很好地解决这个问题。

相关自定义参数解读

  • hz:设置 Redis 后台任务执行频率,比如清除过期键任务,设置范围为1到500,默认为10。数值越大 CPU 消耗越大,延迟越小,建议不要超过100
  • dynamic-hz:根据客户端连接数动态调整hz的值,从而实现Redis定期任务执行频率的自动调整,Redis 5.0以后版本支持
  • azyfree-lazy-eviction:是否使用lazy free的方式驱逐key,当内存达到上限,分配失败后
  • lazyfree-lazy-expire:是否使用lazy free的方式删除设置了expire且到期的key
  • lazyfree-lazy-server-del:在隐式数据删除时,是否使用lazy free

更多关于过期key的清理策略可参考

https://help.aliyun.com/document_detail/142171.html

https://help.aliyun.com/document_detail/142379.html

https://redis.io/commands/expire

Redis内存使用最佳实践

一般来说,dataset占用了Redis实例中的绝大部分内存,所以优化dataset的大小至关重要。一般有以下的优化思路

  • 不要放垃圾数据
  • 测试数据,下线业务的数据需要及时清理
  • 给key设置合理的过期时间和清理策略
  • 对具有时效性的数据设置TTL,通过Redis自身的过期key清理机制降低过期key对内存的占用
  • 建议结合惰性删除和主动删除,惰性删除容易造成大量不被访问的过期key占用内存的问题,主动删除需要额外消耗CPU资源
  • 不同业务使用不同的逻辑db
  • 不同业务使用同一个Redis实例时,存放在不同的逻辑db上有助于业务梳理,无用业务的数据清空等
  • 过期key的主动清理会依次遍历所有db,存放不同的逻辑db更有助于提高过期数据清理效率
  • 避免出现大Key
  • Key过大会导致网络传输时延比较大,需要的输出缓冲区也更大,在定时清理过程中也容易造成比较高的延迟
  • 建议通过业务拆分,数据压缩等方式避免大Key的产生
  • 通过使用阿里云Redis控制台提供的“缓存分析”功能分析大Key,步骤如下图所示
  • 4.png
  • 配置升级
  • 主备架构的云数据库Redis最大支持64G内存,集群版本最大可支持4T内存

更多关于Redis的内存使用优化参考

https://redis.io/topics/memory-optimization





相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
12天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
1月前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
49 1
|
2月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
51 2
|
7天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
17天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
114 9
|
21天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
30天前
|
存储 缓存 NoSQL
Redis Quicklist 竟让内存占用狂降50%?
【10月更文挑战第11天】
40 2
|
1月前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
46 2
|
2月前
|
缓存 监控 NoSQL
阿里面试让聊一聊Redis 的内存淘汰(驱逐)策略
大家好,我是 V 哥。粉丝小 A 面试阿里时被问到 Redis 的内存淘汰策略问题,特此整理了一份详细笔记供参考。Redis 的内存淘汰策略决定了在内存达到上限时如何移除数据。希望这份笔记对你有所帮助!欢迎关注“威哥爱编程”,一起学习与成长。
|
2月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
41 1