mysql一直使用swap,導致swap空間用盡變卡

大致操作步驟:

1.調整內核參數,/proc/sys/vm/swapiness

2.調整mysql參數,innodb_flush_method

3.加mysql配置參數memlock,將mysql鎖定在內存重防止被swapping out

4.手動釋放swap空間,swapoff /xx/xx  和swapon /xx/xx


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

避免將分配給MySQL的地址空間映射到swap上。對於頻繁進行讀寫操作的系統而言,數據看似在內存而實際上在磁盤是非常糟糕的,響應時間的增長很可能直接拖垮整個系統。所以,作爲運維人員,怎樣做到儘量避免MySQL慘遭Swap的毒手將顯得尤爲重要!


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

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

服務器產生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分區上。

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

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才能生效。

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;

1
2
3
4
5
6
7
8
9
10
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配置區域下增加下面一句(根據機器本身的內存配置來設置下面這個參數值):

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

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

開啓SWAP分區的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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分區的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
當系統出現內存不足時,開啓 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。這等同於告訴內核除非虛擬內存完全滿了,否則不要使用交換區。下面是如何檢查這個值的例子:

1
2
cat /proc/sys/vm/swappiness
60

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

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

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

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

解決問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第一種方法:修改系統對虛擬內存的控制
[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的樣子。



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