美文网首页大型网站架构
Redis 持久化内存不足分析

Redis 持久化内存不足分析

作者: stamp1238 | 来源:发表于2019-04-07 22:25 被阅读814次

           在最近的工作中,碰到很多Redis的问题,项目组将Redis当做黑盒子使用,这是一件很危险的事情,导致很多意想不到的事情发生,在使用Redis的时候,我们必须理解redis.conf中常用参数的意义,例如maxmemory最大内存,默认是操作系统可用的最大内存,这个明显在生产上是必须配置具体值,但是很遗憾,项目组在使用中没有调整。今天我要讲的是redis的内存使用,redis的内存其实有数据内存,进程内存,输入缓存区大小,输出连接动态内存等不同的概念,这里我们只针对RDB和AOF持久化的时候,fork进程需要的内存大小进行讲解,从而理解一台虚拟机内存和Redis内存的配置关系。

    1、fork 的原理

            在redis在进行持久化的时候,有的时候可以在日志中看到fork进程失败的提示,一般是系统可用的内存空间不够导致,这需要我们对fork原理明白,才能更好的进行参数调整。一般来说Redis在进行RDB的时候,会fork出一个子进程,子进程和父进程会共享一个地址空间,在fork子进程的时候,会检查当前机器可用的内存是否满足fork出一个子进程的要求,一般由操作系统 overcommit_memory决定。

    overcommit_memory参数说明: 设置内存分配策略(可选,根据服务器的实际情况进行设置) /proc/sys/vm/overcommit_memory 可选值:

     0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。 操作系统默认参数,如果redis进程使用了9G空间,fork需要9G内存,但是整个操作系统内存16G,9+9>16,那么fork会失败。

    1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何,实际在fork出子进程真正执行的时候,需要的内存并不会有父进程那么大,如果redis进程使用了9G,整个内存16G,这个时候只要有剩余内存,就会fork成功,而且子进程在RDB的时候,整个需要的内存空间并不需要9G,因为父子进程共享数据空间,需要新增的的内存空间是RDB备份过程中 copy-on-writer变化的数据空间,取决于数据变化和页空间,一般都不会特别大。

    2, 表示内核允许分配超过所有物理内存和交换空间总和的内存,根据vm.overcommit_ratio定义的值,允许分配超出物理内存加上交换内存的请求。vm.overcommit_ratio参数是一个百分比,加上内存量决定内存可以超量分配多少内存。例如,vm.overcommit_ratio值为50,而内存有16GB,那么这意味着在内存分配请求失败前,加上交换内存4G,内存将允许高达16*.0.5+4=12GB的内存分配请求

          通过上面的分析,我们可以看到,针对Redis的持久化,尤其是在一台机器上部署多个节点的时候,我们可以通过设置overcommit_memory=1的优化,减少操作系统内存,提高redis的fork成功率,因为fork后的进程和父进程共享一个数据空间,持久化要新增的内存空间都会小于父进程已经使用的空间,具体设置为:

    echo “vm.overcommit_memory=1” >> /etc/sysctl.conf

    sysctl vm.overcommit_memory=1

    2、减少fork时父进程变化内存

          上面讲到,当redis持久化fork子进程后,占用内存大小和父进程等同,由于Linux在写时有copy-on-write机制,父子进程共享相同的物理内存页,当父进程处理写请求的时候会把要修改的页创建副本,而子进程在fork过程中共享整个父进程的内存快照,在RDB重写时,Redis的日志输出 RDB: 657 MB of memory used by copy-on-write。代表重写过程中有内存页被修改,父进程创建了副本,这部分的内存空间是657MB的大小。如果我们要减少创建的副本的大小,就涉及操作系统的另外一个概念 Huge Pages 大页。

           在Redhat Linux中,内存都是以页的形式划分的,默认情况下每页是4K,这就意味着如果物理内存很大,则映射表的条目将会非常多,会影响CPU的检索效率。因为内存大小是固定的,为了减少映射表的条目,可采取的办法只有增加页的尺寸。Linux Kernel在2.6.38内核中增加了THP的特性,支持大内存页(2MB)分配,默认开启。当开启后可以加快fork子进程的速度,但fork操作之后,每个内存页从原来的4KB变成了2MB,会大幅增加重写期间父进程内存消耗,同时每次写命令引起的复制内存页单位放大了512倍,会拖慢写操作的执行时间,因此在Redis的时候日志建议是对此特性进行禁用,方法为: echo never > /sys/kernel/mm/transparent_hugepage/enabled。为了让机器重启该参数仍然生效,建议在/etc/rc.local中追加,避免失效。当大页被关闭后,可以看到同等操作下,RDB备份时候的copy-on-write变化内存空间会减少。

         综上分析,我们可以操作系统物理内存和Redis内存之间的一些关系,尤其Redis在持久化的时候fork进程会随操作系统的参数不同,需要的内存也有所不同,为了加快fork子进程的速度以及主备之间的文件传输同步,一般我们建议一个Redis节点的最大内存在10G-15G左右,操作系统的内存适当冗余,尽量控制同一台机器的多个Redis节点在同一个时间点进行RDB备份(可以通过缓存中心定时备份),导致内存同一时刻增加避免内存空间不足导致的fork失败,最安全保险的情况是内存为Redis的2倍,但是在vm.overcommit_memory=1和大页管关闭的情况下,可以根据实际使用,降低操作系统的整个内存大小 。

          对于使用人员来说,一定要清楚原理,重点评估Redis的总节点数和操作系统内存的关系,避免持久化和高可用性带来的内存不足问题。

    相关文章

      网友评论

        本文标题:Redis 持久化内存不足分析

        本文链接:https://www.haomeiwen.com/subject/ibosiqtx.html