为什么 linux cp 命令不消耗磁盘 IO?

tai*_*yao 6 linux cp disk iostat

操作系统:centos7

测试文件:a.txt 1.2G

监控命令:iostat -xdm 1

The first scene:
cp a.txt b.txt        #b.txt is not exist
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

The second scene: 
cp a.txt b.txt        #b.txt is exist
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

为什么第一个场景不消耗IO,而第二个场景消耗IO?

Mic*_*Mol 7

很可能数据在第一次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 时,会立即考虑评估脏内存限制,而另一个在读取时显示为 0。

dirty_background_ratio

包含,作为包含空闲页面和可回收页面的总可用内存的百分比,后台内核刷新线程将开始写出脏数据的页面数。

总可用内存不等于总系统内存。

vm.dirty_bytesvm.dirty_ratio

除了这个门槛,还有第二个门槛。嗯,更多的是限制而不是阈值,它由vm.dirty_bytes和控制vm.dirty_ratio。同样,它没有正式名称,因此我们将其称为“Dirty Limit”。一旦“写入”了足够的数据,但没有提交到底层块设备,进一步的尝试write将不得不等待写入 I/O 完成。(他们必须等待哪些数据的确切细节我不清楚,可能是 I/O 调度程序的一个功能。我不知道。)

为什么?

磁盘很慢。旋转锈尤其如此,因此当磁盘上的 R/W 磁头移动以满足读取请求时,在读取请求完成并且可以启动写入请求之前,无法为写入请求提供服务。(反之亦然)

效率

这就是我们在内存中缓冲写入请求并缓存我们读取的数据的原因;我们将工作从慢速磁盘转移到更快的内存。当我们最终将数据提交到磁盘时,我们有大量的数据可以使用,我们可以尝试以最小化寻道时间的方式编写它。(如果您使用的是 SSD,请将磁盘寻道时间的概念替换为 SSD 块的重新刷新;重新刷新会消耗 SSD 寿命并且是一种缓慢的操作,SSD 试图--取得不同程度的成功--用自己的写入来隐藏缓存。)

我们可以在内核尝试使用vm.dirty_background_bytesvm.dirty_background_ratio

缓冲的写入数据太多!

如果您写入的数据量太大,无法以多快的速度到达磁盘,那么您最终将消耗所有系统内存。首先,您的读取缓存将消失,这意味着将从内存中处理更少的读取请求,而必须从磁盘提供服务,从而进一步减慢您的写入速度!如果你的写压力仍然没有减轻,最终就像内存分配将不得不等待你的写缓存被释放一些,这将更具破坏性。

所以我们有vm.dirty_bytes(和vm.dirty_ratio);它让我们说,“嘿,等一下,现在是我们将数据写入磁盘的时候了,以免情况变得更糟。”

数据还是太多

但是,强行停止 I/O 是非常具有破坏性的。从读取过程的角度来看,磁盘已经很慢了,刷新数据可能需要几秒钟到几分钟的时间;考虑vm.dirty_bytes20 的默认值。如果您的系统有 16GiB 的 RAM 且没有交换,您可能会发现在等待 3.4GiB 数据刷新到磁盘时 I/O 被阻塞。在具有 128GiB 内存的服务器上?当您等待 27.5GiB 的数据时,您将有服务超时!

因此保持vm.dirty_bytes(或者vm.dirty_ratio,如果您愿意)相当低是有帮助的,这样如果您达到这个硬阈值,它只会对您的服务造成最小的破坏。

什么是好的价值观?

使用这些可调参数,您总是在吞吐量和延迟之间进行权衡。缓冲太多,您将获得巨大的吞吐量,但延迟却很糟糕。缓冲区太少,你的吞吐量会很差,但延迟很大。

在只有单个磁盘的工作站和笔记本电脑上,我喜欢设置vm.dirty_background_bytes为 1MiB 左右,以及 8MiBvm.dirty_bytes和 16MiB 之间。对于单用户系统,我很少发现超过 16MiB 的吞吐量优势,但是对于任何同步工作负载(如 Web 浏览器数据存储),延迟挂断可能会变得非常糟糕。

在任何带有条带奇偶校验数组的东西上,我发现数组条带宽度的一些倍数是一个很好的起始值vm.dirty_background_bytes;它减少了在更新奇偶校验时需要执行读/更新/写序列的可能性,从而提高了阵列吞吐量。

对于vm.dirty_bytes,这取决于您的服务会遭受多少延迟。我自己,我喜欢计算块设备的理论吞吐量,用它来计算它在 100 毫秒左右可以移动多少数据,并设置vm.dirty_bytes相应的。100 毫秒的延迟是巨大的,但这并不是灾难性的(在我的环境中)。

不过,所有这些都取决于您的环境;这些只是寻找适合您的方法的起点。