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

老楊伏櫪發表於2021-08-04

問題現象
現網物理機記憶體近幾日內爆漲使用率超過了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重啟了,問題沒了。

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

 

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

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

相關文章