mysql存儲內存中(tmpfs)的問題

MySQL的相關參數:
default-storage-engine = INNODB
innodb_flush_method=O_DIRECT
tmpdir = /tmp/mysqltmp
/tmp/mysqltmp目錄爲tmpfs

發現error日誌中出現

131119 15:12:55 InnoDB: Failed to set O_DIRECT on file ./BetBrainV4/Participant.ibd: OPEN: Invalid argument, continuing anyway

131119 15:12:55 InnoDB: O_DIRECT is known to result in 'Invalid argument' on Linux on tmpfs, see MySQL Bug#26662


解決方式:

1.創建memory或myisam的臨時表代替innodb;
2.修改innodb_flush_method爲非O_DIRECT,如O_DSYNC、fdatasync;
參考:

http://bugs.mysql.com/bug.php?id=26662
http://bugs.mysql.com/bug.php?id=45671


下面介紹一個解決方式的思路:

innodb_flush_method這個參數控制着innodb數據文件及redo log的打開、刷寫模式,對於這個參數,文檔上是這樣描述的:

有三個值:fdatasync(默認),O_DSYNC,O_DIRECT

默認是fdatasync,調用fsync()去刷數據文件與redo log的buffer

爲O_DSYNC時,innodb會使用O_SYNC方式打開和刷寫redo log,使用fsync()刷寫數據文件

爲O_DIRECT時,innodb使用O_DIRECT打開數據文件,使用fsync()刷寫數據文件跟redo log

首先文件的寫操作包括三步:open,write,flush

上面最常提到的fsync(int fd)函數,該函數作用是flush時將與fd文件描述符所指文件有關的buffer刷寫到磁盤,並且flush完元數據信息(比如修改日期、創建日期等)纔算flush成功。

使用O_SYNC方式打開redo文件表示當write日誌時,數據都write到磁盤,並且元數據也需要更新,才返回成功

O_DIRECT則表示我們的write操作是從mysql innodb buffer裏直接向磁盤上寫

至此我再總結一下三者寫數據方式:

fdatasync模式:寫數據時,write這一步並不需要真正寫到磁盤纔算完成(可能寫入到操作系統buffer中就會返回完成),真正完成是flush操作,buffer交給操作系統去flush,並且文件的元數據信息也都需要更新到磁盤。

O_DSYNC模式:寫日誌操作是在write這步完成,而數據文件的寫入是在flush這步通過fsync完成

O_DIRECT模式:數據文件的寫入操作是直接從mysql innodb buffer到磁盤的,並不用通過操作系統的緩衝,而真正的完成也是在flush這步,日誌還是要經過OS緩衝


2個思考

1、innodb_flush_method究竟應不應該使用O_DIRECT?

所有MySQL調優的建議都說,如果硬件沒有預讀功能,那麼使用O_DIRECT將極大降低InnoDB的性能,因爲O_DIRECT跳過了操作系統的文件系統Disk Cache,讓MySQL直接讀寫磁盤了。

但是在我的實踐中來看,如果不使用O_DIRECT,操作系統被迫開闢大量的Disk Cache用於innodb的讀寫緩存,不但沒有提高讀寫性能,反而造成讀寫性能急劇下降。而且buffer pool的數據緩存和操作系統Disk Cache緩存造成了Double buffer的浪費,顯然從我這個實踐來看,浪費得非常厲害。

說O_DIRECT造成MySQL直接讀寫磁盤造成得性能下降問題,我覺得完全是杞人憂天。因爲從JavaEye的數據庫監測來看,Innodb的buffer pool命中率非常高,有98%以上,真正的磁盤操作是微乎其微的。爲了1%的磁盤操作能夠得到Disk Cache,而浪費了98%的double buffer內存空間,無論從性能上看,還是從內存資源的消耗來看,都是非常不明智的。

2、innodb_log_file_size究竟應該大一點,還是小一點?

所有MySQL調優建議都說,innodb_log_file_size要越大越好,避免無謂的buffer pool的flush操作。

但是在我的實踐中來看,innodb_log_file_size開得太大,會明顯增加innodb的log寫入操作,而且會造成操作系統需要更多的Disk Cache開銷。
innodb_flush_method=O_DIRECT是必須的,而innodb_log_file_size也不宜太大。


下面是一段摘錄的備忘


http://www.orczhou.com/index.php/2009/08/innodb_flush_method-file-io/

innodb_flush_log_at_trx_commit參數確定日誌文件何時write、flush。innodb_flush_method則確定日誌及數據文件如何write、flush。在Linux下,innodb_flush_method可以取如下值:fdatasync, O_DSYNC, O_DIRECT,那這三個值分別是如何影響文件寫入的?首先我們需要先來了解Linux的文件I/O是如何工作的。

先來看看Linux/Unix文件I/O的一個典型例子:(Linux 2.6.24測試,gcc編譯)

/**
	A test about syscall of File I/O
	Author:supu@TaobaoDBA
	[email protected] http://orczhou.com http://www.taobaodba.com
*/

#include   "stdlib.h"           /* for exit */
#include   "unistd.h"           /* for write fdatasync*/
#include    "fcntl.h"           /* for open  */
int main(void){
	int fd;
	if((fd=open("/home/zzx/test.file",O_WRONLY|O_APPEND|O_DSYNC))<0){
		exit(1);
        }
        char buff[]="abcdef";
        if(write(fd,buff,6)!= 6){
                exit(2);
        }
        if(fdatasync(fd)==-1){
                exit(3);
        }
        exit(0);
}

