阅读 1931

Redis数据丢失的问题

1. 错误回顾

今天跑项目时今天一直遇到Redis 报错
“MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk”。
复制代码

这个错误信息是Redis客户端工具在保存数据时候抛出的异常信息。

正常情况下,Redis定期回写磁盘的内存数据库,

Background saving started by pid 23823
DB saved on disk
RDB: 14MB of memory used by copy-on-write
复制代码

报错时,查看redis 日志文件,发现存储失败 Can’t save in background: fork: Cannot allocate memory

重启动时的日志:

[26641] 21 Dec 09:46:34 * Slave ask for synchronization

[26641] 21 Dec 09:46:34 * Starting BGSAVE for SYNC

[26641] 21 Dec 09:46:34 # Can’t save in background: fork: Cannot allocate memory

[26641] 21 Dec 09:46:34 * Replication failed, can’t BGSAVE

[26641] 21 Dec 09:46:34 # Received SIGTERM, scheduling shutdown…

[26641] 21 Dec 09:46:34 # User requested shutdown…

很明显的一个问题,系统不能在后台保存,fork进程失败。

[26641] 18 Dec 04:02:14 * 1 changes in 900 seconds. Saving…

[26641] 18 Dec 04:02:14 # Can’t save in background: fork: Cannot allocate memory

系统不能在后台保存,fork进程时无法指定内存。
复制代码

2. 修复方法

vim /etc/sysctl.conf 修改vm.overcommit_memory=1后问题果然解决了 sysctl -p 立即生效

可以用man sysctl 查看sysctl的使用

其实此问题在redis重启时,最开始的日志文件中会提示

3. 原因

Redis的数据回写机制分同步和异步两种,

同步回写即SAVE命令,主进程直接向磁盘回写数据。在数据大的情况下会导致系统假死很长时间,所以一般不是推荐的。 异步回写即BGSAVE命令,主进程fork后,复制自身并通过这个新的进程回写磁盘,回写结束后新进程自行关闭。由于这样做不需要主进程阻塞,系统不会假死,一般默认会采用这个方法。 个人感觉方法2采用fork主进程的方式很拙劣,但似乎是唯一的方法。内存中的热数据随时可能修改,要在磁盘上保存某个时间的内存镜像必须要冻结。冻结就会导致假死。fork一个新的进程之后等于复制了当时的一个内存镜像,这样主进程上就不需要冻结,只要子进程上操作就可以了。

在小内存的进程上做一个fork,不需要太多资源,但当这个进程的内存空间以G为单位时,fork就成为一件很恐怖的操作。何况在16G内存的主机上fork 14G内存的进程呢?肯定会报内存无法分配的。更可气的是,越是改动频繁的主机上fork也越频繁,fork操作本身的代价恐怕也不会比假死好多少。

找到原因之后,直接修改内核参数vm.overcommit_memory = 1

Linux内核会根据参数vm.overcommit_memory参数的设置决定是否放行。

如果 vm.overcommit_memory = 1,直接放行 vm.overcommit_memory = 0:则比较 此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上swap,决定是否放行。 vm.overcommit_memory = 2:则会比较 进程所有已分配的虚拟内存加上此次请求分配的虚拟内存和系统当前的空闲物理内存加上swap,决定是否放行。

4. redis 相关问题

项目在启动redis时,还是会复现此问题,通过 redis-cli -p 8790 进入redis后台。

  1. 输入monitor监控,发现数据量存入特别多,
  2. 输入keys *,发现里面存入了600多万条数据,
  3. 通过配置文件查找,redis落地的文件,dump.rdb ,该文件足足有4G! 最后删除该文件,然后重启redis,发现问题解决。

5. 教训

所有的redis 存入时,除非必要,否则要设置超时时间。 可以采用时间戳存储,最后定期清理。(该方法后续会完成

6. redis配置项

参考https://www.cnblogs.com/qq78292959/archive/2013/09/21/3331032.html

文章分类
后端