MySQL 內存交換區引起的一場“血案”

http://lgdvsehome.blog.51cto.com/3360656/1280247


MySQL 內存交換區引起的一場“血案”
2013-08-22 00:49:36
原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://lgdvsehome.blog.51cto.com/3360656/1280247

                 MySQL Performance murder -- SWAP

故事情節:

最近公司上了一個新的項目,關於搜索一塊的項目。其程序會調用大量的SQL,包括各種條件的搜索,模糊的匹配,聯動的效果,etc。其目的,是提高百度or谷歌的爬蟲的量;其效果,確實有了,從上線了,據SEO相關部門的統計爬蟲抓取的量大概翻了5倍。各個都Happy,唯獨我Happy不起來,因爲MySQL的負載極其不穩定。當晚上線時,就發現Slave的負載有點猛,CTO一聲令下,再加一臺Slave,點頭做事就OK了,呵呵。另加了一臺Slave,發現Slave的負載,確實都回歸正常了,本以爲息事寧人呢,,,誰知,大概隔了3,4個小時吧,新添加的Slave的負載暴增,大概160+,麼情況?原來的slave到是顯得平靜無事。大概過了1個小時,新增加的Slave的負載迴歸正常,大概也就是零點幾的樣子;隔幾個小時,負載又是直接飆升100+。沒得說,身爲DBA,就是要Troubleshooting。

Troubleshooting的過程:

1:因爲是間接性抽風,所以認爲肯定和前臺的訪問的頻率,及調用的scope相關;就結合前臺的日誌,發現確實是這樣。都是百度的or谷歌的爬蟲瘋狂的訪問新的項目(搜索)時,新增的Slave的負載就會瘋一樣的增長。可是爲何原來的Slave爲何沒有太大的波動呢?

2:考慮到新增的server,因爲內存,CPU等硬件的配置和原來數據庫的server都不一樣(其實是新增的Slave比原來的Slave的內存少了一半),必然配置參數的值也會不同。所以就從MySQl的配置文件查起,如:sort_buffer_size的大小(因爲考慮到有許多SQL包含排序),join_buffer_size(用於連接的緩存的大小),max_connections(最大連接數,可是通過show processlist;發現也沒有超過設置的值),innodb_buffer_pool_size(確認是否爲物理內存的合適比例)等等。實際自己也犯2了,呵呵,身爲DBA,這都是基本的配置,按照正常的邏輯判斷也不該有多大的問題,只要把這些參數設置在合理的範圍內,不會引起負載那麼大的波動。那爲何呢?考慮到是新裝的系統,莫非是系統的參數引起的?

3:於是開始查找關於一些TCP/IP,打開文件最大數等等,按照網友的最優調整,只能說希望越大失望越大吧,不起任何作用。這尼瑪到底怎麼回事呢?要是兩臺Slave出現相同的情況,也好解決了,可是就這臺新增加的Slave有問題,,,沒得選,靜下心來,恍然大悟,才考慮到系統的CPU,內存,IO的波動。

4:通過排查,CPU正常,但是問題出來了,發現free -m查看內存時,發現SWAP既然使用了300MB,怎麼會使用這麼多,所以一口咬定就死SWAP搗的蛋。通過vmsata,發現si和so的值不斷的變化,所以更加的肯定是發生了內存交換。既然找到問題了,那就解決吧。

內存交換區:

當操作系統因爲沒有足夠的內存而將一些虛擬內存寫到磁盤就會發生內存交換。

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

因爲內存交換對用戶進程不可見,MySQL(或存儲引擎)並不知道數據實際上已經移動到磁盤,還會意味在內才能中。

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

在GNU/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
echo 0 > /proc/sys/vm/swappiness
#要想永久生效,將其配置寫入/etc/sysctl.conf文件中
echo "vm.swappiness=0" >> /etc/sysctl.conf
#令其立即生效
$ sysctl -p

第二種方法:修改innodb_flush_method參數

1
2
3
4
5
6
7
#注意innodb_flush_method是個全局變量,並且不支持動態修改,所以修改配置文件,重啓MySQL
vi /etc/my.cnf
innodb_flush_method=O_DIRECT
#添加其參數的配置,如果線上正在運行的數據庫,就要先:
mysql> stop slave;
#然後重啓MySQL
/etc/init.d/mysqld restart

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

結果:

新增加的server,運行正常,爬蟲照常抓,負載很穩定在4以下。swap的使用也降到了8MB的樣子。呵呵,終於可以小Happy下了。

總結:

1:troubleshooting的經驗不足吧,自我評定。

2:swap對於操作系統,對於MySQL的性能影響很大。

3:要對swap的監控重視起來。

分享蝦米米:

可能有的同學對於如何查看那個進程佔用swap的大小比較感興趣,如下方法:

1
2
3
4
5
6
7
8
第一步:
top
第二步:
按大寫的O
第三步:
輸入小寫字母p
第四步:
回車

顯示的結果圖如:

希望這場swap的“血案”,讓我們共同成長!!!

本文出自 “Focus on the database” 博客,請務必保留此出處http://lgdvsehome.blog.51cto.com/3360656/1280247


發佈了76 篇原創文章 · 獲贊 16 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章