MySQL如何避免使用Linux的swap分區而提高讀寫性能


MySQL如何避免使用Linux的swap分區而提高讀寫性能


Linux有不少很好的內存、IO調度機制,可是並不會適用於全部場景。對於DBA來講Linux比較讓人頭疼的一個地方是,它不會由於MySQL很重要就避免將分配給MySQL的地址空間映射到swap上。對於頻繁進行讀寫操做的系統而言,數據看似在內存而實際上在磁盤是很是糟糕的,響應時間的增加極可能直接拖垮整個系統。這篇blog主要講講咱們做爲DBA,怎樣儘可能避免MySQL慘遭swap的毒手。html

首先咱們要了解點基礎的東西,好比說爲何會產生swap。假設咱們的物理內存是16G,swap是4G。若是MySQL自己已經佔用了12G物理 內存,而同時其餘程序或者系統模塊又須要6G內存,這時候操做系統就可能把MySQL所擁有的一部分地址空間映射到swap上去。mysql

cp一個大文件,或用mysqldump導出一個很大的數據庫的時候,文件系統每每會向Linux申請大量的內存做爲cache,一不當心就會致使L使用swap。這個情景比較常見,如下是最簡單的三個調整方法:linux

一、/proc/sys/vm/swappiness的內容改爲0(臨時),/etc/sysctl.conf上添加vm.swappiness=0(永久)
這個參數決定了Linux是傾向於使用swap,仍是傾向於釋放文件系統cache。在內存緊張的狀況下,數值越低越傾向於釋放文件系統cache。
固然,這個參數只能減小使用swap的機率,並不能避免Linux使用swap。sql

二、修改MySQL的配置參數innodb_flush_method,開啓O_DIRECT模式。
這種狀況下,InnoDB的buffer pool會直接繞過文件系統cache來訪問磁盤,可是redo log依舊會使用文件系統cache。值得注意的是,Redo log是覆寫模式的,即便使用了文件系統的cache,也不會佔用太多。數據庫

三、添加MySQL的配置參數memlock
這個參數會強迫mysqld進程的地址空間一直被鎖定在物理內存上,對於os來講是很是霸道的一個要求。必需要用root賬號來啓動MySQL才能生效。

還有一個比較複雜的方法,指定MySQL使用大頁內存(Large Page)。Linux上的大頁內存是不會被換出物理內存的,和memlock有殊途同歸之妙。具體的配置方法能夠參 考:http://harrison-fisk.blogspot.com/2009/01/enabling-innodb-large-pages- on-linux.htmlapi

以前介紹了MySQL如何避免使用swap的四個方法。這裏須要補充一下原理和實現機制,對於Linux api不感興趣的同窗能夠直接跳過。緩存

1、操做系統設置swap的目的
程序運行的一個必要條件就是足夠的內存,而內存每每是系統裏面比較緊張的一種資源。爲了知足更多程序的要求,操做系統虛擬了一部份內存地址,並將之映射到 swap上。對於程序來講,它只知道操做系統給本身分配了內存地址,但並不清楚這些內存地址到底映射到物理內存仍是swap。
物理內存和swap在功能上是同樣的,只是由於物理存儲元件的不一樣(內存和磁盤),性能上有很大的差異。操做系統會根據程序使用內存的特色進行換入和換 出,儘量地把物理內存留給最須要它的程序。可是這種調度是按照預先設定的某種規則的,並不能徹底符合程序的須要。一些特殊的程序(好比MySQL)但願 本身的數據永遠寄存在物理內存裏,以便提供更高的性能。因而操做系統就設置了幾個api,以便爲調用者提供「特殊服務」。oracle

2、Linux提供的幾個api
一、mlockall()和munlockall()
這一對函數,可讓調用者的地址空間常駐物理內存,也能夠在須要的時候將此特權取消。mlockall()的flag位能夠是MCL_CURRENT和 MCL_FUTURE的任意組合,分別表明了「保持已分配的地址空間常駐物理內存」和「保持將來分配的地址空間常駐物理內存」。對於Linux來講,這對 函數是很是霸道的,只有root用戶纔有權限調用。app

二、shmget()和shmat()
這一對函數,能夠向操做系統申請使用大頁內存(Large Page)。大頁內存的特色是預分配和永駐物理內存,由於使用了共享內存段的方式,page table有可能會比傳統的小頁分配方式更小。對於多進程共享內存的程序(好比ORACLE),大頁內存可以節省不少page table開銷;而對於MySQL來講,性能和資源開銷都沒有顯著變化,好處就在於減小了內存地址被映射到swap上的可能。至於爲何是減小,而不是徹底避免,以後再講解。函數

