10月,我将在纽约参加O'Reilly Velocity会议,并发表“当我们谈论磁盘IO时我们谈论的话题”的演讲 。 我决定将我的一些准备笔记发布为一系列博客文章。

了解IO的工作原理,使用哪种算法以及在什么情况下可以改善开发人员和操作员的生活:他们将能够预先做出更好的选择(基于他们正在评估的数据库所使用的内容),进行故障排除数据库行为不当(通过将其工作负载与打算使用数据库堆栈的工作负载进行比较)并调整其堆栈(通过分散负载,切换到不同的磁盘类型,文件或操作系统或仅选择文件)时的性能问题不同的索引类型)。

zabbix 磁盘iops 磁盘io在哪看_zabbix 磁盘iops

根据Wikipedia的IO风味

尽管经常讨论和讨论网络IO,但文件系统IO的关注却要少得多。 当然,在现代系统中,人们通常将数据库用作存储手段,因此应用程序通过网络上的驱动程序与它们进行通信。 我认为了解数据如何写入磁盘并从磁盘读回仍然很重要。 此外,网络IO还有更多的要讨论的内容,以及实现不同事物的方式,这在一个操作系统与另一个操作系统之间是非常不同的,而文件系统IO的工具集要少得多。

IO有多种“特色”(为简洁起见,一些功能被省略):

  • 系统调用: 打开 , 写入 , 读取 , fsync , 同步 , 关闭
  • 标准IO: fopen , fwrite , fread , fflush , fclose
  • 向量IO: writev可以获得 , readv
  • 内存映射IO: open , mmap , msync , munmap

今天,我们将讨论标准IO与一系列“用户区”优化的结合。 在大多数情况下,应用程序开发人员都在使用它,并在此之上加上几个不同的标志。 让我们开始吧。

缓冲IO

在谈论stdio.h函数时,在“缓冲”方面存在一些混乱,因为它们自己进行一些缓冲。 使用标准IO时,可以在全缓冲和行缓冲之间进行选择,也可以从任何缓冲中退出 。 这种“用户空间”缓冲与内核后面的缓冲无关。 您还可以考虑将“缓冲”和“缓存”区分开来,这应该使概念不同且直观。

磁盘(HDD,SSD)称为块设备 ,其上最小的可寻址单元称为扇区:无法传输小于扇区大小的数据量。 同样,文件系统的最小可寻址单元是一个块 (通常大于扇区)。 块大小通常小于(或等于) 页面大小 (概念来自虚拟内存)。

我们试图在磁盘上处理的所有内容最终都被加载到RAM中,最有可能由操作系统在两者之间进行缓存。

页面缓存

页面缓存 (以前完全分开,缓冲区缓存和页面缓存在2.4 Linux内核中是统一的 ) 帮助保持缓存更可能在最近的时间访问的缓冲区。 时间局部性原则意味着读取的页面将在短时间内访问多次,空间局部性意味着相关元素很可能彼此靠近,因此保存数据以进行摊销是有意义的IO成本。 为了提高IO性能,内核通过延迟写入和合并相邻的读取在内部缓冲数据。

页面缓存不一定包含整个文件(尽管肯定会发生)。 根据文件大小和访问模式,仅最近访问的块。 由于所有IO操作都是通过Cache发生的,因此诸如读写操作之类的操作序列可以完全由内存提供,而无需访问磁盘上的(同时过时的)数据。

执行读取操作时,将首先查询页面缓存。 如果数据已经可以在页面缓存中找到,则将其复制给用户。 否则,它将从磁盘加载并存储在页面缓存中以供进一步访问。 执行写操作时,页面将首先写入高速缓存,并在高速缓存中标记为脏。

标记为脏的页面(由于其缓存的表示形式现在与持久的页面不同)将被刷新到磁盘。 此过程称为writeback 。 当然,回写有其自身的潜在弊端,例如排队过多的IO请求,因此值得了解使用回写时使用的阈值和比率,并检查队列深度以确保可以避免节流和高延迟。

延迟错误

执行由内核和/或库缓冲区支持的写操作时,重要的是要确保数据实际到达磁盘,因为它可能在某个地方缓冲或缓存。 当数据刷新到磁盘时(在同步或关闭文件时),将出现错误。

直接IO

O_DIRECT是打开文件时可以传递的标志。 它指示操作系统绕过页面缓存。 这一切都意味着对于使用Direct IO的“传统”应用程序,很可能会导致性能下降而不是加速。

内核开发人员常常不赞成使用Direct IO,而且到目前为止,Linux手册页还引用了Linus Torwalds:“ 关于O_DIRECT,一直困扰我的是整个接口都是愚蠢的 ”。

但是,诸如PostgreSQL和MySQL之类的数据库使用Direct IO是有原因的。 开发人员可以使用自定义IO调度程序和特定于应用程序的缓冲区缓存来确保对数据访问模式进行更细粒度的控制。 例如,PostgreSQL使用Direct IO进行WAL (预写日志),因为他们必须在确保持久性的同时尽可能快地执行写入操作,并且可以使用此优化方法,因为他们知道不会立即重用数据,因此绕过内核页面缓存编写它不会导致性能下降。

即使数据最近被访问并且可能位于缓存中,直接读取也将直接从磁盘进行读取。 这有助于避免创建额外的数据副本。 写操作也是如此:执行写操作时,直接从用户空间缓冲区执行写操作。

块对齐

因为DMA(直接内存访问)使请求直接绕过中间内核缓冲区而直接发送到后备存储,所以要求所有操作都按扇区对齐(对齐512B边界)。 换句话说,每个操作的起始偏移量必须为512的倍数,缓冲区大小也必须为512的倍数。

例如,RocksDB 通过预先检查操作来确保操作是按块对齐的 (较旧的版本通过在后台对齐来允许未对齐的访问)。

无论是否使用O_DIRECT标志,确保读写块对齐始终是一个好主意:进行未对齐的访问将导致从磁盘加载多个扇区(或写回磁盘)。

使用块大小或恰好适合块内部的值可以保证块对齐的I / O请求,并防止内核内部的多余工作。

非阻塞文件系统IO

我在这里添加此部分是因为我经常听到文件系统IO上下文中的“非阻塞”消息。 这是很正常的,因为网络和文件系统IO的大多数编程接口都是相同的。 但是值得一提的是, 没有真正的“非阻塞” IO可以用同样的方式理解。

对于常规文件(在磁盘上),通常会忽略O_NONBLOCK,因为块设备操作通常被认为是非阻塞的(例如,与套接字不同)。 系统不考虑文件系统IO延迟。 之所以做出此决定,是因为数据到达的时间或多或少受到限制。

出于相同的原因,通常会使用诸如select和epoll之类的内容禁止监视和/或检查常规文件的状态。

结束语

考虑到要覆盖的材料太多,很难找到最佳的帖子大小,但是在移动到mmap和矢量IO之前,有必要在获得标准IO之后进行清理。

如果您发现任何要添加的内容或我的帖子中有错误