答案1
很可能数据在第一次cp
操作期间并未刷新到磁盘,而是在第二次操作期间刷新到磁盘。
尝试设置vm.dirty_background_bytes
为较小的值,例如 1048576 (1 MiB),看看是否是这种情况; run sysctl -w vm.dirty_background_bytes=1048576
,然后你的第一个cp
场景应该显示 I/O。
这里发生了什么?
除了同步和/或直接 I/O 的情况外,对磁盘的写入都会在内存中缓冲,直到达到阈值,此时它们开始在后台刷新到磁盘。该阈值没有正式名称,但它由vm.dirty_background_bytes
和控制vm.dirty_background_ratio
,因此我将其称为“脏背景阈值”。来自内核文档:
vm.dirty_background_bytes
包含后台内核刷新器线程将开始写回的脏内存量。
笔记:
dirty_background_bytes
是 的对应项dirty_background_ratio
。一次只能指定其中之一。当写入一个 sysctl 时,会立即考虑评估脏内存限制,而另一个 sysctl 在读取时显示为 0。
dirty_background_ratio
包含(以包含空闲页和可回收页的总可用内存的百分比)后台内核刷新器线程将开始写出脏数据的页数。
总可用内存不等于总系统内存。
vm.dirty_bytes
和vm.dirty_ratio
除了这个门槛之外,还有第二个门槛。嗯,与其说是阈值,不如说是限制,它由vm.dirty_bytes
和控制vm.dirty_ratio
。同样,它没有正式名称,因此我们将其称为“脏限制”。一旦足够的数据被“写入”,但未提交到底层块设备,进一步的尝试将write
必须等待写入 I/O 完成。 (我不清楚他们必须等待哪些数据的确切细节,可能是 I/O 调度程序的函数。我不知道。)
为什么?
磁盘速度很慢。旋转锈尤其如此,因此当磁盘上的读/写头移动以满足读取请求时,在读取请求完成并且可以开始写入请求之前,无法处理写入请求。 (反之亦然)
效率
这就是为什么我们在内存中缓冲写入请求并缓存我们读取的数据;我们将工作从慢速磁盘转移到更快的内存。当我们最终将数据提交到磁盘时,我们有大量的数据可供使用,我们可以尝试以最小化寻道时间的方式写入它。 (如果您使用的是 SSD,请用 SSD 块的重新刷新来代替磁盘寻道时间的概念;重新刷新会消耗 SSD 的寿命,并且是一个缓慢的操作,SSD 试图通过自己的写入来隐藏该操作(取得了不同程度的成功)缓存。)
vm.dirty_background_bytes
我们可以使用和调整内核尝试将数据写入磁盘之前缓冲的数据量vm.dirty_background_ratio
。
缓冲写入数据过多!
如果您写入的数据量对于其到达磁盘的速度来说太大,您最终将耗尽所有系统内存。首先,您的读取缓存将消失,这意味着从内存提供服务的读取请求将减少,并且必须从磁盘提供服务,从而进一步减慢写入速度!如果您的写入压力仍然没有减轻,最终内存分配将不得不等待您的写入缓存释放一些,这将更具破坏性。
所以我们有vm.dirty_bytes
(和vm.dirty_ratio
);它让我们说:“嘿,等一下,我们真的是时候将数据写入磁盘了,以免情况变得更糟。”
数据仍然太多
不过,对 I/O 进行硬停止是非常具有破坏性的;从读取过程来看磁盘已经很慢了,可能需要几秒到几秒的时间分钟以便刷新该数据;考虑vm.dirty_bytes
默认值为 20。如果您的系统具有 16GiB RAM 并且没有交换区,则在等待 3.4GiB 数据刷新到磁盘时,您可能会发现 I/O 被阻塞。在具有 128GiB RAM 的服务器上?当您等待 27.5GiB 数据时,服务将会超时!
因此,保持vm.dirty_bytes
(或者vm.dirty_ratio
,如果您愿意的话)相当低是有帮助的,这样如果您打这个硬门槛,只会对您的服务造成最小程度的破坏。
什么是好的价值观?
通过这些可调参数,您始终在吞吐量和延迟之间进行权衡。缓冲太多,您将获得巨大的吞吐量,但延迟却很糟糕。缓冲区太少,吞吐量会很差,但延迟会很大。
在只有单个磁盘的工作站和笔记本电脑上,我喜欢设置vm.dirty_background_bytes
为 1MiB 左右,以及vm.dirty_bytes
8MiB 和 16MiB 之间。我很少发现单用户系统的吞吐量超过 16MiB,但对于 Web 浏览器数据存储等任何同步工作负载来说,延迟挂起可能会变得非常糟糕。
在任何带有条带奇偶校验数组的东西上,我发现数组条带宽度的一些倍数是一个很好的起始值vm.dirty_background_bytes
;它减少了在更新奇偶校验时需要执行读/更新/写序列的可能性,从而提高了阵列吞吐量。
对于vm.dirty_bytes
,这取决于您的服务可能遭受多少延迟。我自己喜欢计算块设备的理论吞吐量,用它来计算它在 100 毫秒左右可以移动多少数据,并进行vm.dirty_bytes
相应的设置。 100 毫秒的延迟是巨大的,但它并不是灾难性的(在我的环境中)。
不过,所有这些都取决于您的环境;这些只是寻找最适合您的方法的起点。