天天看點

98%的DBA不知道的資料庫記憶體知識點

|作者 鄧英明,騰訊雲DBA,擅長資料庫架構設計、故障診斷、性能優化,現主要負責騰訊雲資料庫MySQL/TDSQL-C/Redis的相關工作。

在日常工作中,時不時會收到記憶體使用率高的告警,那麼我們應該如何處理呢?本文将從Linux和MySQL兩個層面,介紹記憶體管理的相關知識點,希望能給大家帶來一些幫助,以便更好地應對記憶體問題。

一、如何看懂記憶體名額

遇到記憶體問題,可以先通過free、vmstat、top等指令,進行檢查。free指令,可以擷取系統記憶體的總體使用情況;vmstat指令,可以實時觀察記憶體的變化情況;top指令,可以進行排序,擷取記憶體占用大的程序。這裡簡單介紹一下free指令輸出(以CentOS 7為例): 

第一行是記憶體資料 1. total:記憶體總大小,對應于/proc/meminfo的MemTotal 2. used:已使用的記憶體大小,對應于/proc/meminfo的(MemTotal - MemFree - Buffers - Cached - Slab) 3. free:未使用的記憶體大小,對應于/proc/meminfo的MemFree 4. buff/cache:已使用的緩存大小,對應于/proc/meminfo的Buffers+Cached 5. available:可供使用的記憶體大小,這是一個預估值,對應于/proc/meminfo的MemAvailable 第二行是交換分區資料 1. total:交換分區總大小,對應于/proc/meminfo的SwapTotal2. used:已使用的交換分區,對應于/proc/meminfo的(SwapTotal - SwapFree)3. free:未使用的的記憶體大小,對應于/proc/meminfo的SwapFree 這裡值得注意的是,Linux作業系統會最大限度利用記憶體,空閑記憶體free少,不代表系統記憶體不夠用了。個人建議,一方面需要觀察記憶體增長的整體趨勢是否逐漸趨于平穩、以及used和buff/cache的變化情況;另一方面需要觀察是否頻繁使用到交換分區swap,當然了,這裡要避免NUMA和swapiness設定不正确帶來的幹擾。

二、MySQL如何使用記憶體

在MySQL中,記憶體占用主要包括以下幾部分,全局共享的記憶體、線程獨占的記憶體、記憶體配置設定器占用的記憶體,具體如下: 

在MySQL中,buffer pool的記憶體,是通過mmap()方式直接向作業系統申請配置設定;除此之外,大多數的記憶體管理,都需要經過記憶體配置設定器。為了實作更高效的記憶體管理,避免頻繁的記憶體配置設定與回收,記憶體配置設定器會長時間占用大量記憶體,以供内部重複使用。關于記憶體配置設定器的選擇,推薦使用jemalloc,可以有效解決記憶體碎片與提升整體性能。 是以,MySQL占用記憶體高的原因可能包括:innodb_buffer_pool_size設定過大、連接配接數/并發數過高、大量排序操作、記憶體配置設定器占用、以及MySQL Bug等等。一般來說,在MySQL整個運作周期内,剛啟動時記憶體上漲會比較快,運作一段時間後會逐漸趨于平穩,這種情況是不需要過多關注的;如果在穩定運作後,出現記憶體突增、記憶體持續增長不釋放的情況,那就需要我們進一步分析是什麼原因造成的。

三、到底是誰占用了記憶體

在絕大多數情況下,我們是不需要花費過多精力,去關注MySQL記憶體使用情況的;但是,也不能排除确實存在記憶體占用異常的情況,這個時候我們應該如何去進行深入排查呢?其實,MySQL官方就提供了強大的實時監控工具——performance_schema庫下的監控記憶體表,通過這個工具,我們可以很清晰地觀察到MySQL記憶體到底是被誰占用了、分别占用了多少。

我們可以選擇,在執行個體啟動時,開啟記憶體監控采集器,具體方法如下: 

禁用方法如下: 

執行個體運作時開啟

我們也可以選擇,在執行個體運作時,動态開啟記憶體監控采集器,具體方法如下:

因為采集器的實作原理,是在記憶體進行配置設定/回收時,更新相對應記憶體監控表的資料;換句話說,就是采集器隻能監控到開啟之後的記憶體使用情況;而MySQL很大一部分記憶體都是在執行個體啟動時就預先配置設定的,是以要想準确監控執行個體的記憶體使用率,需要在執行個體啟動時就開啟記憶體采集器。 

在performance_schema庫下,提供多個次元的記憶體監控表,具體如下: memory_summary_by_account_by_event_name:賬号緯度的記憶體監控表 memory_summary_by_host_by_event_name:主機緯度的記憶體監控表 memory_summary_by_thread_by_event_name:線程次元的記憶體監控表 memory_summary_by_user_by_event_name:使用者緯度的記憶體監控表 memory_summary_global_by_event_name:全局緯度的記憶體監控表 記憶體監控表均包括以下關鍵字段:COUNT_ALLOC:記憶體配置設定次數 COUNT_FREE:記憶體回收次數 SUM_NUMBER_OF_BYTES_ALLOC:記憶體配置設定大小 SUM_NUMBER_OF_BYTES_FREE:記憶體回收大小 CURRENT_COUNT_USED:目前配置設定的記憶體,通過COUNT_ALLOC-COUNT_FREE計算得到 CURRENT_NUMBER_OF_BYTES_USED:目前配置設定的記憶體大小,通過SUM_NUMBER_OF_BYTES_ALLOC-SUM_NUMBER_OF_BYTES_FREE計算得到 LOW_COUNT_USED:CURRENT_COUNT_USED的最小值 HIGH_COUNT_USED:CURRENT_COUNT_USED的最大值 LOW_NUMBER_OF_BYTES_USED:CURRENT_NUMBER_OF_BYTES_USED的最小值 HIGH_NUMBER_OF_BYTES_USED:CURRENT_NUMBER_OF_BYTES_USED的最大值 接下來,讓我們看一個正常運作執行個體的記憶體使用情況,具體如下: 

再看一個Bug #86821的場景,buffer pool占用最大記憶體正常,但是存儲過程占用3GB就比較異常了,存在記憶體洩漏的風險;由此可知,通過記憶體監控表,我們可以快速定位記憶體異常占用問題。 

另外,如果我們在記憶體監控表,看見一些比較陌生的event,可以翻閱官方文檔或源碼,繼續進一步解讀,例如 memory/innodb/os0event 

memory/innodb/hash0hash

四、總結

總的來說,隻要我們的作業系統/資料庫有一個相對合理的配置(NUMA、swapiness、jemalloc、innodb_buffer_pool_size等等),大多數情況是不需要關注記憶體問題的;如果非常不幸運地碰到記憶體占用異常問題,可以通過官方提供的實時監控工具——記憶體監控表,快速進行定位;不過需要注意的是,開啟記憶體采集器也會帶來一些問題,比如額外的記憶體占用和性能損耗,一般建議是在系統出現記憶體問題之後,再重新開機執行個體啟用,并等待複現。

dba