MySQL內存不釋放分析

2021-02-16 懶人的記錄

問題分析場景1 使用sysbench壓測資料庫場景2 load 一個很大事務的insert語句問題突破測試jemalloc場景1使用sysbench壓測資料庫場景2 load 一個很大事務的insert語句小結MySQL到底有沒有釋放內存?通過gdb調試結論

線上MySQL資料庫發現一些實例,內存使用不斷增高,並且當連接數斷開後內存不會釋放,最終導致的結果是被作業系統OOM

問題分析

模擬兩個場景來分析此問題:

場景1 使用sysbench壓測資料庫

使用sysbench壓測MySQL,等待連接斷開後,使用top查看mysqld進程,內存使用情況
將mysql innodb_buffer_pool設置為128M,方便觀察內存增長情況。

啟動MySQL後內存使用情況,大約在150M:


sysbench壓測60s

sysbench --db-driver=mysql /usr/share/sysbench/oltp_read_only.lua --mysql-host=127.0.0.1 --mysql-port=3320  --mysql-db=sbtest --mysql-user=root --mysql-password=123456 --tables=16 --table_size=500000 --threads=128 --time=60 --report-interval=1 run

觀察內存使用情況:



內存增長到540M左右,但是內存並沒有隨著sysbench連接斷開而釋放

場景2 load 一個大事務的insert語句

重啟MySQL觀察內存使用情況, 大約佔用170M:


文件大小43M


文件內容:

INSERT INTO dummy VALUES
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
(1,'This is a dummy value'),
..

source 文件:

mysql> source test.sql

source完成後觀察內存使用情況:



佔用到1G,同樣也是,沒有隨著連接斷開,內存進行釋放。

問題突破

通過上面兩個例子,能證明MySQL連接斷開後,內存沒有釋放。長時間下去就會導致內存不斷增高,查看bugs.mysql 發現有人提過此問題:
https://bugs.mysql.com/bug.php?id=83047 --裡面有人提出load個大事務,復現此問題。
同樣percona版本也有人提過:
https://bugs.launchpad.net/ubuntu/+source/mysql-5.7/+bug/1710146
在連接中,有人提到過使用jemalloc好像能夠解決此問題。

測試jemalloc

同樣是測試上面兩種場景, 使用jemalloc方式如下:

yum install jemalloc
在mysqld_safe中,最前面添加如下信息:
export LD_PRELOAD="/lib64/libjemalloc.so.1"
重啟啟動mysql實例

使用pt工具確認是否是用你了jemalloc
pt-mysql-summary  -S /tmp/mysql-3320.sock --user root --password 123456|grep -A 5 "Memory management" 
顯示如下信息,則代表使用了jemalloc

場景1使用sysbench壓測資料庫

啟動MySQL後內存使用情況,大約在150M:


sysbench壓測60s

sysbench --db-driver=mysql /usr/share/sysbench/oltp_read_only.lua --mysql-host=127.0.0.1 --mysql-port=3320  --mysql-db=sbtest --mysql-user=root --mysql-password=123456 --tables=16 --table_size=500000 --threads=128 --time=60 --report-interval=1 run

觀察內存使用情況:



壓測時內存增長到550M左右,隨著sysbench連接斷開而釋放了一部分內存, 保持在370M左右。

場景2 load 一個大事務的insert語句

重啟MySQL觀察內存使用情況, 大約佔用150M:


source 文件:

mysql> source test.sql

source完成後觀察內存使用情況:



隨著SQL執行完成,內存釋放到了300M。

小結

通過以上對比,可以得出,jemalloc內存分配方式,確實可以使內存快速釋放給作業系統,減少系統內存使用過高而被OOM

MySQL到底有沒有釋放內存?

繼續分析MySQL實際執行中,到底有沒有主動釋放內存?在哪裡釋放的內存?

通過gdb調試

先登錄到mysql中

另外一個窗口,執行gdb attach 到mysql進程

/opt/rh/devtoolset-8/root/bin/gdb  -p  `ps -ef|grep  -w mysqld|grep -v grep | awk {'print $2'}`

設置斷點

