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

标签: | 发表时间:2020-06-15 07:36 | 作者:
出处:https://www.cnblogs.com

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

MISCONF Redisisconfigured to save RDB snapshots, but itiscurrently not able to persist on disk. Commands that may modify the datasetare disabled,        
becausethisinstanceisconfigured to report errors during writesifRDB snapshotting fails (stop-writes-on-bgsave-error option).
Please check the Redis logsfordetails about the RDB error.

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

420:M14Sep15:56:27.067# Can't save in background: fork: Cannot allocate memory420:M14Sep15:56:33.071*10000changesin60seconds. Saving...420:M14Sep15:56:33.072# Can't save in background: fork: Cannot allocate memory420:M14Sep15:56:39.079*10000changesin60seconds. Saving...420:M14Sep15:56:39.080# Can't save in background: fork: Cannot allocate memory420:M14Sep15:56:45.083*10000changesin60seconds. Saving...420:M14Sep15:56:45.083# Can't save in background: fork: Cannot allocate memory420:M14Sep15:56:51.094*10000changesin60seconds. Saving...420:M14Sep15:56:51.095# Can't save in background: fork: Cannot allocate memory420:M14Sep15:56:57.002*10000changesin60seconds. 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博主「朱清震」的原创文章,遵循 CC4.0BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zqz_zqz/article/details/53384854

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

/etc/sysctl.conf
vm.overcommit_memory=1sysctl-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

 

相关 [redis bgsave 内存] 推荐:

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

- -
中秋的时候正在外面愉快的在外卖喝着咖啡玩电脑. 突发 redis 报警从 sentry 应用端曝出的错误. 于是又开始愉快的处理问题了,看上去像是执行 rdb 快照持久化的时候出现的问题,上到 redis 机器查看日志定位详细问题. 可以很明显的发现应该是尝试 fork 的时候内存不够,并没有被 linux 内核放行.

linux下redis执行bgsave时,报overcommit_memory错误问题

- - 博学无忧
一台机器如果内存用完,在进行bgsave时,可能会报错. 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.

Redis内存存储结构分析

- 小丑鱼 - 搜索技术博客-淘宝
Redis 内存存储结构. 本文是基于 Redis-v2.2.4 版本进行分析.. 1.1 Redis 内存存储总体结构. Redis 是支持多key-value数据库(表)的,并用 RedisDb 来表示一个key-value数据库(表). redisServer 中有一个 redisDb *db; 成员变量, RedisServer 在初始化时,会根据配置文件的 db 数量来创建一个 redisDb 数组.

Redis源码分析-内存分配

- gOODiDEA - NoSQLFan
本文转载自Day Day Up博客,文章对Redis的内存分配封装zmalloc库进行了分析,描述了Redis在内存分配和使用统计方面的各种细节和技巧. 原文链接:blog.ddup.us. Redis中到处都会进行内存分配操作. 为了屏蔽不同平台之间的差异,以及统计内存占用量等,Redis对内存分配函数进行了一层封装,程序中统一使用zmalloc,zfree一系列函数,位于zmalloc.h,zmalloc.c文中.

转:Redis学习手册(内存优化)

- - 膘叔
项目中在使用Redis,自然对内存这玩意相对比较看重一点,虽然内存不值钱,但能节约一点内存也就意味着可以存储更多的东西. 所以在博客园看到这篇文章的时候,不管现在是用了没,先备份一下资料,我英文烂 ,有点英文资料虽然能看懂,但毕竟不是母语,看起来累. 内容详细如下:http://www.cnblogs.com/stephen-liu74/archive/2012/04/11/2370521.html.

Redis内存使用优化与存储

- - 企业架构 - ITeye博客
传统MySQL+ Memcached架构遇到的问题. 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,我们遇到了很多问题:. MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间.

别再问我Redis内存满了该怎么办了

- - IT瘾-tuicool
Redis的文章,我之前写过一篇关于 「Redis的缓存的三大问题」,累计阅读也快800了,对于还只有3k左右的粉丝量,能够达到这个阅读量,已经是比较难了. 这说明那篇文章写的还过得去,收到很多人的阅读肯定,感兴趣的看一下[ 看完这篇Redis缓存三大问题,保你能和面试官互扯. 「三大缓存问题」只是Redis的其中的一小部分的知识点,想要深入学习Redis还要学习比较多的知识点.

Redis 负载监控——redis-monitor

- - ITeye资讯频道
redis-monitor是一个Web可视化的 redis 监控程序. 使用 Flask 来开发的,代码结构非常简单,适合移植到公司内网使用. redis 服务器信息,包括 redis 版本、上线时间、 os 系统信息等等. 实时的消息处理信息,例如处理 command 数量、连接总数量等. 内存占用、 cpu 消耗实时动态图表.

Redis 起步

- - 博客园_首页
Rdis和JQuery一样是纯粹为应用而产生的,这里记录的是在CentOS 5.7上学习入门文章:. Redis是一个key-value存储系统. 和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型.

redis 配置

- - 谁主沉浮
# 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写). # 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB. # daemonize no 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes. # 当redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid,你可以配置到其他地址.