MySQL針對Swap分區的運維注意點

 

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

SWAP是操做系統虛擬出來的一部份內存地址,它的物理存儲元件是磁盤。在備份數據或恢復數據時,文件系統會向Linux系統請求大量的內存做爲cache。在物理內存使用殆盡時候,爲了確保程序運行,每每會將另外的一些佔用物理內存地址空間的程序映射到swap分區上。算法

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

服務器產生Swa分區的緣由
1)copy一個大文件,好比上百G的backup包
2)正在mysqldump以及mysql import一個很大的庫的時候。
3)大批量的併發操做的io writer和io read操做。數據庫

MySQL程序運行時,物理內存爲MySQL分配了大量的物理地址空間,以提升執行的速率。爲了不在執行消耗大量內存的操做時將MySQL所擁有的部分物理內存地址空間映射到swap分區上(好比出現了MySQL服務器Swap滿了100%致使db很慢很卡的現象),可作一下調整(解決辦法):
1)改系統內核參數/proc/sys/vm/swappiness。調整系統使用swap分區的傾向性,數值越低越傾向於釋放文件系統的cache,不能避免Linux系統使用swap分區。swappiness=0表示最大限度使用物理內存,而後纔是swap分區。swappiness=100表示積極使用swap分區,而且將內存上的數據及時的映射到swap分區上。vim

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

2)改MySQL參數innodb_flush_method,開啓O_DIRECT模式。Innodb的buffer pool會直接繞過文件系統cache來訪問磁盤,可是redo log依舊會使用文件系統cache。Redo Log是覆寫模式的,即便使用了文件系統的cache也不會佔用太多緩存

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

4)指定MySQL使用大頁內存(Large Page)。Linux上的大頁內存是不會被換出物理內存的,和memlock有殊途同歸之妙。服務器

5)臨時釋放鎖佔據的swap。併發

=========================================================

在Mysql數據庫維護中,會遇到的一個現象: MySQL內存持續增長,最高時物理內存消耗達到90%以上,致使swap使用率100%,進而形成內存不足,系統自動kill mysql進程。Mysql服務掛掉查看Mysql的error日誌信息:
[ERROR] InnoDB: Unable to lock /usr/local/mysql/var/ibdata1, error: 11
或者
InnoDB: mmap(137363456 bytes) failed; errno 12
2016-03-01 01:38:42 13064 [ERROR] InnoDB: Cannot allocate memory for the buffer pool
2016-03-01 01:38:42 13064 [ERROR] Plugin 'InnoDB' init function returned error.
2016-03-01 01:38:42 13064 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2016-03-01 01:38:42 13064 [ERROR] Unknown/unsupported storage engine: InnoDB
2016-03-01 01:38:42 13064 [ERROR] Aborting

出現上面報錯的緣由通常是系統內存資源不足形成的(error 11在mysql中是資源臨時不可用),解決方法是升級系統內存或者添加swap;

MySQL的內存消耗分爲:
1)會話級別的內存消耗:如sort_buffer_size等,每一個會話都會開闢一個sort_buffer_size來進行排序操做。
2)全局的內存消耗:例如:innodb_buffer_pool_size等,全局共享的內存段。

會話級的內存消耗多是一個緣由。關於會話級的內存消耗解釋以下:
read_buffer_size, sort_buffer_size, read_rnd_buffer_size, tmp_table_size這些參數在須要的時候才分配,操做後釋放。
這些會話級的內存,無論使用多少都分配該size的值,即便實際須要遠遠小於這些size。
每一個線程可能會不止一次須要分配buffer,例如子查詢,每層都須要有本身的read_buffer,sort_buffer, tmp_table_size 等。
找到每次內存消耗峯值是不切實際的,所以建議能夠用來衡量一下你實際修改一些變量值產生的反應,例如把 sort_buffer_size 
從1MB增長到4MB而且在max_connections爲1000 的狀況下,內存消耗增加峯值並非你所計算的3000MB而是30MB。

首先在/etc/my.cnf的mysqld配置區域下增長下面一句(根據機器自己的內存配置來設置下面這個參數值):

[root@mysql01 ~]# vim /etc/my.cnf
[mysqld]
.......
innodb_buffer_pool_size = 128M

而後開啓Swap分區,重啓Mysql服務。

開啓SWAP分區的方法

