在这里我将解释一些事情:什么是写时复制(CoW)以及它如何消耗内存,为什么设置'vm.overcommit_memory = 1'不会帮助解决内存使用和性能问题,以及支持的最佳实践起来 Redis 数据。
Copy-on-Write 及其内存使用情况
Redis 的快照备份利用现代操作系统提供的 CoW 语义,解决了在 fork 进程时,父进程的内存被复制到子进程从而使内存占用加倍的问题。在 CoW 中,分叉的子进程将共享父进程的原始内存空间。它仅在任一进程修改该内存页面时复制该内存页面。下面是数据修改前和数据修改后的内存空间示意图:
当 Redis 的 RDB 备份正在进行时,在父进程中会发生数据变化,它正在接受来自客户端的新请求并在内存中处理它。如果 QPS 很高,父进程将在子进程的备份时间内复制大量内存页面以用于新的更改。因此父进程将消耗额外的内存。在极端情况下,如果所有的内存页面都被修改了,那么 Redis 实例的内存占用会翻倍。是的,内存翻倍是有可能的,这个事实就解释了为什么Redis提供了“overcommit_memory=1”选项,能解决什么问题,不能解决什么问题(减少内存使用)。
“vm.overcommit_memory = 1”是什么,它解决了什么问题
在 RDB 备份期间,您可能会看到这样的日志错误:
10202:M 13 Sep 11:34:16.535 # Can't save in background: fork: Cannot allocate memory
它表示没有足够的内存来分叉子进程进行备份。如果 Redis 进程现在消耗了 2GB 内存,在 fork 子进程时,操作系统会假设你还有 ANOTHER 2GB 内存,这样在 CoW 的极端情况下,就有足够的内存来复制所有脏内存页。即使在 fork 子进程时还没有使用额外的内存,它也会检查空闲内存以避免以后的内存不足错误。在 Redis 日志中,它提供了解决方案:
10202:M 13 Sep 11:33:09.943 # WARNING overcommit_memory is set to 0! Background
save may fail under low memory condition. 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.
因此,设置 'vm.overcommit_memory = 1' 将允许您在空闲内存不足时分叉子进程。如果你知道备份过程中的脏内存页不会太多,就不会有任何实际问题,因为每次发生新的 CoW 操作时都会成功分配内存。
并且,'vm.overcommit_memory = 1'只保证你可以fork子进程来备份Redis数据,但如果父进程一直在进行写操作,它并不能减少内存使用。
Redis备份实践
Redis内存数据的持久化方式有三种:RDB(snapshotting),AOF,以及两者的混合。无论您如何配置设置,任何方法都会在一定程度上影响服务器响应时间。为了尽量减少持久化进程的影响,我们通常在从属实例中运行备份,而不是在主实例上运行。但是,如果我们在奴隶上这样做,就会有新的风险。当有网络分区时发生这种情况时,slave 可能无法保持最新状态,因此在 slave 上备份可能会丢失一些数据。一种解决方案是拥有多个从属服务器,因此降低了所有从属服务器与主实例不同步的可能性。另一个预防措施是建立强大的监控系统,以便我们可以更快地发现网络问题并减少网络分区的时间跨度。