【原创】为什么 Redis 重启后没有正确恢复之前的内存数据

安装 Redis 后,默认配置下启动会得到如下日志: 
1
2
[3077] 05 Sep 10:01:54.181 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.
To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
      该输出信息大致的意思是: overcommit_memory 值目前设置为 0 !在可用内存比较少时,后台执行的 save 命令可能会失败。为了修复该问题,需要添加 'vm.overcommit_memory  = 1' 到 /etc/sysctl.conf 文件中,并 通过 重启系统或执行 'cysctl vm.overcommit_memory=1' 命令使得该配置生效。 

查看系统中的配置如下: 
1
2
[root@Betty ~]# sysctl vm.overcommit_memory
vm.overcommit_memory = 0
恩,确实系统配置为 0 。那么 overcommit_memory 该值的含义是什么?可以设置哪些值呢? 

网上相关文章对该参数有如下说明: 

 CommitLimit 和 Committed_As  
1
2
3
4
[root@Betty ~]# grep -i commit /proc/meminfo
CommitLimit:     5856624 kB
Committed_AS:     863300 kB
[root@Betty ~]#
在内存信息中可以看到 CommitLimit 和 Committed_As 两个参数。其中 
  • CommitLimit 是内存分配的上限。CommitLimit = 物理内存 * overcommit_ratio/100 + swap
  • Committed_As 是已经分配的内存大小。
 overcommit_memory  

说法一: 
overcommit_memory 参数就是控制内存分配是否可以超过 CommitLimit 值的,其指定了内核针对内存分配的策略,默认是 0 。 
  • 0 表示尽量减少 swap 的使用;
  • 1 表示允许超过 CommitLimit ;
  • 2 表示不允许超过 CommitLimit 。

说法二: 
  • 0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,则允许内存申请;否则,内存申请失败,并把错误返回给应用进程。
  • 1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
  • 2, 表示内核允许分配超过所有物理内存和交换空间总和的内存

说法三: 
vm.overcommit_memory 表示内核在分配内存时候做检查的方式。 这个变量可以取到 0、1、2 三个值。针对取不同的值时的处理方式,定义在内核源码 mm/mmap.c 的 __vm_enough_memory 函数中。 

取 1 时 此时宏为 OVERCOMMIT_ALWAYS,函数直接 return 0,分配成功。 
取 2 时:  此时宏为 OVERCOMMIT_NEVER,内核计算: 
1
内存总量*vm.overcommit_ratio/100 + SWAP 的总量
如果申请空间超过此数值,则分配失败。vm.overcommit_ratio 的默认值为 50。 
取 0 时 此时宏为 OVERCOMMIT_GUESS,内核计算: 
1
NR_FILE_PAGES 总量 + SWAP总量 + slab 中可以释放的内存总量
如果申请空间超过此数值,则将此数值与空闲内存总量减掉 total reserve_pages(?) 的总量相加。如果申请空间依然超过此数值,则分配失败。 

以上为粗略描述,在实际计算时, 如果非 root 进程,则在计算时候会保留 3% 的空间,而 root 进程则没有该限制  

说法四: 
vm.overcommit_memory  默认值为 0 。 从内核文档里得知,该参数有三个值,分别是: 
  • 0:当用户空间请求更多的的内存时,内核尝试估算出剩余可用的内存;
  • 1:当设这个参数值为 1 时,内核允许超量使用内存直到用完为止,主要用于科学计算;
  • 2:当设这个参数值为 2 时,内核会使用一个决不过量使用内存的算法,即系统整个内存地址空间不能超过 swap + 50% 的 RAM 值50% 参数的设定是在 overcommit_ratio 中设定。
vm.overcommit_ratio  默认值为 50 。 这个参数值只有在 vm.overcommit_memory=2 的情况下,这个参数才会生效。 

说法五: 
Controls overcommit of system memory, possibly allowing processes to allocate (but not use) more memory than is actually available. 
  • 0 - Heuristic overcommit handling. Obvious overcommits of address space are refused. Used for a typical system. It ensures a seriously wild allocation fails while allowing overcommit to reduce swap usage. root is allowed to allocate slighly more memory in this mode. This is the default.
  • 1 - Always overcommit. Appropriate for some scientific applications.
  • 2 - Don't overcommit. The total address space commit for the system is not permitted to exceed swap plus a configurable percentage (default is 50) of physical RAM. Depending on the percentage you use, in most situations this means a process will not be killed while attempting to use already-allocated memory but will receive errors on memory allocation as appropriate.

说法六(可以认为是上面英文比较准确的翻译): 
  • 0:默认值,由 Linux 内核通过一些启发式算法来决定是否超售和超售的大小,一般允许轻微的超售,拒绝一些明显不可能提供的请求,同时做一些规则限制,比如不同用户 overcommit 的大小也不一样。 
  • 1:允许,不做限制的超售,当然这个也不是无限大,还受到寻址空间的限制,32 位系统最大可能只有 4G,64 位系统大概 16T 左右。 
  • 2:禁止,禁止超售,系统能够分配的内存不会超过 swap + 实际物理内存 * overcommit_ratio,该值可以通过 /proc/sys/vm/overcommit_ratio设置,默认 50% 。


说了半天,这个和 Redis 上面给出的输出信息有什么关系呢? 
       目前我认为比较正确的解释是这样的:Redis 在执行 bgsave 的时候需要 fork 出一个子进程,一般情况下我们认为子进程所需内存空间的量等于父进程内存空间使用量,但由于 Linux 中 COW 的原因,子进程在刚刚运行时可能并不需要占用等同于父进程使用的内存量。但是尽管如此,若系统当前可用内存量较少,且在 overcommit_memory = 0 的情况下,就可能会出现由于判定内存不足导致分配失败的情况(虽然实际上可能并不真正需要那么多内存)。所以 overcommit = 1 这种设置对于 Redis 这种缓存系统显得更为有意义。 
      关于  overcommit 本身,其实还有很多可以研究的地方,以上只是本人粗浅的理解,后续将继续学习相关知识。 

=== 我是华丽的分隔线 === 

      什么是 overcommit?overcommit 中文翻译为“超售”。 在某些操作系统中(如 Solaris),malloc 函数返回为 null 则表示系统没有可分配的内存空间。但 Linux 的内存分配采取的是一种更加积极的分配策略,它假设应用申请了内存空间后并不会立即去使用它,所以允许一定量的超售,当应用真的需要使用它的时候,操作系统可能已经通过回收了其他应用的内存空间而变得有能力去满足这个应用的需求,简单的说,就是允许应用申请比实际可分配空间(包括物理内存和 Swap)更多的内存,这个特性称为 OverCommit 。 
上一篇:jquery ajax登录提交 页面跳转


下一篇:.NET面试基础知识之String(一)