Direct IO 直接IO

介绍

直接I/O是一个系统范围的特性,它支持绕过系统页面缓存直接从/从存储设备到/从用户内存空间进行读写。 缓冲I/O通常是大多数操作系统启用的默认I/O模式。

我们为什么需要它?

使用缓冲的I/O,由于页面缓存作为存储和内存之间的代理,数据在存储和内存之间复制了两次。 在大多数情况下,引入页面缓存可以获得更好的性能。但对于self-caching RocksDB等应用程序,应用程序本身应该有知识的逻辑语义的数据比操作系统,应用程序提供了一个机会, 可以实现更高效的缓存替换算法与任何应用程序定义的数据块作为一个单元通过利用他们的知识的数据语义。 另一方面,在某些情况下,我们希望某些数据选择退出系统缓存。此时,直接I/O将是更好的选择。

实现

启用直接I/O的方法取决于操作系统,而直接I/O的支持取决于文件系统。在使用此功能之前,请检查文件系统是否支持直接I/O。RocksDB已经为您处理了这些依赖于os的复杂问题,但是我们很高兴在这里分享一些实现细节。

  1. 1.File Open 文件打开
  2. 对于LINUX,必须包含O_DIRECT标志。对于Mac OSX, O_DIRECT不可用。相反,fcntl(fd, F_NOCACHE, 1)看起来是规范的解决方案,其中fd是文件的文件描述符。
  3. 对于Windows,有一个名为FILE_FLAG_NO_BUFFERING的标志作为O_DIRECT窗口中的对应标志。
  4. 2.File R/W 文件读/写
  5. 直接I/O要求文件R/W对齐,这意味着位置指示器(偏移量)、#字节和缓冲区地址必须与底层存储设备的逻辑扇区大小对齐。
  6. 因此,位置指示器和缓冲区指针必须在逻辑扇区大小边界上对齐,读取或写入的字节数必须是逻辑扇区大小的倍数。
  7. RocksDB实现了FileReader/FileWriter内部的所有对齐逻辑,这是文件类之上的一层更高的抽象,可以忽略操作系统的对齐。
  8. 因此,不同的OSs可以有自己的文件类实现。

API

直接输入/输出很容易使用,因为options.h中提供了两个新选项:

  1. // Enable direct I/O mode for read/write
  2. // they may or may not improve performance depending on the use case
  3. //
  4. // Files will be opened in "direct I/O" mode
  5. // which means that data r/w from the disk will not be cached or
  6. // buffered. The hardware buffer of the devices may however still
  7. // be used. Memory mapped files are not impacted by these parameters.
  8. // Use O_DIRECT for user and compaction reads.
  9. // When true, we also force new_table_reader_for_compaction_inputs to true.
  10. // Default: false
  11. // Not supported in ROCKSDB_LITE mode!
  12. bool use_direct_reads = false;
  13. // Use O_DIRECT for both reads and writes in background flush and compactions
  14. // When true, we also force new_table_reader_for_compaction_inputs to true.
  15. // Default: false
  16. // Not supported in ROCKSDB_LITE mode!
  17. bool use_direct_io_for_flush_and_compaction = false;

代码是不言自明的。

您可能还需要其他选项来优化直接I/O性能。

  1. // options.h
  2. // Option to enable readahead in compaction
  3. // If not set, it will be set to 2MB internally
  4. size_t compaction_readahead_size = 2 * 1024 * 1024; // recommend at least 2MB
  5. // Option to tune write buffer for direct writes
  6. size_t writable_file_max_buffer_size = 1024 * 1024; // 1MB by default
  7. // DEPRECATED!
  8. // table.h
  9. // If true, block will not be explicitly flushed to disk during building
  10. // a SstTable. Instead, buffer in WritableFileWriter will take
  11. // care of the flushing when it is full.
  12. // This option is deprecated and always be true
  13. bbto.skip_table_builder_flush = true;

如果启用了直接I/O,那么最近的版本会自动设置这些选项。

注释说明

1.allow_mmap_reads不能与use_direct_reads或use_direct_io_for_flush_and_compact一起使用。 allow_mmap_write不能与use_direct_io_for_flush_and_compact一起使用,即,它们不能同时设置为true。 2.use_direct_io_for_flush_and_compact和use_direct_reads将只应用于SST文件I/O,而不应用于WAL I/O或MANIFEST I/O。 还不支持用于WAL和清单文件的直接I/O。 3.在启用直接I/O之后,压缩写将不再在OS页面缓存中,所以第一次读取将执行真正的IO。有些用户可能知道RocksDB有一个称为压缩块缓存的特性,它应该能够用启用了直接I/O来替换页面缓存。 但请在启用前阅读以下评论: 4.碎片。 RocksDB的压缩块没有与页面大小对齐。压缩块驻留在RocksDB压缩块缓存中的malloc’ed内存中。 它通常意味着内存使用中的碎片。OS页面缓存做得稍微好一些,因为它缓存整个物理页面。如果一些连续块都是热的,OS页面缓存使用更少的内存来缓存它们。 5.OS页面缓存提供预先读取。默认情况下,在RocksDB中这是关闭的,但用户可以选择打开它。这将在范围循环控制的工作负载中非常有用。 RocksDB压缩缓存没有任何匹配的功能。 6.可能的错误。以前从未使用过RocksDB压缩块缓存代码。我们确实看到外部用户向它报告了bug,但是我们从来没有采取更多的步骤来改进这个组件。 7.直接IO模式下的迭代器也支持自动预读。这样,Sysbench中的远程和全表扫描基准测试(通过MyRocks构建)就可以匹配缓冲IO模式。 8.如果您的工作负载是点查询和范围查询的组合,建议通过设置LRUCacheOptions为块缓存打开中点插入策略。 high_pri_pool_ratio = 0.5。(注意,这取决于BlockBasedTableOptions。cache_index_and_filter_blocks以及cache_index_and_filter_blocks_with_high_priority)。