MySQL如何避免使用swap(2)

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

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

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

二、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模式下的文件描述符。sql

四、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()函數。api

二、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是不能使用的。app

以上的分析基於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性能

相關文章
相關標籤/搜索