Redis BGSAVE因为内存不足 fork 失败导致目标 Redis 无法访问的问题

中秋的时候正在外面愉快的在外卖喝着咖啡玩电脑。。。。。。突发 redis 报警从 sentry 应用端曝出的错误

MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.

于是又开始愉快的处理问题了,看上去像是执行 rdb 快照持久化的时候出现的问题,上到 redis 机器查看日志定位详细问题

420:M 14 Sep 15:56:27.067 # Can‘t save in background: fork: Cannot allocate memory
420:M 14 Sep 15:56:33.071 * 10000 changes in 60 seconds. Saving...
420:M 14 Sep 15:56:33.072 # Can‘t save in background: fork: Cannot allocate memory
420:M 14 Sep 15:56:39.079 * 10000 changes in 60 seconds. Saving...
420:M 14 Sep 15:56:39.080 # Can‘t save in background: fork: Cannot allocate memory
420:M 14 Sep 15:56:45.083 * 10000 changes in 60 seconds. Saving...
420:M 14 Sep 15:56:45.083 # Can‘t save in background: fork: Cannot allocate memory
420:M 14 Sep 15:56:51.094 * 10000 changes in 60 seconds. Saving...
420:M 14 Sep 15:56:51.095 # Can‘t save in background: fork: Cannot allocate memory
420:M 14 Sep 15:56:57.002 * 10000 changes in 60 seconds. Saving...

可以很明显的发现应该是尝试 fork 的时候内存不够,并没有被 linux 内核放行。

这里有两个点我认为需要注意一下,一个是 redis 在默认配置的情况是下是开启参数

stop-writes-on-bgsave-error yes

也就是 如果 bgsave 存储快照失败,那么 redis 将阻止数据继续写入,如果将这个设置成 False 那么即使是 bgsave 快照写入磁盘失败,也不会让 redis 立即对外停止服务。

但是无法 bgsave 让数据落盘始终是隐患,要是机器一重启,就完蛋了。所以我尝试查询一些热修复的手段来修复这个问题。

最终 linux 端有一个参数 vm.overcommit_memory 可以解决这个问题默认参数是 0 ,它有三个值可以配置。

这时候就是内存不足,到了这里,操作系统要怎么办,就要祭出我们的主角“overcommit_memory”参数了(/proc/sys/vm/overcommit_memory);

vm.overcommit_memory = 0   启发策略
比较 此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上swap,决定是否放行。系统在为应用进程分配虚拟地址空间时,会判断当前申请的虚拟地址空间大小是否超过剩余内存大小,如果超过,则虚拟地址空间分配失败。因此,也就是如果进程本身占用的虚拟地址空间比较大或者剩余内存比较小时,fork、malloc等调用可能会失败。

vm.overcommit_memory = 1 允许overcommit
直接放行,系统在为应用进程分配虚拟地址空间时,完全不进行限制,这种情况下,避免了fork可能产生的失败,但由于malloc是先分配虚拟地址空间,而后通过异常陷入内核分配真正的物理内存,在内存不足的情况下,这相当于完全屏蔽了应用进程对系统内存状态的感知,即malloc总是能成功,一旦内存不足,会引起系统OOM杀进程,应用程序对于这种后果是无法预测的。

vm.overcommit_memory = 2 禁止overcommit
根据系统内存状态确定了虚拟地址空间的上限,由于很多情况下,进程的虚拟地址空间占用远大于其实际占用的物理内存,这样一旦内存使用量上去以后,对于一些动态产生的进程(需要复制父进程地址空间)则很容易创建失败,如果业务过程没有过多的这种动态申请内存或者创建子进程,则影响不大,否则会产生比较大的影响 。这种情况下系统所能分配的内存不会超过上面提到的CommitLimit大小,如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。
————————————————
版权声明:本文为CSDN博主「朱清震」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zqz_zqz/article/details/53384854

所以这里 bgsave 我们 redis 应用会尝试对主进程进行 fork ,然后内存不够申请未被内核放行。所以 hotfix 我尝试将参数 vm.overcommit_memory 设置成 1 直接进行放行。

/etc/sysctl.conf
vm.overcommit_memory=1
sysctl -p

生效,再看日志发现就可以成功了。

这里我找到官方 FAQ 也对类似问题有描述

Background saving fails with a fork() error under Linux even if I have a lot of free RAM!

Short answer: echo 1 > /proc/sys/vm/overcommit_memory :)

And now the long one:

Redis background saving schema relies on the copy-on-write semantic of fork in modern operating systems: Redis forks (creates a child process) that is an exact copy of the parent. The child process dumps the DB on disk and finally exits. In theory the child should use as much memory as the parent being a copy, but actually thanks to the copy-on-write semantic implemented by most modern operating systems the parent and child process will share the common memory pages. A page will be duplicated only when it changes in the child or in the parent. Since in theory all the pages may change while the child process is saving, Linux can‘t tell in advance how much memory the child will take, so if the overcommit_memory setting is set to zero fork will fail unless there is as much free RAM as required to really duplicate all the parent memory pages, with the result that if you have a Redis dataset of 3 GB and just 2 GB of free memory it will fail.

Setting overcommit_memory to 1 tells Linux to relax and perform the fork in a more optimistic allocation fashion, and this is indeed what you want for Redis.