1)建立用於交換分區的文件(block_size、number_of_block 大小能夠根據機器自己配置狀況進行自定義,)
[root@mysql01 ~]# dd if=/dev/zero of=/mnt/swap bs=1M count=4096

2)設置交換分區文件:
[root@mysql01 ~]# mkswap /mnt/swap

3)當即啓用交換分區文件
[root@mysql01 ~]# swapon /mnt/swap

舒適提示:
若是在/etc/rc.local中有"swapoff -a",則須要修改成"swapon -a"

4)設置開機時自啓用 SWAP 分區:
須要修改文件 /etc/fstab 中的 SWAP 行,添加:
[root@mysql01 ~]# vim /etc/fstab
/mnt/swap swap swap defaults 0 0

5)修改swpapiness參數
在Linux系統中,能夠經過查看/proc/sys/vm/swappiness內容的值來肯定系統對SWAP分區的使用原則。
當swappiness內容的值爲0時,表示最大限度地使用物理內存,物理內存使用完畢後,纔會使用SWAP分區。
當swappiness內容的值爲100時,表示積極地使用SWAP分區,而且把內存中的數據及時地置換到SWAP分區。

查看修改前爲0,須要在物理內存使用完畢後纔會使用SWAP分區:
[root@mysql01 ~]# echo 0 > /proc/sys/vm/swappiness

能夠上面的方法臨時修改此參數,假設咱們配置爲空閒內存少於10%時才使用SWAP分區,則操做方法以下:
[root@mysql01 ~]# echo 10 > /proc/sys/vm/swappiness

若須要永久修改此配置,在系統重啓以後也生效的話,能夠修改 /etc/sysctl.conf 文件,並增長如下內容:  
[root@mysql01 ~]# vim /etc/sysctl.conf
vm.swappiness=10
[root@mysql01 ~]# sysctl -p

6)最後重啓mysql服務
[root@mysql01 ~]# /etc/init.d/mysqld restart

關閉SWAP分區的方法

當系統出現內存不足時,開啓 SWAP 可能會因頻繁換頁操做,致使 IO 性能降低。若是要關閉 SWAP,能夠採用以下方法。

1)free -m 查詢 SWAP 分區設置:
[root@mysql01 ~]# free -m

2)使用命令 swapoff 關閉 SWAP,好比:
[root@mysql01 ~]# swapoff /mnt/swap

3)修改 /etc/fstab 文件,刪除或註釋相關配置,取消SWAP的自動掛載:
[root@mysql01 ~]# vim /etc/fsta
#/mnt/swap swap swap defaults 0 0

4)經過 free -m  確認 SWAP 已經關閉。
[root@mysql01 ~]# free -m

5)swappiness 參數調整:
可使用下述方法臨時修改此參數,這裏配置爲 0%:
[root@mysql01 ~]# echo 0 >/proc/sys/vm/swappiness

若須要永久修改此配置,在系統重啓以後也生效的話,能夠修改 /etc/sysctl.conf 文件,並增長如下內容:
[root@mysql01 ~]# vim /etc/sysctl.conf
vm.swappiness=0
[root@mysql01 ~]# sysctl -p

=========================================================
來看看曾經碰到的一個因爲MySQL內存交換區引發的一場事故

 事故現象:公司的一個業務系統程序會調用大量的SQL,一天,發現MySQL的負載極其不穩定,尤爲是Slave的負載有點猛,而後通過討論準備再加一臺Slave,。另加了一臺Slave後,發現Slave的負載確實都回歸正常了,本覺得息事寧人,可是過了兩個小時後,新添加的Slave的負載暴增至100以上!原來的Slave服務則顯示正常。大概過了兩個小時,新增長的Slave的負載又迴歸正常了,可是又過了兩個小時後,負責又飆升至100以上!因而乎,趕忙Troubleshooting!過程以下:

1)考慮到新增的server,由於內存,CPU等硬件的配置和原來數據庫的server都不同(新增的Slave比原來的Slave的內存少了一半),必然配置參數的值也會不一樣。因此就從MySQl的配置文件查起,如:sort_buffer_size的大小(由於考慮到有許多SQL包含排序),join_buffer_size(用於鏈接的緩存的大小),max_connections(最大鏈接數,但是經過show processlist;發現也沒有超過設置的值),innodb_buffer_pool_size(確認是否爲物理內存的合適比例)等等。

2)使用free -m查看內存時,發現SWAP既然使用了500MB!經過vmsata,發現si和so的值不斷的變化,能夠確定的是發生了內存交換。原來是SWAP搗的蛋!