三、O_DIRECT和posix_memalign()
以上兩個方法都不會減小內存的使用量,調用者的本意是獲取更高的系統特權,而不是節約系統資源。O_DIRECT是一種更加理想化的方式,經過避免 double buffer,節省了文件系統cache的開銷,最終減小swap的使用率。O_DIRECT是Linux IO調度相關的標誌,在open函數裏面調用。經過O_DIRECT標誌打開的文件,讀寫都不會用到文件系統的cache。傳統的數據庫(ORACLE、MySQL)基本都有O_DIRECT相關的開關,在提升性能的同時,也減小了內存的使用。至於posix_memalign(),是用來申請對齊的內存地址的。只有用posix_memalign()申請的內存地址,才能用來讀寫O_DIRECT模式下的文件描述符。

四、madvise()和fadvise()
這對函數也是比較溫和的,能夠將調用者對數據訪問模式的預期傳遞給Linux,以期獲得更好的性能。
咱們比較感興趣的是MADV_DONTNEED和FADV_NOREUSE這兩個flag。前者會建議Linux釋放指定的內存區域,然後者會建議文件系統釋放指定文件所佔用的cache。

3、MySQL內存使用相關的一些代碼
一、memlock
在MySQL的源碼目錄裏面查詢memlock,能夠知道這個參數的做用是使MySQL調用mlockall()。在源碼裏面匹配能夠得知NDB、MyISAM和 mysqld都調用了mlockall()。NDB是能夠獨立於MySQL而存在的存儲引擎,此處按下不表。mysqld調用mlockall()的方式 有點出乎意料,在init_server_components()函數裏傳給mlockall()的flag是MCL_CURRENT,也就是說以後申 請的內存一律不用鎖住。再看看MyISAM的調用順序是:mlockall() <- lock_memory() <- mi_repair(),MyISAM只有修復的時候會調用mlockall()函數。

二、large-pages
根據Linux的內核文檔,大頁內存有兩種方法能夠用到:一種是建立hugetlb類型的文件,並將它mmap到程序的內存地址裏面,而後進行正常的讀寫 操做。另一種是以前說到的shmget()+shmat(),也正是MySQL採用的方式。在MySQL的源碼目錄裏面匹配shmget,能夠發現 BDB、NDB、InnoDB、MyISAM都調用了這個函數。接着看一下比較經常使用的InnoDB和MyISAM引擎。
在InnoDB裏面能夠找到os_mem_alloc_large()調用了shmget(),而調用os_mem_alloc_large()的函數只 有buf_pool_init()——InnoDB Buffer Pool的初始化函數。根據觀察獲得的結論是,InnoDB會根據配置參數在Buffer Pool裏面使用大頁內存,Redo log貌似就沒有這個待遇了。
對於MyISAM,在storage層級的代碼裏面找不到對shmget()的直接調用。這是由於MyISAM是MySQL的原生存儲引擎,不少函數存放 在上一層的mysys目錄裏面。經過搜索shmget(),咱們能夠找到MyISAM的調用順序是這樣的:shmget() <- my_large_malloc_int() <- my_large_malloc() <- init_key_cache()。也就是說MyISAM只有索引緩存用到了大頁內存,這是很容易理解,由於MyISAM的數據是直接扔給文件系統作緩存 的,無法使用大頁內存。

三、innodb_flush_method
O_DIRECT是BDB、NDB、InnoDB特有的參數,在這裏只討論InnoDB這個比較常見的引擎。在InnoDB的源碼目錄裏面匹配 O_DIRECT,很容易找到一個叫作os_file_set_nocache()的函數,而這個函數做用是將文件的打開方式改成O_DIRECT模式。 再跟蹤一下,會發現只有os_file_create()函數調用了os_file_set_nocache()。雖然函數名裏面還有create,實際 上os_file_create()會根據傳入參數的不一樣,選擇打開或者新建一個文件。同時os_file_create()還會根據MySQL的配置, 來調用os_file_set_nocache()關閉文件系統的相應cache。在os_file_create()函數裏面有以下一段代碼:
/* We disable OS caching (O_DIRECT) only on data files */
if (type != OS_LOG_FILE &&
srv_unix_file_flush_method == SRV_UNIX_O_DIRECT)
{
os_file_set_nocache(file, name, mode_str);
}
這段代碼的意思是,只有InnoDB的數據文件有資格使用O_DIRECT模式,Redo log是不能使用的。

以上的分析基於5.0.85版本的原版MySQL,InnoDB是Innobase。
版本不一樣狀況下可能會有一些出入,歡迎參與討論。

參考文獻:
Virtual memory@wiki
All about Linux swap space
HugeTLB – Large Page Support in the Linux Kernel
Page table@wiki

來源:http://www.taobaodba.com/html/552_mysql_avoid_swap.html

http://www.taobaodba.com/html/554_mysql_avoid_swap_2.html

永久連接 : http://www.ha97.com/4201.html

相關文章
相關標籤/搜索