MySQL/InnoDB和GroupCommit(2)

orczhou發表於2016-03-28

今天發現Percona Release的Percona-Server-5-5-18-23-0已經完成了Group Commit工作,而且是用最優雅的方式(移植了MariaDB的實現,而不是workaround),心裡難掩激動。

這篇文章接前篇繼續介紹一下問題的背景:什麼是Group Commit,現在的官方版本Group Commit做到了什麼程度?

1. 什麼是Group Commit

MySQL/InnoDB在做事務的時候使用的日誌先行(Write-ahead logging)的方式保證事務的快速和持久,所以每次事務完成都要求日誌必須持久化到磁碟,在Linux上對應的操作就是“write and fsync”,write速度是很快的,一般對應的寫Page Cache,而fsync則要求檔案系統把對應檔案的哦Page Cache必須持久化到磁碟上,而對於傳統磁碟一次寫操作大約需要1~3ms(不說BBU),這意味著對於傳統磁碟1秒鐘最多最多做333~1000個事務,這是不理想的,對硬體的利用率(吞吐量)也是非常低。

所以,這裡就有了Group操作的概念,即當好幾個執行緒都要fsync同一個日誌檔案時,我們將這些fsync合併到一次來做。簡單舉例:我們讓系統每2ms做一次fsync,這樣如果這2ms內有100個執行緒都需要做fsync,那就賺到了,原本100個fsync都獨立來做那耗時會非常多的。

你肯定會說,難道這不是很簡單的想法嗎?是的,這就是原本是很簡單、也很自然的想法。

但對MySQL來說卻變成了一種奢求(看看這個Bug討論)。

2. 為啥MySQL一直沒有實現?

MySQL是不是太弱了,這麼簡單的事情都搞不定?不是的。

MySQL從開源到現在,成功的一個非常重要的原因,就是MySQL的外掛式架構。如果MySQL只是MyISAM估計不會有現在的流行程度,外掛式的架構讓諸如Heikki Tuuri有了發揮空間,在InnoDB和MySQL一起時,MySQL才能算是一個真正的DBMS。再到後來,有Infobright、 FEDERATED、PBXT等等。

外掛式的架構給MySQL帶來了活力,做出犧牲便是在上層(MySQL)和下層(儲存引擎)互動時帶來的額外消耗,有時甚至上層和下層需要做一些重複工作。無法做Group Commit就是這其中的犧牲之一。

3. 現在的官方版本Group Commit做到了什麼程度

InnoDB在一次事務中,有兩次fsync,分別是prepare階段和commit階段,對於這兩次操作InnoDB都是實現了Group操作的。

如果你設定了binlog_sync=1,則又多了一次fsync操作(參考MYSQL_BIN_LOG::flush_and_sync),這次操作在innobase_xa_prepare和innodb_commit之間:

binlog_prepare (do nothing)
innodb_prepare

binlog_commit
innobase_commit

這次fsync,在MySQL中一直都無法做Group。所以,一直以來當innodb_flush_log_at_trx_commit和binlog_sync都等於1的時候,MySQL的效能就非常、非常糟糕。原因是為了保證InnoDB內部Commit的順序和MySQL日誌的順序一致(參考)。

4. 開源社群的努力

難道沒人去解決這個問題嗎?不是的。有很多人已經做出了努力,直到Kristian Nielsen@MariaDB提出了理論和實際上的最優解決方案。

Mark Callaghan@Facebook關於Group Commit做的工作:FB的基本實現 | FB的問題和改進 | 效能 | 對比MariaDB的實現(對比的結果是MariaDB完勝,無論是方案架構還是效能)

Facebooke可以說是通過一個Trick快速實現了Group Commit。而Kristian Nielsen@MariaDB這是從架構上根本的解決了這個問題。

MariaDB關於Group Commit架構和實現: WL116 | WL132 | WL164

5. 未完待續

後面還有很多值得寫的,我希望自己能夠一直寫完這個系列。


相關文章