內存交換區:當操做系統由於沒有足夠的內存而將一些虛擬內存寫到磁盤就會發生內存交換。

內存交換對MySQL性能影響是極其糟糕的。它破壞了緩存在內存的目的,而且相對於使用很小的內存作緩存,使用交換區的性能更差。MySQL和存儲引擎有不少算法來區別對待內存中的數據和硬盤上的數據,由於通常都是假設內存數據訪問代價更低。

由於內存交換對用戶進程不可見,MySQL(或存儲引擎)並不知道數據實際上已經移動到磁盤,還會覺得仍然在內存中呢。

結果會致使不好的性能。例如。若存儲引擎認爲數據依然在內存,可能以爲爲"短暫"的內存操做鎖定一個全局互斥變量(例如,InnoDB緩衝池Mutex)是OK的。若是這個操做實際上引發了硬盤I/O,直到I/O操做完成前任何操做都會被掛起。這意味着內存交換比直接作硬盤I/O操做還要糟糕。

在Linux上,能夠用vmstat來監控內存交換。最好查看si和so列報告的內存交換I/O活動,這比看swapd列報告的交換區利用率更重要。咱們都喜歡si和so列的值爲0,而且必定要保證它們低於每秒10塊。

能夠經過正確地配置MySQL緩衝來解決大部份內存交換問題,可是有時操做系統的虛擬內存系統仍是會決定交換MySQL內存。這一般發生在操做系統看到MySQL發出了大量I/O,所以嘗試增長文件緩存來保存更多數據時。若是沒有足夠的內存,有些東西就必須交換出去,有些可能就是MySQL自己。

有些人主張徹底禁用交換文件。這樣作是很危險的,由於禁用內存交換就至關於給虛擬內存設置了一個不可動搖的限制。若是MySQL須要臨時使用很大一塊內存,或者有很耗內存的進程運行在同一臺server上(如夜間的批量任務),MySQL可能會內存溢出,崩潰,或者被操做系統kill掉。

操做系統一般容許對虛擬內存和I/O進行一些控制。最基本的方法就是修改/proc/sys/vm/swappiness爲一個很小的值,如0或1。這等同於告訴內核除非虛擬內存徹底滿了,不然不要使用交換區。下面是如何檢查這個值的例子:

$ cat /proc/sys/vm/swappiness
60

這個值顯示爲60,這是默認的設置(範圍是0~100)。對於服務器而言這是個很糟糕的默認值。服務器應該設置爲0:

$ echo 0 > /proc/sys/vm/swappiness

另外一個選項是修改存儲引擎怎麼讀取和寫入數據。使用innodb_flush_method=O_DIRECT,減輕I/O壓力。DIRECT I/O並不緩存,所以操做系統並不能把MySQL視爲增長文件緩存的緣由。這個參數只對InnoDB有效。你也可使用大頁,不參與換入換出,這對MyISAM和InnoDB都有效。

另外一個選擇是使用MySQL的memlock配置項,能夠把MySQL鎖定在內存。這能夠避免交換,可是也可能帶來危險:若是沒有足夠的可鎖定內存,MySQL在嘗試分配更多內存時就會崩潰。

解決問題:

第一種方法:修改系統對虛擬內存的控制
[root@mysql01 ~]# echo 0 > /proc/sys/vm/swappiness

#要想永久生效,將其配置寫入/etc/sysctl.conf文件中
[root@mysql01 ~]# echo "vm.swappiness=0" >> /etc/sysctl.conf
#令其當即生效
[root@mysql01 ~]# sysctl -p

第二種方法:修改innodb_flush_method參數
#注意innodb_flush_method是個全局變量,而且不支持動態修改,因此修改配置文件,重啓MySQL
[root@mysql01 ~]# vim /etc/my.cnf
innodb_flush_method=O_DIRECT

#添加其參數的配置,若是線上正在運行的數據庫,就要先:
mysql> stop slave;

#而後重啓MySQL
[root@mysql01 ~]# /etc/init.d/mysqld restart

對於修改memlock配置項,不推薦。

結果:新增長的slave負載正常。swap的使用也降到了10MB的樣子。

最後說下如何查看那個進程佔用swap?

第一步:
[root@mysql01 ~]# top

第二步:
按大寫的O

第三步:
輸入小寫字母p

第四步:
回車

顯示的結果以下圖:

相關文章
相關標籤/搜索