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 ...
随机推荐
- 详解CockroachDB事务处理系统
本文提到的一些术语,比如Serializability和Linearizability,解释看Linearizability, Serializability and Strict Serializa ...
- Android 桌面不显示应用图标
忽然有一天,运行自己的程序,发现桌面没有应用图标了. google了半天,也没什么发现. 最后发现是主Activity中: <action android:name="android. ...
- 006开源O/R映射框架内容回顾
Hibernate是一个O/R映射框架(也称为ORM) 从ORM词来看,O---Object(对象模型):R--- Relational(关联模型),可以做对象和关联的一种映射,当然这只是部分功能,一 ...
- iOS安全攻防之越狱设备检测
iOS 越狱(iOS Jailbreaking),是用于获取苹果公司便携装置操作系统iOS最高权限的一种技术手段,用户使用这种技术及软件可以获取到 iOS 的最高权限,甚至可能可以进一步解开运营商对手 ...
- javascript基础-闭包
原理 函数里包含函数,即闭包. 包含函数的结果是,子函数会挟持父函数的活动对象.子函数在访问一个变量时,先读自身的活动对象,是否包含此变量,没有从父函数里找,还没有,去祖函数,层层回溯,直到windo ...
- 码工具通过ICP备案
5月22日,为广大程序员造福的在线工具--码工具 通过了ICP备案,这也意味着本站也越来越正规化,规范化.大家从今日起就可以在网站底部看到本站的ICP备案号. 备案/许可证编号:粤ICP备170597 ...
- Tips_of_JS 之 利用JS实现水仙花数的寻找与实现斐波那契数列
一.水仙花数 1.啥是水仙花数? 水仙花数是指一个 n 位正整数 ( n≥3 ),它的每个位上的数字的 n 次幂之和等于它本身.(例如:1^3 + 5^3+ 3^3 = 153) 2.利用JS实现对水 ...
- Bottle源码阅读笔记(一):WSGI
前言 Bottle是一个Python Web框架.整个框架只有一个文件,不到4k行的代码,没有Python标准库以外的依赖,却包含了路由.模板和插件等Web框架常用功能.通过阅读Bottle源码来了解 ...
- kali&BT安装好之后无法上网或者无法获得内网IP
大家都知道,要想进行内网渗透攻击,你必须要在那个内网里. 但是大家在Vmware里安装kali的时候,大多数用户为了方便,未选择桥接模式,而是选择了使用与本机共享的IP网络当然,这样能上网,但是你的虚 ...
- integer与int区别以及integer.values()方法详解
声明:本文为博主转载文章,原文地址见文末. 知识点1:integer和int的区别 /* * int是java提供的8种原始数据类型之一.Java为每个原始类型提供了封装类,Integer是java为 ...