記錄一次現網MySQL內存增長超限問題定位過程


問題現象
現網物理機內存近幾日內爆漲使用率超過了90%,可用內存從250G,降低到20G以下,報告警。
服務器使用情況來看,並沒有什么異常。除了QPS緩慢增長外。

MySQL內存分配結構

定位這個問題,先了解一下MySQL的內存分配知識。

MySQL的內存分配分為兩部分,一部分是啟動之初就分配的,主要是buffer_pool_size,key_buffer_size(本例256M)等。
還有一部分是每個連接建立並執行查詢等操作時分配的。
https://dev.mysql.com/doc/refman/5.6/en/memory-use.html

MySQL的內存分配分為兩部分,一部分是啟動之初就分配的,主要是buffer_pool_size,key_buffer_size(本例256M)等。
還有一部分是每個連接建立並執行查詢等操作時分配的。
https://dev.mysql.com/doc/refman/5.6/en/memory-use.html


每個連接分配的(當有相應的SQL操作時,非活動線程占用的內存很小)
(read_buffer_size+read_rnd_buffer_size+sort_buffer_size+thread_stack+
join_buffer_size+binlog_cache_size)=8M+32M+8M+256K+2M+32K~=51M

但是,需要注意的是,每個連接。有需要的時候會動態的擴展到max_allowed_packet,也即64M。也許這就是文件中很多64M的內存塊分配的原因吧?

我猜想是這樣,但是實際上確不太正確。因為Threads_connected | 75, Threads_created | 622,狀態信息顯示所有的連接創建數總共才622個,而且按文檔所述連接所用的內存應該主要按照活動的連接進程來計算。 現網即時狀態顯示,活動連接總計才 Threads_running | 3 個。

max_allowed_packet = 64M
Each thread that the server uses to manage client connections requires some thread-specific space. The following list indicates these and which system variables control their size:
A stack (thread_stack)
A connection buffer (net_buffer_length)
A result buffer (net_buffer_length)
The connection buffer and result buffer each begin with a size equal to net_buffer_length bytes, but are dynamically enlarged up to max_allowed_packet bytes as needed. The result buffer shrinks to net_buffer_length bytes after each SQL statement. While a statement is running, a copy of the current statement string is also allocated.
When a thread is no longer needed, the memory allocated to it is released and returned to the system unless the thread goes back into the thread cache. In that case, the memory remains allocated.

 

注:還有上面所述的關於read_buffer_size, read_rnd_buffer_size
根據我的經驗最大只能配置2M左右。太大了,非但不會提升性能,還會影響性能。

至於原因,記得是在某篇文章里面提到過。不太記得出處在哪了,等我找到了再更新。

但這個參數,我是不打算改了。因為是現網,還有這是客戶系統,就算有再充分的原因,沒有嚴重問題也不可能接受你的建議修改的。

 

用PMAP查看內存具體分配

除了Buffer pool預先分配的內存230G是在已知的情況下。
內存PMAP統計信息顯示其中有1659個64M=104G的內存塊分配。
問題應該就是出在這里,到底是什么導致很多不同的64M的內存塊分配,而且分配后還沒有釋放?

 

下一步對策

從前面的分析來看,沒有任何一點能解釋問題發生的根本原因。
我猜測問題有可能是內存泄露,也許是內存未釋放。

那接下來,我們該怎么辦呢?目前來看,我幾乎沒有辦法。

內存問題的定位工具,由於現網無法使用。
Valgrind

其實還可以查詢一下performance_schema關於內存的統計信息。
但可惜這個是5.7之后的版本才有的功能。現網5.6版本不支持。

最后的辦法:
1)在實驗室搭建一個和現網一樣的環境(數據一樣)。但沒有流量數據,可能也難以復現問題。
2)升級到5.7,反正早晚都要升級的。升級后,關於內存這一塊就可以查看更多的信息了。

注:關於內存不釋放我也懷疑過有可能是OS的問題,比如說solaris系統就是如此(屬於solaris系統的正常現象)。
https://bugs.mysql.com/bug.php?id=77616

Solaris系統下,當內存被進程主動釋放時,OS不會回收內存到OS,只會標記內存為未使用,等下次進程再需要時再指配給進程,這就是所謂內存重用。表向為進程占用內存一直維持高位不會下降。
而Redhat, centos是會把內存回收給OS,表向為進程占用內存下將。現網的機器是centos核的,應該不會有此問題。

 

MySQL 5.7 performance_schema 內存查詢

 

其它檢查項

檢查是否有使用內存表,結果是沒有(因為只有系統虛內存表)
select * from information_schema.tables where engine='MEMORY‘

檢查performance_schema占用內存,結果顯示為約為761M
SHOW ENGINE PERFORMANCE_SCHEMA STATUS
Type: performance_schema
Name: performance_schema.memory
Status: 798686336
167 rows in set (0.00 sec)

 

白高興

突然有同事告訴我,現網還大量使用了存儲過程。這個是我之前忽略的一點。

 

我一查,是不是有存儲過程的Memory leak的BUG?

結果真有。我興沖沖的用測試機器測試了一下,結果是死活不出現我所期待的現象。

再一查,這個BUG,居然在目前使用的版本之前已經修復了。

https://bugs.mysql.com/bug.php?id=76349

 

后續

問題定位兩天無果。隔了一個周末來看,居然告訴我系統被Monitor重啟了,問題沒了。

只能等待下次出現問題再說了。

 

雖然沒有定位出問題,但過程也還是很有參考意義的。

問題天天有,世界照樣轉。不說了,得去別的地方搬磚了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM