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

在 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 道思考題,相信答對者年薪百萬那是妥妥的:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Kpa3KWjRz4ndJVlaYlACEw