A good source to understand how Linux Virtual Memory works and other alternatives for overcommit_memory and overcommit_ratio is this classic from Red Hat Magazine, "Understanding Virtual Memory". Beware, this article had 1 and 2 configuration values for overcommit_memory reversed: refer to the proc(5) man page for the right meaning of the available values.

后来 hotfix 之后,我们清理了一些很久未能释放的大 key,将内存恢复到比较小的水平。就很稳了,这次问题发生之后没有无脑进行重启,而是迅速通过一定的思路来查询问题,感觉自己解决问题的方法稍微成熟了一点点。

Reference:

https://zhuanlan.zhihu.com/p/36872365    fork 的原理及实现

https://stackoverflow.com/questions/11752544/redis-bgsave-failed-because-fork-cannot-allocate-memory    redis bgsave failed because fork Cannot allocate memory

https://www.freebsd.org/doc/zh_CN/books/handbook/configtuning-sysctl.html    12.11. 用 sysctl 进行调整

https://blog.csdn.net/zqz_zqz/article/details/53384854    redis Can’t save in background: fork: Cannot allocate memory 解决及原理

https://redis.io/topics/faq    官方 FAQ

原文地址:https://www.cnblogs.com/piperck/p/11529874.html

时间: 2024-07-31 10:54:00

Redis BGSAVE因为内存不足 fork 失败导致目标 Redis 无法访问的问题的相关文章

Redis 数据结构与内存管理策略(下)

Redis 数据结构与内存管理策略(下) Redis 数据类型特点与使用场景 String.List.Hash.Set.Zset 案例:沪江团购系统大促 hot-top 接口 cache 设计 Redis 内存数据结构与编码 OBJECT encoding key.DEBUG OBJECT key 简单动态字符串(simple dynamic string) 链表(linked list) 字典(dict) 跳表(skip list) 整数集合(int set) 压缩表(zip list) Re

redis占用高内存导致的socket连接失败

最近在搞ELK日志平台,部署filebeat收集日志时(输出到redis),出现了经典的[连接被目标机器积极拒绝]异常, 1)环境配置: 开发机,开发服务机(开发机上的virtualbox虚拟机,启动了ELK服务).测试机.测试服务机(ELK) 2)本机(开发机)使用可输出日志到开发服务机 + 测试服务机 3)测试机可输出日志到开发服务机,配置为测试服务机则提示 [2017-04-07T13:39:52+08:00 ERR Connecting error publishing events (

redis翻译_内存优化

Special encoding of small aggregate data types 特别编码小集合的数据类型 Since Redis 2.2 many data types are optimized to use less space up to a certain size. Hashes, Lists, Sets composed of just integers, and Sorted Sets, when smaller than a given number of elem

[bug]WCF 内存入口检查失败 Memory gates checking failed

bug描述 异常信息:内存入口检查失败,因为可用内存(xxx 字节)少于总内存的 xx%.因此,该服务不可用于传入的请求.若要解决此问题,请减少计算机上的负载,或调整 serviceHostingEnvironment 配置元素上的 minFreeMemoryPercentageToActivateService 的值. 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: System.Insuffic

Redis Cluster机器内存充爆处理

机器配置 系统:CentOS6.7 配置:4C8G 应用:Redis Cluster,实例化 现象 1.无法启动redis,启动后系统OOM,直接杀死 2.Redis: OOM command not allowed when used memory > 'maxmemory' 原因 内存已满,不允许数据在写入 注: used_memory_human表示已用内存 used_memory_rss表示系统给redis分配的内存(即常驻内存) mem_fragmentation_ratio=used

设置Redis最大占用内存

https://blog.csdn.net/happyrabbit456/article/details/54945667 Redis需要设置最大占用内存吗?如果Redis内存使用超出了设置的最大值会怎样? 设置Redis最大占用内存 Redis设置最大占用内存,打开redis配置文件,找到如下段落,设置maxmemory参数,maxmemory是bytes字节类型,注意转换.修改如下所示: Vim 1 2 3 4 5 6 # In short... if you have slaves att

redis 如何做内存优化?

edis所有的数据都在内存中,而内存又是非常宝贵的资源.对于如何优化内存使用一直是Redis用户非常关注的问题.本文让我们深入到Redis细节中,学习内存优化的技巧.分为如下几个部分: 一.redisObject对象 二.缩减键值对象 三.共享对象池 四.字符串优化 五.编码优化 六.控制key的数量 一. redisObject对象 Redis存储的所有值对象在内部定义为redisObject结构体,内部结构如下图所示. Redis存储的数据都使用redisObject来封装,包括string

分布式缓存技术redis学习系列(二)——详细讲解redis数据结构(内存模型)以及常用命令

Redis数据类型 与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String.List.Hash.Set和Sorted Set. Redis数据类型内存结构分析 Redis内部使用一个redisObject对象来表示所有的key和value.redisObject主要的信息包括数据类型(type).编码方式(encoding).数据指针(ptr).虚拟内存(vm)等.type代表一个value对象具体是何种数

说下Redis采用不同内存分配器

参考文章: http://blog.sina.com.cn/s/blog_51df3eae01016peu.html 我们知道Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西.所以系统内存分配器的性能及碎片率会对Redis造成一些性能上的影响. 在Redis的 zmalloc.c 源码中,我们可以看到如下代码: 49 #if defined(USE_TCMALLOC) 50 #define malloc(size) tc_malloc(size) 51 #define