redis内存消耗详解
Redis所有的数据都存在内存中,相对于廉价的硬盘,内存资源还是比较昂贵的,因此如何高效利用redis内存变得非常重要。
- 内存消耗分析
- 管理内存的原理和方法
- 内存优化技巧
一、内存消耗
理解redis内存,首先要掌握redis内存消耗在哪些方面。有些内存消耗是必不可少的,而有些可以通过参数调整和合理使用来规避内存浪费。
1.1 内存使用统计
首先需要了解redis自身使用内存的统计数据,可通过执行info memory命令来获取内存相关指标
- used_memory redis分配器分配的内存总量,也就是内部存储的所有数据内存占用量
- used_memory_human 以可读的格式返回used_memory
- used_memory_rss 从操作系统的角度显示redis进程占用的物理内存总量
- used_memory_peak 内存使用的最大值,表示used_memory的峰值
- used_memory_peak_human 以可读的格式返回used_memory_peak
- used_memory_lua Lua引擎所消耗的内存大小
- mem_fragmentation_ratio used_memory_rss/used_memory比值。表示内存碎片率
- mem_allocator redis 所使用的内存分配器,默认为jemalloc
需要重点关注的指标有:used_memory_rss和used_memory 以及它们的比值mem_fragmentation_ratio。
当mem_fragmentation_ratio>1时,说明used_memory_rss - used_memory 多出的部分内存并没有用于数据存储,而是被内存所消耗,如果两者相差很大,说明碎片率严重。
当mem_fragmentation_ratio<1时,这种情况一般出现在操作系统把redis内存交换(swap)到磁盘导致,出现这种情况要格外关注,由于硬盘速度远远慢于内存,redis性能会变得很差,甚至僵死。
1.2 内存消耗划分
redis进程内消耗主要包括:
- 自身内存
- 对象内存
- 缓冲内存
- 内存碎片
redis自身内存消耗非常少,通常空进程中used_memory_rss在3MB左右,used_memory在800KB左右,一个空的redis进程消耗的内存可以忽略不计。
1.2.1 对象内存
对象内存是redis内存占用最大的一块,存储着用户所有的数据,对象内存可以简单理解为sizeof(key)+ sizeof(values)。键对象都是字符串,在使用redis时很容易忽略键对内存消耗的影响,应当避免使用过长的键。value对象更复杂些,主要包含五种基本数据类型:字符串、列表、哈希、集合、有序集合。其它数据类型都是建立在这5种数据结构之上实现的,如:Bitmaps和HyperLogLog是使用字符串实现的,GEO使用有序集合实现等。每种value对象类型根据使用规模不同,占用内存不同。在使用时一定要合理预估并监控value对象占用情况,避免内存溢出。
1.2.2 缓冲内存
缓冲内存主要包括:客户端缓冲、复制积压缓冲区、AOF缓冲区。
客户端缓冲指的是所有接入到redis服务器tcp连接的输入输出缓冲。输入缓冲无法控制,最大空间为1G,如果超过将断开连接。对于 Redis 的输出(也就是命令的返回值)来说,其大小经常是不可控的,可能是一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行命令太多,产生的返 回数据的速率超过了往客户端发送的速率,这时也会产生消息堆积,从而造成输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。所以 Redis 设置了一些保护机制来避免这种情况的出现,这些机制作用于不同种类的客户端,有不同的输出缓冲区大小限制,限制方式有两种:
- 一种是大小限制,当某一个客户端的缓冲区超过某一大小时,直接关闭掉这个客户端连接
- 另一种是当某一个客户端的缓冲区持续一段时间占用空间过大时,也直接关闭掉客户端连接
输出缓冲区通过client-output-buffer-limit控制:
- 对普通客户端来说,默认配置是client-output-buffer-limit normal 0 0 0,也就是不限制,因为普通客户端通常采用阻塞式的消息应答模式,如:发送请求,等待返回,再发请求,再等待返回。这种模式通常不会导致输出缓冲区的堆积膨胀。但是当有大量慢连接客户端接入时,这部分内存消耗就不能忽略不计了,可以设置maxclients做限制。特别是当使用大量数据输出的命令且数据无法及时推送给客户端时,如monitor命令,容易造成redis服务器内存突然飙升。
- 对于 Pub/Sub 客户端来说,大小限制是32m,当输出缓冲区超过32m时,会关闭连接。持续性限制是,当客户端缓冲区大小持续60秒超过8m,也会导致连接关闭。当订阅服务的消息生产快于消费速度时,输出缓冲区会产生积压造成输出缓冲区空间溢出。
- 而对于 Slave 客户端来说,大小限制是256m,持续性限制是当客户端缓冲区大小持续60秒超过64m时,关闭连接。当从节点之间网络延迟较高或主节点挂在大量从节点时这部分内存消耗占用很大一部分,建议主节点挂在的从节点不要多于2个,主节点不要部署在网络较差的环境下,如异地机房环境,防止复制客户端连接缓慢造成溢出。
输入输出缓冲区在大流量的场景中容易失控,造成redis内存不稳定,需要重点监控。
复制积压缓冲区:redis2.8版本之后提供了一个可重用的固定大小缓冲区用于实现部分复制功能,根据repl-backlog-size参数控制,默认是1M。对于复制积压缓冲区整个主节点只有一个,所有的从节点共享此缓冲区,因此可以设置较大的缓冲区空间,如100M,这部分内存投入是有价值的,可以有效避免全量复制
AOF缓冲区:这部分空间用于在redis重写期间保存最近写入命令,此缓冲区空间消耗用户无法控制,消耗的内存取决于AOF重写时间和写入命令量,这部分空间占用通常很小。
1.2.3 内存碎片
redis默认的内存分配器采用jemalloc,可选的分配器还有:glibc、tcmalloc。内存分配器为了更好的管理和重复利用内存,分配内存策略一般采用固定范围的内存块进行分配。例如jemalloc在64位系统中将内存空间分为:小、大、巨大三个范围。每个范围内又划分为多个小的内存块单位。比如当保存5KB对象时,jemalloc可能会采用8KB的块存储,而剩下的3KB空间变为了内存碎片不能再分配给其他对象存储。内存碎片虽然是所有内存服务的通病,但是jemalloc针对碎片化问题专门做了优化,一般不会存在过度碎片化的问题,正常的碎片率(mem_fragmentation_tatio)在1.03左右。但是当存储的数据长短差异较大时,以下场景容易出现高内存碎片问题:
- 频繁做更新操作,例如频繁对已存在的键执行append、setrange等更新操作。
- 大量过期键删除,键对象过期删除后,释放的空间无法得到充分利用,导致碎片率上升
出现高内存碎片问题时,常见的解决方式如下:
- 数据对齐:在条件允许的情况下尽量做数据对齐,比如数据尽量采用数字类型或者固定长度字符串等,但是这要视具体的业务而定,有些场景无法做到。
- 安全重启:重启节点可以做到内存碎片重新整理,因此可以利用高可用架构,如sentinel或cluster,将碎片率过高的主节点转换为从节点,进行安全重启。
1.3 子进程内存消耗
子进程内存消耗主要指执行AOF/RDB重写时redis创建的子进程内存消耗。redis执行fork操作产生的子进程内存占用量对外表现与父进程相同,理论上需要一倍的物理内存来完成重写操作。但linux具有写时复制技术(copy-on-write),父子进程会共享相同的物理内存页,当父进程处理写请求时会对需要修改的页复制出一份副本来完成写操作,而子进程依然读取fork时整个父进程的内存快照。
linux kernel在2.6.38内核增加了Transparent Huge Page(THP)机制,而有些linux发行版即使内核达不到2.6.38也会默认加入并开启这个功能,如Redhat Enterprise Linux在6.0以上版本默认会引入THP。虽然开启THP可以降低fork子进程的速度,但之后cory-on-write期间复制内存页的单位从4KB变为2MB,如果父进程有大量写命令,会加重内存拷贝量,从而造成过度内存消耗。例如,以下两个执行AOF重写时的内存消耗日志:
#开启THP:
C * AOF rewrite: 1039 MB of memory used by copy-on-write
#关闭THP:
C * AOF rewrite: 9 MB of memory used by copy-on-write
这两个日志出自同一redis进程,used_memory总量为1.5GB,子进程执行期间每秒写命令量都在200左右。当分别开启和关闭THP时,子进程的内存消耗有天壤之别。如果在高并发写的场景下开启THP,子进程内存消耗可能是父进程的数倍,极易造成机器物理内存溢出,从而触发SWAP或者OOM,子进程消耗总结如下:
- redis产生的子进程并不需要消耗一倍的父进程内存,实际消耗根据期间写入命令量决定,但是依然要预留出一些内存防止溢出。
- 需要设置sysctl vm.overcommit_memory=1 允许内核可以分配所有的物理内存,防止redis进程执行fork时因系统剩余内存不足而失败。
- 排查当前系统是否支持THP,如果开启建议关闭,防止copy-on-write期间内存过度消耗。
redis内存消耗详解的更多相关文章
- 深度历险:Redis 内存模型详解
https://mp.weixin.qq.com/s/Gp6Ur7omGY6ZqDWygU2meQ Redis 是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说 Redi ...
- Redis 配置文件 redis.conf 项目详解
Redis.conf 配置文件详解 # [Redis](http://yijiebuyi.com/category/redis.html) 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, ...
- Redis配置参数详解
Redis配置参数详解 /********************************* GENERAL *********************************/ // 是否作为守护进 ...
- Redis 复制过程详解
Redis 的复制功能分为同步( sync )和命令传播( command propagate )两个步骤: 同步用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态. 命令传播则用于在主服务 ...
- [转]Reids配置文件redis.conf中文详解
转自: Reids配置文件redis.conf中文详解 redis的各种配置都是在redis.conf文件中进行配置的. 有关其每项配置的中文详细解释如下: 对应的中文版解释redis.conf # ...
- 探索Redis设计与实现2:Redis内部数据结构详解——dict
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- Redis底层函数详解
Redis底层函数详解 serverCron 函数 它负责管理服务器的资源,并维持服务器的正常运行.在执行 serverCron 函数的过程中会调用相关的子函数,如 trackOperationsPe ...
- Tomcat内存设置详解
Java内存溢出详解 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError: Java heap space ----JVM Heap(堆)溢出 JVM在 ...
- Tomcat内存溢出详解【转载】
本文转载自 http://elf8848.iteye.com/blog/378805 Java内存溢出详解 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryEr ...
随机推荐
- (HTTPS)web 项目如何实现https
HTTPS实际是SSL over HTTP, 该协议通过SSL在发送方把原始数据进行加密,在接收方解密,因此,所传送的数据不容易被网络黑客截获和破解.本文介绍HTTPS的三种实现方法.方法一 静态超链 ...
- spring-线程池(3)
一.初始化 1,直接调用 import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; import org.springframe ...
- More 3D Graphics (rgl) for Classification with Local Logistic Regression and Kernel Density Estimates (from The Elements of Statistical Learning)(转)
This post builds on a previous post, but can be read and understood independently. As part of my cou ...
- Docker - 运行 containers 使用在 swarm 模式下创建的 overlay 模式的 network
前言 在Docker engine v1.12, 使用Swarm可以方便的创建overlay模式的网络,但是它只能被swarm下面的service所使用的,相对于container,这个网络是完全隔离 ...
- SQL注入攻击[详解]
SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候, ...
- 数据库MySQL纯净卸载
有些人在安装MySQL后,卸载后再次安装时,一直安装不上去,到最后不得不重装系统来安装MySQL.这里教大家如何将MySQL卸载干净,不影响下次安装. 卸载过程 1.停止mysql服务 2.进行卸载 ...
- Android之使用JAVA占位符格式数据(很实用)
小编虽然是学java出生,但工作之后就一直从事android开发,很多java基础都忘记完了,最近一年从ES换到了AS,原来的很多习惯都收到了挑战,比如我喜欢ES写方法的时候先在JAVA projec ...
- webpack 插件拾趣 (1) —— webpack-dev-server
结束了一季的忙碌,我这封笔已久的博客也终究该从春困的咒印中复苏,想来写些实用易读的作为开篇,自然是最好不过. 新开个 webpack 插件/工具介绍的文章系列,约莫每周更新一篇篇幅适中的文章聊以共勉, ...
- WPF MVVM 架构 Step By Step(3)(把后台代码移到一个类中)
我觉得大部分开发者应该已经知道怎么去解决这个问题.一般都是把后台代码(GLUE code)移动到一个类库.这个类库用来代表UI的属性和行为.任何代码当被移到一个类库中时都可以被编译成一个DLL,然后可 ...
- MVC启动windows身份验证时初次访问特别慢
最近做了一个关于MVC的项目,刚开始往服务器上面部署时,没有开启windows身份验证,等开发基本收尾时候,将验证开启时,第一次打开的时候需要将近15s的访问时间,别说用户受不了,自己都受不了了. 对 ...