最近生产环境中某个Set的Redis集群经常出现短暂的内存降低现象,经过查看日志是因为在RDB持久化所造成的内存突降(日志中:RDB: 4929 MB of memory used by copy-on-write ),其根本原理是RDB持久化的过程中,Redis借助操作系统提供的写时复制技术(Copy-On-Write,COW),在执行bgsave(snapshot)快照的同时,能够处理正常的写请求。
如果主线程要修改一块数据,那么这块数据就会被复制一份,生成该数据的副本。然后bgsave 子进程会把这个副本写入正在持久化的RDB文件,而在这个过程中,主线程仍然可以直接修改原来的数据。另外,子进程是会复制一份和主进程一模一样的虚拟页表来映射内存,保证持久化文件的完整性。
正因为数据会被额外的复制一份,所以会占用额外的内存,当在进行RDB持久化操作的过程中,与此同时如果持续往redis中写入的数据量越多,就会导致占用的额外内存消耗越大。
那么在此期间写入的数据去哪了呢?
写入的数据还是存在了内存当中,并没有写入当前的持久化文件中,等到下次进行RDB持久化时才会把 ” 写入的数据 ” 落盘到RDB文件中。
bgsave:fork出的子进程开始根据父进程内存数据生成临时的快照文件,然后替换原文件。
这里解释一下几个跟 RDB 相关的参数:
根据 rdb_bgsave_in_progress 这一项为 0,可以判断在执行 info Persistence 命令时,bgsave 已经执行完成了。除了通过命令的方式触发 RDB 持久化之外,Redis 内部还有自动触发 RDB 的机制。比如以下场景:
如果频繁执行全量快照,会带来两方面的开销:
当遇到 RDB 所在分区磁盘满了,可以临时修改 RDB 路径,操作如下:
Redis 支持对 RDB 进行压缩,参数为 rdbcompression,设置为 yes 表示开启(默认开启的)。压缩不但可以节省磁盘空间,在创建主从时,也能更快的将全量备份传给从实例,因此建议开启压缩功能。
当发现 Reids RDB 文件损坏时,可以使用 redis-check-rdb 进行检测,用法如下:
RDB looks OK! 说明rdb文件没有错误。
有些情况,我们会在单台服务器上部署多个 Redis 实例,但是使用配置文件中增加 save 的方式又怕几个实例 RDB 时间冲突,从而影响落盘速度。这种情况,可以使用脚本结合定时任务触发 bgsave 进行 RDB 备份。这样,同机器不同实例的 RDB 备份时间可以自定义错开,防止 IO 跑满带来的问题。(注意一定要设置好持久化的目录,防止多个实例共用同一目录)
那么 Redis 究竟怎么备份更好呢?RDB 尽管恢复会快很多,但是可靠性比 AOF 低,但是如果只使用 AOF,又会存在恢复慢的问题,因此,Redis 4.0 提出了混合使用 AOF 日志和内存快照的方法。因此对于 Redis 的备份,建议如下:
当然,如果有从实例,也优先考虑在从实例上进行备份。