img

mmap 等於放棄對記憶體的細粒度控制:

  1. 無法執行非阻塞的記憶體存取。
  2. 「磁碟上」的呈現必須與「記憶體中」的呈現一致。
  3. 資料庫無法知悉記憶體中的 Pages 內容。
  4. 各種與 mmap 相關的 syscalls 都不具有可移植性。

以上是這張圖這麼告訴我們的。

我們先瞭解 PostgreSQL 是怎麼處理記憶體配置的。

早期 PostgreSQL 採用 sysv,在啟動時先分配。9.3 版本時,為大幅度降低對 sysv 的依賴而改用 mmap。但 mmap 有其缺點,所以 9.4 版本時,額外支援動態分配記憶體。[1][2]

我們不能說 OS Cache 總是最好的。它可能非常擅長處理 Block-level caching,但因為無法重寫,因此無法做到細微操作。作業系統除了能收集基本的存取統計外,就無法再多做什麼。所以若要 PostgreSQL 這樣的高級資料庫完全拋棄自己的 Cache 而全用 OS Cache 也是不合理的。實際上,大多數 PostgreSQL 專家都建議只有一小部分可用的 RAM 應該用作 Buffer Cache,並且讓作業系統管理大部分記憶體是正確的。

所以 PostgreSQL 並不總是使用 mmap,而是有自己的 Buffer Cache,只是大部分仍是使用基於 mmap 的記憶體管理方式。

接著來看 MySQL 的實現。

MySQL 因為是可抽換引擎,所以這裡我們只討論 InnoDB。依據我先前撰寫的兩篇內容 [4][5],可以知道 MySQL InnoDB 自帶 InnoDB Buffer Pool 管理自己的記憶體,這與 PostgreSQL 有很大的不同,各有優缺點。

我們也可由此延伸到 MySQL 處理連線的 session 配置,來進一步認識 mmap 的影響。

MySQL 處理連線時,每個連線都會依據 read_buffer_size (其一) 的設定,配置相對應的記憶體大小,預設是 128 KB。若記憶體配置軟體是 Glibc (通常是),從原始碼得知 [6],當配置 > 128 KB 的空間時 [6],呼叫方法會改為較重量的 mmap,此時記憶體配置的速度會降低,且每條連線的記憶體佔用也比較大。所以 read_buffer_size 並不是愈大愈好,反而預設的 128 KB 可能才是最優解,又快又省記憶體。

補充資料

[1] 如何度量 Kernel Resources for PostgreSQL

[2] PostgreSQL Documentation: Chapter 19. Server Configuration

[3] Hacker News : Why not mmap?

[4] 淺談 MySQL / PostgreSQL 的 Double Buffering

[5] 淺談 MySQL / PostgreSQL 的 Double Buffering (續)

[6] glibc : malloc.c