本篇文章是為使用Apache+MySQL,并為Apache耗盡內存而困擾的系統管理員而寫。如果您沒有耐心讀完本文,請參考以下步驟:
- 修改/etc/my.cnf,加上這樣一行:
log-slow-queries=queries-slow.log
- 重啟MySQL
- 酌情過上一段時間,執行mysqldumpslow命令,或直接查看datadir/queries-slow.log,從中找出執行超時的SQL語句。其中datadir是MySQL所有數據庫的存放路徑。
- 根據上一步找出的SQL語句的內容,找到相應的數據表和腳本文件,查看超時原因所在。可能的原因有:數據量確實太大,未使用索引加快查詢速度,等等。但程序沒寫好肯定是原因之一。
- 根據查出的原因酌情解決。
可能有很多系統管理員都為這樣的情形而困擾過:Apache進程動輒占用內存幾百兆,而且劇烈浮動,變化無常,有時甚至耗掉上G內存。在訪問者那里的表現就是網站速度極不穩定,間歇性抽風,有時甚至會有5-30分鐘無法打開網頁。這時候,你的上司或同事可能會急切地打來電話問:我們的網站怎么上不去了?我們今天還要xxxx呢!但這時你的SSH也無法像往常一樣忠實地給你一個“login:”,因為它也連不上服務器了。這個時候你的服務器在做什么呢?你找出各種理由向他們解釋之后,過上一段時間,網站能夠正常訪問了,你也終于SSH上了服務器(聽起來系統管理員真沒用),然后你會用dmesg或者在/var/log/messages里面發現一堆有關內存的信息,什么DMA了,HighMem了,其中會包含類似這樣一行:
Out of Memory: Killed process 6760 (httpd).
它告訴你的信息是,你的服務器物理內存耗盡,也沒什么好向交換區淘汰的了,只好殺掉了你的Apache的一個進程。之所以選擇這個進程,基本上就是因為它占用的物理內存最多。
很沒面子的說,我被這樣的問題困擾了大半年。我曾在網上瘋狂地搜索解決的辦法,那些文章一般都會告訴你修改httpd.conf,把MinSpareThreads或是MaxSpareThreads或是MaxRequestsPerChild設小一點,我嘗試過各種設定,都沒有明顯的效果。我甚至曾極端地經把MaxRequestsPerChild設為1,但是Apache的進程還是會淤積,超過ServerLimit設定值,直到耗盡內存。
我曾經懷疑過是PHP內存泄漏,因為PHP官方不保證PHP的各種擴展是線程安全的,推薦使用prefork MPM而不是worker MPM.于是我也曾經把ThreadsPerChild設為1,因為這樣每個進程也就只有一個線程,在一定意義上相當于prefork MPM,但是這種做法還是沒有效果,問題依舊。
大約一周以前,我在檢查MySQL的運行狀態的時候,發現有一項Slow_queries數值很高,我敏感地認識到可能是某個程序的SQL語句沒有寫好,而且很可能就是這樣的程序導致Apache進程淤積。然后我翻閱了MySQL手冊中有關Slow query log 的部分,按照那里的指示,修改/etc/my.cnf,在mysqld那一段加上如下一行:
log-slow-queries=queries-slow.log
重啟MySQL。過上一段時間以后,用mysqldumpslow命令找出了執行超時的那條語句,根據那條查詢的內容,我找出相應的腳本。那是一個留言本頁面,程序確實沒有寫好,成了垃圾廣告的樂園。有人通過這個頁面向數據庫里面寫入了超過10萬條的流言,而正常的留言還不到300條。而且這個程序里面分頁的做法,居然是查詢數據表里面所有10萬條數據并排序,而用到的只有區區10條。竟然不使用LIMIT語句,我~@#$% ^&*!后來證實這個留言本已經是廢棄的了(我想也不可能是正在使用的,因為每次都執行超時,已經無法使用了),就刪掉了。之后,就再也沒有出現過進程淤積、內存耗盡的問題。在ThreadsPerChild=64的情況下,每個Apache進程的內存一般就幾十兆,最多的時候也就只有200M,進程數量也很少超過六七個。
歷經千辛萬苦,終于搞定了這個問題。在此加以總結,希望本文能夠給跟我一樣被程序員害慘的系統管理員們一點點幫助。
好文章啊 ,留下 做參考
文章列表