【问题标题】:what is copy-on-write memory什么是写时复制内存
【发布时间】:2014-06-04 18:16:52
【问题描述】:

随着我不断向redis写入数据,copy-on-write使用的内存不断增加。即使我将程序写入足够长的睡眠时间,以便 redis 能够完成所有后台保存(最后的内存消息是写时复制使用的 0 MB 内存),下一次后台保存将回到高号码。

例子,

cow 使用了 1300MB 内存

cow 使用了 1400MB 内存

牛使用0MB内存

cow 使用了 1500MB 内存

所有这些到底是什么意思?据我所知,如果写时复制内存不断增加,那么内存就没有足够的空间。此外,每次使用高内存的后台保存时,redis 似乎都无法正常工作。 Jedis 总是遇到套接字超时异常。

【问题讨论】:

  • 您查看了this 博客吗?你设置vm.overcommit_memory = 1了吗?
  • 是的,我确实设置了 overcommit = 1,appendonly 是,appendfsync 否
  • 很有趣,但我怕是忍不住。你也可以在redis google group 上提问,这有助于更好地解决技术问题。新用户有一两天的等待时间/批准计划。如果您在问题中提供 redis 版本和操作系统规范,它确实会有所帮助。

标签: redis jedis copy-on-write


【解决方案1】:

在这里我将解释一些事情:Copy-on-Write (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

这表示没有足够的内存来fork子进程进行备份。如果 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 备份实践

persisting the Redis memory data有三种方式:RDB(snapshotting),AOF,以及两者的混合。无论您如何配置设置,任何方法都会在一定程度上影响服务器响应时间。为了尽量减少持久化进程的影响,我们通常在从属实例中运行备份,而不是在主实例上运行。但是,如果我们在奴隶上这样做,就会有新的风险。当network partitions 发生时,slave 可能无法保持最新​​状态,因此在 slave 上备份可能会丢失一些数据。一种解决方案是拥有多个从属服务器,因此降低了所有从属服务器与主实例不同步的可能性。另一个预防措施是建立强大的监控系统,以便我们可以更快地发现网络问题并减少网络分区的时间跨度。

【讨论】:

    【解决方案2】:

    来自 Redis 常见问题解答:

    Redis 后台保存模式依赖于现代操作系统中 fork 的写时复制语义:Redis fork(创建子进程)是父进程的精确副本。子进程将数据库转储到磁盘上并最终退出。理论上,子进程应该使用与作为副本的父进程一样多的内存,但实际上由于大多数现代操作系统实现的写时复制语义,父进程和子进程将共享公共内存页。仅当页面在子级或父级中发生更改时,才会复制页面。因为从理论上讲,所有页面都可能在子进程保存时发生变化。

    由于写入时复制 (COW) 机制,保存过程中内存使用量的增加取决于转储过程中执行的写入次数。

    您可以做的是,配置一个 Redis 从站并将持久性任务委托给它。

    【讨论】:

      猜你喜欢
      • 2010-10-12
      • 2020-02-11
      • 2018-11-03
      • 2012-04-30
      • 2012-02-28
      • 2021-10-23
      • 2014-10-24
      • 2018-06-22
      • 2019-02-06
      相关资源
      最近更新 更多