(gdb) b /mydata/Project/mysql-server/sql/sql_parse.cc:1947
(gdb) b connection_handler_per_thread.cc:321
Breakpoint 2 at 0x166ee7f: file /mydata/Project/mysql-server/sql/conn_handler/connection_handler_per_thread.cc, line 321.

gdb 中執行c

(gdb) c
Continuing

mysql客戶端source 文件

mysql> source test.sql

此時可能需要等待一段時間,因為文件比較大,mysql client 需要進行解析文件,只要在gdb 窗口等待即可,不用執行任何命令,直到出現下面這種情況:

gdb) c
Continuing.
[Switching to Thread 0x7fea2b008700 (LWP 29406)]

Thread 28 "mysqld" hit Breakpoint 1, dispatch_command (thd=0x7fea02819000, com_data=0x7fea2b007c90, command=COM_QUERY)
    at /mydata/Project/mysql-server/sql/sql_parse.cc:1947
1947      free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));

此時就可以開始調試,查看MySQL內存使用情況:

(gdb) p thd->main_mem_root
$1 = {free = 0x7fe9c2406020, used = 0x7fe9c29b3020, pre_alloc = 0x7fea02827020, min_malloc = 32, block_size = 8160, block_num = 863,
  first_block_usage = 0, max_capacity = 0, allocated_size = 844547312, error_for_capacity_exceeded = 0 '\000', error_handler = 0x14baa8b
     <sql_alloc_error_handler()>, m_psi_key = 8}

這裡看到allocated_size=844547312 大約是800M,此時mysqld進程佔用內存情況:



執行source 之前的內存使用情況+800M 約等於 目前的1.1g。
輸入'n'執行下一步free_root後,在查看內存:

gdb) n
1961      thd->profiling.finish_current_query();
(gdb) p thd->main_mem_root
$2 = {free = 0x7fea02827020, used = 0x0, pre_alloc = 0x7fea02827020, min_malloc = 32, block_size = 8160, block_num = 4,
  first_block_usage = 0, max_capacity = 0, allocated_size = 8208, error_for_capacity_exceeded = 0 '\000',
  error_handler = 0x14baa8b <sql_alloc_error_handler()>, m_psi_key = 8}

此時allocated_size = 8208,也就是在MySQL層,執行完SQL後內存已經釋放了,top看下作業系統情況:



這裡使用的是jemalloc,內存進行了釋放(當前佔有內存約300M),如果是默認的glibc,則內存不會釋放。
這裡還分析到,內存的釋放是SQL語句級別的,並不是整個事務結束後才釋放,而是SQL執行完後即釋放。

繼續將會話退出,則會觸發另外一個斷點



會話退出,會接受到COM_QUIT 命令,接著會釋放整個線程的內存,這時再看下top中mysqld內存使用情況:



會看到隨著會話退出,內存又釋放了40M左右,這部分內存就是mysql中緩存的source 文件大小,在SQL執行完後,會話不退出時,可以通過查看performance 表查看線程內存使用情況:



能看到,正好是等於文件的大小。

kill 也會導致NET::buff釋放
通過測試,如果SQL執行完成,在另外一個會話Kill此線程,NET::buff也會被釋放

這裡的NET::buff並沒有隨著SQL執行完成釋放,查看官方文檔net_buffer_length的介紹:



大概意思是:
每個客戶端會話線程會分配兩個緩衝區:連接緩衝區和結果集緩衝區,並且可以動態放大到 max_allowed_packet 系統變量指定的字節大小, 每個 SQL 語句執行完成 之後,結果集緩衝區自動縮小到 net_buffer_length 變量指定的大小。然後文檔並沒有提到連接緩衝區會被釋放,也就是說會被一直緩存,直到連接斷開。

結論

mysql中會主動釋放內存

釋放內存是語句級別,不是會話級別

NET::buff內存是需要會話退出後,內存才會釋放

kill也會釋放會話所佔有的內存

jemalloc比glibc內存分配機制更好一些,能及時將內存釋放給作業系統

