博客 / 詳情

返回

第45問:MySQL 的內存突增, 該如何診斷

在 第44問 中, 我們使用 tcmalloc 提供的工具, 來查看 MySQL 的內存分配

該方法對性能影響不大, 可以在生產環境運行, 但需要將 MySQL 的分配器配置成 tcmalloc

在本次實驗中, 我們介紹另外一種方法, 針對於 MySQL 的內存突增情況進行診斷

實驗

我們依然寬油起一個數據庫:

本實驗中, 我們需要模擬MySQL的內存突增的情況. 我們從 MySQL 的 bug 庫裏找到一個易於復現的相關 bug: https://bugs.mysql.com/bug.ph...

這個 bug 的描述很清晰, 並提供了一個 SQL 腳本, 直接執行該腳本就可以復現內存激增的情況:

我們來試一下:

當前 MySQL 的內存佔用為181M

執行腳本:

在腳本執行過程中, 我們會發現 MySQL 使用的內存在不斷上漲

現在我們來診斷 MySQL 對內存的使用. 在腳本執行過程中, 同時執行如下命令進行觀測:

小貼士

什麼是系統調用mmap?

簡單來説, MySQL不是直接向Linux申請內存, 而是向glibc申請內存. glibc會維持一個內存池, 當glibc發現內存池吃緊時, 會通過系統調用mmap(或者brk)向Linux申請內存.

所以我們監聽系統調用mmap, 也就監聽了MySQL在什麼情況下需要大量內存 (即什麼時候glibc的內存池吃緊了)


至於glibc內存分配的機制, 可參考閲讀: https://sploitfun.wordpress.c...

可以看到在當前目錄下生成了 perf.data :

我們將 perf.data 轉換成可讀的方式:

簡單看一下 perf.out , 我們會看到每次 mmap 的調用都記錄了足夠的信息, 包括:

  1. 圖中紅色標記的部分, 是要求分配內存的線程號. 有了線程號, 我們就可以找到是哪個SQL在佔用內存.
  2. 圖中藍色標記的部分, 是分配內存的大小
  3. 圖中綠色標記的部分, 是分配內存時的堆棧信息

有了線程號和堆棧信息, 我們就可以判斷是哪個 SQL(或者 MySQL 的哪個內部線程), 在什麼情況下要求分配內存

本例中, 我們判斷29735號進程, 在 create view 這個操作中, 打開表時需要分配內存

當然, perf.out 文件很長, 大家需要將信息都聚合在一起, 再判斷誰是最消耗內存的

侷限

本實驗所介紹的方法是有侷限的: 本方法適用於 MySQL 內存激增的情況, 在其他情況下 (比如內存緩慢並持續增長), 本方法不一定能觀測到準確的信息.

如小貼士所述,MySQL 向 glibc 申請內存 (第一步), glibc 的內存池吃緊時,glibc 再向 Linux 申請內存 (第二步).

在內存激增的情況下, 第一步的發生 大概率會導致 第二步的發生, 我們觀測系統調用 mmap , 實際上是觀察到了第二步的發生, 推導出第一步的原因.

而在內存緩慢增長的情況下, 第一步的發生 小概率會導致 第二步的發生, 導致無法從第二步推導第一步.

比如: 由於某個原因, 內存緩慢增長, 當 glibc 的內存池即將吃緊時, 發生了其他業務 SQL , 導致這些業務 SQL 觸發了 glibc 內存池吃緊, 那麼我們診斷出內存增長的原因是因為業務 SQL , 而真正的原因被藏了起來.

使用本方法時, 希望大家能注意到這個侷限.


關於 MySQL 的技術內容,你們還有什麼想知道的嗎?趕緊留言告訴小編吧!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.