Redis数据持久化到硬盘失败
在项目中用到了redis缓存,数据量8.5G,服务器内存16G,但是在redis持久化数据到硬盘上时失败了
![](https://i-blog.csdnimg.cn/blog_migrate/e2fb921e9130b5719abdba03765d4237.png)
注意标记为红色处,此处的意思是说:
Redis被配置为保存数据库快照,但它目前不能持久化到硬盘。用来修改集合数据的命令不能用。请查看Redis日志的详细错误信息。
原因分析
日志信息中说,数据持久化到硬盘上失败了。那么我们首先来了解一下什么时Redis中的持久化。持久化
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或者少量损失)数据。Redis中的持久化有RDB和AOF俩种方式。RDB
RDB持久化通过保存数据中的键值对来记录数据库状态的不同。将某个时间点上的数据库状态保存到一个RDB文件(经过压缩的二进制文件)中,通过这个文件可以还原数据库状态![](https://i-blog.csdnimg.cn/blog_migrate/3d54ac8c1a254dd63929128339893a42.png)
RDB持久化可以手动执行,也可以根据配置定期执行。
有俩个的Redis命令可以生成RDB文件,一个时SAVE,另外的一个是BGSAVE
SAVE命令会阻塞服务器进程,是同步的,不能处理任何命令请求
BGSAVE会派生出一个子进程,是异步的,服务器进程继续处理命令请求
自动间隔性保存:通过配置选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
BGSAVE会派生一个子进程执行RDB的持久化工作,当通过配置save选项设置多个保存条件,任意一个条件满足,服务器会自动执行BGSAVE命令。
Redis的服务器周期性操作函数serverCron默认每隔100毫秒会执行一次,对正在运行的服务器进行维护,它的其中一项工作就是检测save选项所设置的保存条件是否已经满足,如果满足就执行BGSAVE命令。
对于BGSAVE命令,在Redis中的配置如下:
![](https://i-blog.csdnimg.cn/blog_migrate/17c03a6c777a5d3b2caf86f2d95317d6.png)
当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:Redis 调用 fork() ,同时拥有父进程和子进程。子进程将数据集写入到一个临时 RDB 文件中。当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
对于使用BGSAVE命令,生成rdb文件,在Redis中的配置如下,配置成使用压缩:
![](https://i-blog.csdnimg.cn/blog_migrate/73c235a7cb11c2dd07b9c7d0add8426c.png)
AOF
AOF是持久化是通过保存Redis服务器所执行的写命令来记录数据库状态。关于AOF的持久化方式在这里不做过多的讨论。![](https://i-blog.csdnimg.cn/blog_migrate/9f82129b8a22c4c831e11fc464b0e2e8.png)
redis的相关的配置文件如下:
![](https://i-blog.csdnimg.cn/blog_migrate/17c648eeaf619488e9d92d8cda5c196e.png)
相关配置解释如下:
appendonly no 默认情况下,redis会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,而且备份也不能很频繁。如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失,所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式。开启appendonly模式之后,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中。当redis重新启动时,会从该文件恢复出之前的状态,但是这样会造成appendonly.aof文件过大,所以redis还支持了BGREWRITEAOF指令对appendonly.aof 进行重新整理,你可以同时开启asynchronous dumps 和 AOF;
appendfilename appendonly.aof AOF文件名称,默认为"appendonly.aof";
appendfsync everysec 支持三种同步AOF文件的策略: no代表不进行同步,系统去操作,always代表每次有写操作都进行同步,everysec代表对写操作进行累积,每秒同步一次,默认是"everysec",按照速度和安全折中这是最好的。
持久化到硬盘上失败的原因
个人感觉采用fork主进程的方式很拙劣,但似乎是唯一的方法。内存中的热数据随时可能修改,要在磁盘上保存某个时间的内存镜像必须要冻结。冻结就会导致假死。fork一个新的进程之后等于复制了当时的一个内存镜像,这样主进程上就不需要冻结,只要子进程上操作就可以了。在小内存的进程上做一个fork,不需要太多资源,但当这个进程的内存空间以G为单位时,fork就成为一件很恐怖的操作。何况在16G内存的主机上fork 14G内存的进程呢?肯定会报内存无法分配的。更可气的是,越是改动频繁的主机上fork也越频繁,fork操作本身的代价恐怕也不会比假死好多少。
所以简单的来说,Redis在保存数据到硬盘时为了避免主进程假死,需要Fork一份主进程,然后在Fork进程内完成数据保存到硬盘的操作,如果主进程使用了4GB的内存,Fork子进程的时候需要额外的4GB,此时内存就不够了,Fork失败,进而数据保存硬盘也失败了。
解决方法
方法一
查看日志文件:![](https://i-blog.csdnimg.cn/blog_migrate/dbc1b2bcf52e409d736e4832a960d8b5.png)
内核参数overcommit_memory,它是内存分配策略
可选值:0、1、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2, 表示内核允许分配超过所有物理内存和交换空间总和的内存
编辑/etc/sysctl.conf ,改vm.overcommit_memory=1
方法二
可以看到redis配置文件中有如下的配置:![](https://i-blog.csdnimg.cn/blog_migrate/aa27720d1b20b7006e7f74ba977465c7.png)
那么你可以将这个设置改成no,但是这样可能会将一些其它的key干掉,但是最后还是不能够正确将数据写入到硬盘上。不建议使用