相關焦點

  • JavaScript中的內存釋放
    這時候很多同學可能會疑惑了,我們不是講解JavaScript的內存釋放的嘛?怎麼還講起了作用域的內容,稍安勿躁...在JavaScript的內存釋放中要用到這些知識呢!02堆內存的釋放對象數據類型或者函數類型在定義的時候,首先都會開闢一個堆內存,堆內存有一個引用地址,如果外面有引用這個地址,我們就說這個內存被佔用了,就不能銷毀了。
  • MySQL 大量 Opening tables 案例分析-愛可生
    2 塊,第一塊是分析1402 個線程的堆棧,看下是在等待什麼鎖資源,第二塊是分析這個鎖資源被哪個線程佔用了,為啥不釋放?  先來看第一部分,過程就是從 MySQL 快照中找到一個如下類似的線程(這裡去掉了一些參數顯示),然後從 MySQL 源碼中一步一步分析函數調用。
  • MySQL InnoDB存儲引擎啟動過程源碼分析
    調用 innobase_start_or_create_for_mysql 函數,這個函數非常重要,後面詳細分析這個函數。innobase_start_or_create_for_mysql 函數:這個函數位於源碼文件storage/innobase/srv/srv0start.cc,主要作用是啟動innodb引擎,如果數據目錄為空,則會創建一個新的資料庫所需要的文件。
  • MySQL優化原理分析及優化方案總結
    但要如何評估打開緩存是否能夠帶來性能提升是一件非常困難的事情,也不在本文討論的範疇內。當然查詢緩存系統本身是非常複雜的,這裡討論的也只是很小的一部分,其他更深入的話題,比如:緩存是如何使用內存的?如何控制內存的碎片化?事務對查詢緩存有何影響等等,讀者可以自行閱讀相關資料,這裡權當拋磚引玉吧。MySQL通過關鍵字將SQL語句進行解析,並生成一顆對應的解析樹。這個過程解析器主要通過語法規則來驗證和解析。
  • 獨家|Linux進程內存用量分析之堆內存篇
    而glibc默認的ptmalloc卻不自帶這樣的工具,一種排查方法是去了解ptmalloc的實現和結構以後編寫程序或者gdb腳本去分析進程的內存結構,我們接下來要介紹的一種內存分析工具就是以這種方法實現的。1.環境58自研的搜尋引擎Esearch底層使用C++實現。Esearch在內存管理方面針對不同的場景會有不同的策略。
  • 中間件mysql-proxy的一些細節
    mysql-proxy是官方提供的mysql中間件產品可以實現負載平衡,讀寫分離,failover等,但其不支持大數據量的分庫分表且性能較差。Atlas(是由 Qihoo 360, Web平臺部基礎架構團隊開發維護的一個基於MySQL協議的數據中間層項目。)今天推薦一篇文章,深度分享mysql-proxy的細節。
  • C/C++程序內存問題分析
    本次,FourExperts小組整理了一些內存問題,希望通過對這些問題的分析,大家能夠了解到程序內存問題的類型以及一些基礎知識。從分析結果來看,這類問題主要可以分為幾類:指向內存空間的指針丟失申請的內存沒有釋放或釋放不完全內存無限申請而不釋放使用的數據結構不釋放內存指針丟失問題,是最常見的一類內存洩露的原因。
  • MySQL 優化案例 - select count-愛可生
    ,可以看到確實運行很慢,要 40 多秒左右,確實很不正常~mysql> select count(*) from api_runtime_log;++| count(*) |++| 5718952 |++1 row in set (42.95 sec)我們再去看下表結構,看上去貌似也挺正常的~存在主鍵,表引擎也是 InnoDB,字符集也沒問題。
  • MySQL 性能優化之骨灰級,高階神技 !
    分析slowlog文件的不常用但好用的工具:zabbix                  監控主機、系統、資料庫(部署zabbix監控平臺)pt-query-digest         分析慢日誌mysqlslap               分析慢日誌sysbench
  • 使用 mtrace 分析 「內存洩露」
    mtrace 工具的主要思路是在我們的調用內存分配和釋放的函數中裝載 「鉤子(hook)」 函數,通過 「鉤子(hook)」 函數列印的日誌來幫助我們分析對內存的使用是否存在問題。很顯然,這段代碼的第 13 行存在問題,由於第 13 行分配的內存沒有被釋放掉,會引起 「內存洩漏」。以上是我們人工閱讀代碼後的分析結果,現在我們來看看如何利用 mtrace 機制幫助我們得到相同的結論。
  • MySQL 備份數據那點事
    當使用此次參數 mysqldump 正在導出數據時,為了確保得到有效的結果(正確的表內容和二進位坐標),要保證其他的連接不使用 ALTER_TABLE, CREATE_TABLE, 因為一致性的讀不會隔離這些操作,所以當一個表被導出時會導致 mysqldump 使用 SELECT操作會檢索到不正確或者發生錯誤。
  • MySQL死鎖分析與解決之路
    本文將會對死鎖進行相應介紹,對常見的死鎖案例進行相關分析與探討,以及如何去儘可能避免死鎖給出一些建議。--什麼是死鎖 --死鎖是並發系統中常見的問題,同樣也會出現在資料庫MySQL的並發讀寫請求場景中。當兩個及以上的事務,雙方都在等待對方釋放已經持有的鎖或因為加鎖順序不一致造成循環等待鎖資源,就會出現「死鎖」。
  • 【藏經閣】C/C++程序性能案例分析-內存問題分析
    從造成洩漏的根本原因上劃分,我們可以簡單地將問題分為兩類:▌3.1.1 程序設計不合理從分析結果來看,這類問題主要可以分為幾類:指向內存空間的指針丟失申請的內存沒有釋放或釋放不完全、內存無限申請而不釋放使用的數據結構不釋放內存
  • 我想說:mysql的join 真的很弱
    二、問題分析: 對這個結論,你是否有懷疑呢?也不知道是哪位先哲說的不要人云亦云,今天我設計sql,來驗證這個結論。(實驗沒有從代碼角度分析,目前達不到。可以把mysql當一個黑盒,使用角度來驗證這個結論) 驗證結論的時候,會有很多發現,各位往後看。
  • MySQL優化:學會使用show profile和trace分析慢查詢
    MySQL優化:定位慢查詢的兩種方法以及使用explain分析SQL在上一節我們學習了定位慢 SQL 及使用 explain 分析慢 SQL,我們也提到了分析慢 SQL 還有 show profile 和 trace 等方法,本節就重點補充學習這兩種方法。
  • 七日殺內存不足怎麼辦 自動釋放內存軟體推薦
    ,幾乎十個玩家中就有八九個人會說玩遊戲的時候很卡,這個不容置疑還是由於七日殺內存不足的問題導致。下面巴士單機遊戲小編為大家介紹下七日殺釋放內存方法,推薦一款自動釋放內存軟體,感興趣的朋友不要錯過。 使用槍枝打倒樹木速度會快點哦   七日殺自動釋放內存軟體:   下載地址:http://kuai.xunlei.com/d/2Bh2Brx4ivMjUgQAa1a
  • 幾個常見而嚴重的 MySQL 問題分析 | 運維進階
    很多時候發生資料庫報錯時,不一定就是資料庫的問題,不一定非得急著呼叫資料庫人員解決。我們要形成這樣一種意識,我們不只是寫應用的,我們是寫金融系統的,我們理應具備一定的問題排查解決能力。本文將抽取幾個常見而嚴重的MySQL問題進行分析,並給出深度解答。藉以大家幫助思考。
  • 史上最全的MySQL高性能優化實戰總結!
    SHOW ENGINE INNODB STATUS Innodb   SHOW PROCESSLIST    explain             how index           slow-log            mysqldumpslow       不常用但好用的工具zabbix
  • MySQL 全面優化,讓你的 MySQL 飛起來!
    :分析slowlog文件的不常用但好用的工具:1)Zabbix:監控主機、系統、資料庫(部署zabbix監控平臺)2)pt-query-digest:分析慢日誌3)MySQL slap:分析慢日誌4)sysbench:壓力測試工具5)MySQL profiling:統計資料庫整體狀態工具    6)Performance
  • 學會用 Mysql show processlist 排查問題
    mysql show full processlist 查看當前線程處理情況事發現場每次執行看到的結果應該都有變化,因為是實時的,