程序描述了一般的文件I/O操作的三個過程open、write、fdatasync,分別是打開文件、寫文件、flush操作(將文件緩存刷到磁盤上)。

一、Open階段

open("test.file",O_WRONLY|O_APPDENT|O_SYNC))

系統調用Open會爲該進程一個文件描述符fd【附錄2】。這裏使用了O_WRONLY|O_APPDENT|O_SYNC打開文件:

  1. O_WRONLY表示我們以"寫"的方式打開,告訴內核我們需要向文件中寫入數據;

  2. O_APPDENT告訴內核以"追加"的方式寫文件;

  3. O_DSYNC告訴內核,當向文件寫入數據的時候,只有當數據寫到了磁盤時,寫入操作纔算完成(write才返回成功)。和O_DSYNC同類的文件標誌,還有O_SYNC,O_RSYNC,O_DIRECT。

    • O_SYNC比O_DSYNC更嚴格,不僅要求數據已經寫到了磁盤,而且對應的數據文件的屬性(例如文件長度等)也需要更新完成纔算write操作成功。可見O_SYNC較之O_DSYNC要多做一些操作。

    • O_RSYNC表示文件讀取時,該文件的OS cache必須已經全部flush到磁盤了【附錄3】

    • 如果使用O_DIRECT打開文件,則讀/寫操作都會跳過OS cache,直接在device(disk)上讀/寫。因爲沒有了OS cache,所以會O_DIRECT降低文件的順序讀寫的效率。


二、Write階段

write(fd,buf,6)

在使用open打開文件獲得文件描述符之後,我們就可以調用write函數來寫入數據了,write會根據前面的open參數不同,而表現不同。

三、Flush階段

fdatasync(fd) == -1

write操作後,我們還調用了fdatasync來確保文件數據flush到了disk上。fdatasync返回成功後,那麼可以認爲數據已經寫到了磁盤上。像這樣的flush的函數還有fsync、sync。

  1. Fsync和fdatasync的區別等同於O_SYNC和O_DSYNC的區別。

  2. Sync函數表示將文件在OS cache中的數據排入寫隊列,並不確認是否真的寫磁盤了,所以sync並不可以靠。

忽略文件打開的過程,通常我們會說“寫文件”有兩個階段,一個是調用write我們稱爲寫數據階段(其實是受open的參數影響),調用fsync(或者fdatasync)我們稱爲flush階段。

回到MySQL,參數Innodb_flush_method(Linux)可以設定爲:Fdatasync、O_DSYNC、O_DIRECT。我們看看這個三個參數是如何影響程序MySQL對日誌和數據文件的操作:


Open logFlush logOpen datafileFlush data
Fdatasync
fsync()
fsync()
O_DSYNCO_SYNC

fsync()
O_DIRECT
fsync()O_DIRECTFsync()

fdatasync被認爲是安全的,因爲在MySQL總會調用fsync來flush數據。使用O_DSYNC是有些風險的,有些OS會忽略該參數O_SYNC

我們看到O_DIRECT和fdatasync和很類似,但是它會使用O_DIRECT來打開數據文件。有數據表明,如果是大量隨機寫入操作,O_DIRECT會提升效率。但是順序寫入和讀取效率都會降低。所以使用O_DIRECT需要謹慎。

參考文章:

  1. Unix環境高級編程(第二版)

  2. http://rdc.taobao.com/blog/dba/html/296_innodb_flush_method_performance.html

  3. http://dev.mysql.com/doc/refman/5.0/en/innodb-parameters.html

  4. http://www.ukuug.org/events/linux2001/papers/html/AArcangeli-o_direct.html

  5. http://xiaomeng.yo2.cn/articles/buffered-io-and-non-buffered-io.html

  6. http://articles.directorym.net/Operating_Systems_System_Calls_and_IO-a894576.html

  7. http://www.ibm.com/developerworks/cn/linux/l-cn-read/

  8. http://www.kernel.org/doc/man-pages/online/pages/man2/open.2.html

  9. 系統調用:http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/index.html

【附錄1】文章需要你瞭解什麼是“系統調用“,參考:

http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/index.html

可以簡單的理解爲:“系統調用“是在內核之上的一層封裝。由內核直接提供接口,“系統調用”需要陷入內核執行(內核態)。其中fdatasync就是一個系統調用,該系統調用可以通知OS立刻將OS Cache中的數據Flush到磁盤文件中。

【附錄2】這時候內核會爲該該進程打開的文件分配一個文件描述符,並將該文件描述符返回給該進程。在內核的文件表中新建一個文件項,標記文件狀態、文件當前偏移、以及I節點(v節點)的位置,內核還會打開該文件的I節點(這裏記錄文件的操作的函數指針,例如讀操作、寫操作)。

【附錄3】O_RSYNC我的理解是,對於同一個文件描述符可以保證讀數據安全。同一個文件描述符包括dup和fcntl函數dup的文件描述符,即共用同一個文件表項。O_RSYNC不是我們今天關注的,暫時忽略

未解問題:

  1. O_DIRECT在哪些OS(或者FS)上能夠正常工作?

  2. O_SYNC在哪寫OS上不能正常工作呢?

  3. 內核的read、write是FS級別的還是內核(kernel)級別?

  4. 文章innodb_flush_method帶來的性能影響中O_DSYNC、和fdatasync效率差很多,這是爲什麼?

(全文完)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章