華爲雲數據庫GaussDB(for Cassandra)揭祕第二期:內存異常增長的排查經歷

摘要:內存的異常增長對於程序來說是一個致命的問題,因爲其可能觸發OOM,進程異常宕機,業務中斷等結果,所以對內存進行合理的規劃使用及控制就顯得尤爲重要。

華爲雲數據庫GaussDB(for Cassandra) 是一款基於計算存儲分離架構,兼容Cassandra生態的雲原生NoSQL數據庫;它依靠共享存儲池實現了強一致,保證數據的安全可靠。核心特點是:存算分離、低成本、高性能。

問題描述

GaussDB(for Cassandra)自研架構下遇到一些挑戰性問題,比如cpu過高,內存泄漏,內存異常增長,時延高等問題,這些也都是開發過程中遇到的典型問題。分析內存異常增長是一個比較大的挑戰,內存的異常增長對於程序來說是一個致命的問題,因爲其可能觸發OOM,進程異常宕機,業務中斷等結果,所以對內存進行合理的規劃使用及控制就顯得尤爲重要。通過調整cache容量,bloom過濾器大小,以及memtable大小等等,實現性能提升,讀寫時延改善等效果。

在線下測試過程中發現內核在長時間運行後,內存只增不減,出現異常增長的情況,懷疑可能存在內存泄漏。

分析&驗證

首先根據內存使用,將內存分爲堆內和堆外兩個部分,分別進行該兩塊內存的分析。確定有問題的內存是堆外內存,進一步對堆外內存分析。引入更高效的內存管理工具tcmalloc,解決內存異常增長問題。下面爲具體分析驗證過程。

確定內存異常區域

使用jdk的jmap命令和Cassandra的監控(配置jvm.memory.*監控項)等方法,每隔1min採集jvm的堆內內存及進程整體內存。

啓動測試用例,直到內核的整體內存達到上限。分析採集到的堆內內存和進程內存變化曲線,發現其堆內內存仍保持相對穩定,未出現一直持續上漲,但期間內核的整體內存仍然在持續上漲,兩者的增長曲線不符。即問題應該發生在堆外內存。

堆外內存分析驗證

glibc內存管理

使用pmap命令打印進程的內存地址空間分佈,發現有大量的64MB的內存塊和許多內存碎片,該現象與glibc的內存分配方式有關。堆外內存的使用和進程整體的內存增長趨勢相近,初步懷疑該問題是由堆外內存導致。加之glibc歸還內存的條件苛刻,即內存不易及時釋放,內存碎片多,猜測問題和gblic有關係。當內存碎片過多,空閒內存浪費嚴重,最終進程內存的最大使用量會出現超過預期計劃最大值的可能,甚至出現OOM。

tcmalloc內存管理

引入tcmalloc內存管理器,代替glibc的ptmalloc內存管理方式。減少過多的內存碎片,提高內存使用效率,本次分析驗證採用gperftools-2.7源碼進行tcmalloc的編譯。運行相同的測試用例,發現內存仍在持續上漲,但是上漲幅度較之前降低,通過pmap打印出該內存地址分佈情況,發現之前的小內存塊和內存碎片顯著減小,說明該工具有一定優化效果,印證了前面提到內存碎片過多的猜測。

但是內存異常增長的問題仍然存在,有點像是tcmalloc的回收不及時或者不回收導致。實際上tcmalloc的內存回收是比較 "reluctant" 的,主要是爲了當再次需要內存申請時可以直接使用,減少系統調用次數,提高性能。基於此原因,下來進行手動調用其釋放內存接口releasefreememory。發現效果不明顯,原因暫時未知(可能確實存在沒待釋放的空閒內存)。

手動觸發tcmalloc的releasefreememory接口

爲驗證該問題,通過設置cache容量的方式進行。

  1. 先設置cache的容量爲6GB,然後將讀請求壓起來,使cache的6GB容量填滿
  2. 修改cache的容量爲2GB,爲快速是內存釋放,手動調用tcmalloc的releasefreememory接口,發現沒有效果,推測採用tcmalloc之後,內存仍然一直上漲不下跌的原因可能與該接口的有關。
  3. 在releasefreememory接口內部的多個地方記錄日誌,然後啓動進程再次測試,發現一處報錯是在進行系統調用madvise時有出現失敗。

代碼位置:

報錯日誌信息:

  1. 通過該處的調用失敗,分析代碼。發現tcmalloc的內存釋放邏輯是“round-robin”,即中間有一個span釋放失敗,則後續待釋放的span被終止,releasefreememory邏輯調用結束。這個就和前面的現象吻合,執行完releasefreememory接口後基本沒有效果,發現每次都是在釋放了幾十MB時,因爲該接口的調用失敗導致釋放邏輯終止。
  2. 再次分析該系統調用madvise失敗原因。通過給內核的該方法打patch,發現其失敗原因是因爲傳入的地址塊對應的內存狀態是LOCKED狀態。導致系統調用失敗,報錯爲非法參數。
  3. 內存爲LOCKED狀態,和該狀態相關的有代碼調用mlock系統方法、系統的ulimit配置。分析相關代碼未發現異常點。查詢系統ulimit配置,發現max locked memory 爲unlimited。修改其配置爲16MB,重啓Cassandra進程,再次測試,發現內存釋放效果顯著。
  4. 繼續運行測試,發現內存持續上漲的情況消失。在業務持續存在的情況下,內存會上漲到最高,不再上漲,保持平穩,符合內存計劃使用量。業務壓力減少甚至停止後,內存出現緩慢下降趨勢。

解決&總結

  1. 引入tcmalloc工具,優化內存管理。比較優秀的內存管理器有Google的tcmalloc和Facebook的jemalloc等
  2. 修改系統的max locked memory參數配置。

合理分配進程需要使用內存的最大值,並預留一定容量,對於不符合預期增長的內存需要進一步分析。內存相關問題和程序相關性較強。系統的關鍵配置需謹慎,要評估其影響。同時排查了類似的所有配置。

增加releasefreememory的命令,後端進行調用,優化tcmalloc hold內存不釋放問題。不過releasefreememory命令的執行會鎖整個pageHeap,可能導致內存分配請求被hang,所以需要小心執行。

後端增加可動態配置tcmalloc_release_rate的參數,來調整tcmalloc將內存交還給操作系統的頻率。該值的合理範圍是[0-10],0表示永遠不交還,值越大,表示交還的頻率越高,默認值是1。

結語

本文通過分析開發過程中遇到的內存增長問題,使用更優秀的內存管理工具,以及更細粒度的內存監控,更直觀的監控數據庫運行期間的內存狀態,確保數據庫平穩高性能運行。

 本文分享自華爲雲社區《華爲雲數據庫GaussDB(for Cassandra)揭祕第二期: 內存異常增長的排查經歷》,原文作者:高斯Cassandra官方。

點擊關注,第一時間瞭解華爲雲新鮮技術~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章