查看原文
其他

PostgreSQL 不支持的 O_DIRECT,MySQL 和 Oracle 都有

破产码农 InsideMySQL 2022-10-13

        

破产码农

IT圈最会讲故事的网红 · 南山彭于晏

在 MySQL 数据库中,几乎默认会将 InnoDB 的脏页刷新参数 innodb_flush_method 设置为 O_DIRECT ,以此提升数据库的磁盘刷新性能。

Oracle 数据库也有提供了参数 FILESYTEMIO_OPTIONS  可将刷新设置为 O_DIRECT。

然而,PostgreSQL 数据库并不支持 O_DIRECT,所以 PG 存在数据库性能抖动的问题,无法在海量互联网业务中使用。

然而,什么是 O_DIRECT 呢?

O_DIRECT  内存地址对齐

相信很多同学会说,O_DIRECT 是指文件读写时,数据直接访问磁盘,而不要经过操作系统的缓存。

嗯,这个没有错,但 show me your code 。

接着,Java 开发工程师、DBA 们就一脸茫然了。

估计你让一个 P8 工程师来,也写不出。

不信?那你问问身边的工程师们。

O_DIRECT 原理本身不特别复杂,一看即懂,一句话就能说清。

但这个特性却非常小众,按我的理解只存在于类似数据库的开发领域。

绝大部分业务的开发工程师们,与文件打交道就是打印日志。

日志打印主要目的是性能,不需要 O_DIRECT 。日志数据由于缓存写,若发生丢失,丢就丢了吧。

甚至,Java 语言本身都没提供 O_DIRECT 的文件选项。若想使用,还需要自己额外进行一层底层的封装。

好吧,接着让姜老师写个最简单的 demo :

这个 demo 就是向文件 f.test 写入16384个字节。

可以看到,这里 open 的时候加入了额外的 O_DIRECT 选项,接着通过 pwrite 函数将数据写入文件。

但这里需要特别注意的是 O_DIRECT 写入,要求内存地址与扇区大小对齐。下面是官方文档的说明:

The O_DIRECT flag may impose alignment restrictions on the length and address of user-space buffers and the file offset of I/Os. 

因此,你会看到下面这两行用于处理内存对齐的逻辑:

buf = (char*)malloc(sizeof(char*)*PAGE_SIZE*2); buf_aligned = (char*)ut_align(buf,SECTOR_SIZE); // align address for DIRECT_IO

只有做了地址对齐,才能使用 O_DIRECT,否则 pwrite 后的 assert 校验就会失败。

但扇区大小是多少呢?文档的说法就相当玄幻了:

In Linux alignment restrictions vary by filesystem and kernel version and might be absent entirely. However there is currently no filesystem-independent interface for an application to discover these restrictions for a given file or filesystem.

文档的意思大致就是扇区大小是可变化的,而且也没有提供一个统一的接口去获取文件系统的扇区大小。

根据经验,我们知道大部分磁盘的扇区大小是 512 字节,SSD 的扇区大小是 4K。

因此,若要使用 O_DIRECT ,建议直接按 4K 对齐,这样就无需关注下面的具体存储类型了(至少目前好像还没有扇区大小超过 4K 的设备)。

细心的同学会发现,在上面的代码中,使用 O_DIRECT 后,还需要进行 fsync 这又是为什么呢?

O_DIRECT 到底还要不要 fsync ?


是的,使用 O_DIRECT 选项后,文件写入时会绕过操作系统缓存,数据直接落盘:


从上图可以看到使用 O_DIRECT 选项后,磁盘读写从文件系统层直接访问最底层的存储设备,不走操作系统层的 Page Cache。

但即便使用 O_DIRECT ,在写入后,还是需要通过调用一次 fsync 用于保证数据真正落到磁盘。

这是因为文件对应的元数据信息还没有落盘,例如文件的大小,最后的修改时间等。

但是,若文件没有增长呢?只是更新了一个页的数据。

是的,那这时就无需在写入文件后,再进行 fsync 操作。这样可进一步提升磁盘性能。

MySQL 5.7.25 版本开始,就进行了类似这样的优化,对参数 innodb_flush_method 提供了新的选项 O_DIRECT_NO_FSYNC 。看文档的说明:

O_DIRECT_NO_FSYNC: InnoDB uses O_DIRECT during flushing I/O, but skips the fsync() system call after each write operation.
Prior to MySQL 5.7.25, this setting is not suitable for file systems such as XFS and EXT4, which require an fsync() system call to synchronize file system metadata changes. If you are not sure whether your file system requires an fsync() system call to synchronize file system metadata changes, use O_DIRECT instead.
As of MySQL 5.7.25, fsync() is called after creating a new file, after increasing file size, and after closing a file, to ensure that file system metadata changes are synchronized. The fsync() system call is still skipped after each write operation.
总结

今天姜老师深入讲解了 O_DIRECT 的使用,这是一个文件系统操作非常底层的使用选项,一般仅用于数据库中。

今天留下2道思考题,相信答对者年薪百万那是妥妥的:

  • MySQL InnoDB存储引擎刷新磁盘使用 O_DIRECT,他是根据多少大小进行字节对齐的呢?

  • 为什么重做日志文件写入却不需要启用 O_DIRECT 选项呢?

  • RAW格式是否还有性能优势呢?

直播预告


每周五、六,不定期直播,分享技术干货


IMG群是码农的交流社区,IMG微信群交流内容包括但不限于技术、经济、军事、八卦等话题。欢迎有态度的码农们加入IMG大家庭。
IMG目前有少林群、武当群、峨眉群、华山群、M悦会(高端VIP群)
仅限码农入群,猎头或其他行业勿加,入群请加姜老师个人微信 82946772,并备注:码农入IMG群
-----------------------
公众号:破产码农
视频号:破产码农
抖音号:破产码农
B站号:姜老师带你飞
长按下图二维码关注,将感受到一个有趣的灵魂,每篇文章都会有新惊喜。


          

往期推荐

一个参数,MySQL 8.0 性能飙升180%!!!


全网首测,MySQL Router来了,不容错过!!!


MySQL 8.0,二进制日志的一大改进!


02集 | MySQL崛起:起飞


01集 | MySQL崛起:缘起


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存