線上問題排查實例分析|關于 Redis 內存泄漏

Redis 作為高性能的 key-value 內存型數據庫,普遍使用在對性能要求較高的系統中,同時也是滴滴內部的內存使用大戶。本文從 KV 團隊對線上 Redis 內存泄漏定位的時間線維度,簡要介紹 Linux 上內存泄漏的問題定位思路和工具。

16:30 問題暴露

業務反饋縮容后內存使用率90%告警,和預期不符合,key 只有1萬個,使用大 key 診斷,沒有超過512字節以上的大 key。

16:40 確認內存泄漏

發現該系統中有部分實例內存明顯偏高達到300~800MB,正常實例只有10MB左右,版本號為4ce35dea,在9月份時已經有發現49bdcd0b這個較老版本有內存泄漏情況發生,現象看起來一樣,說明內存泄漏問題一直存在,未被修復,于是開始排查該問題。

17:30 開始排查社區版本

排查問題先易后難,先排除是不是社區的版本Bug問題:

  • 不需要從最新修復一直倒敘確認到3系列的 commit 提交,因為如果是嚴重的內存泄漏,3系列的舊版本也一定會有 backport 修復記錄。

查看3.2.8的commit記錄,只有一次內存泄漏相關提交:Memory leak in clusterRedirectBlockedClientIfNeeded.

本次提交只修復了在 cluster 出現 key 重定向錯誤時對 block client 處理時對一個指針的泄漏,不可能出現如此大的泄漏量。3.2.8的社區版已上線數年,但在社區內未搜索到相關內存泄漏問題,因此推測是我們的某些定制功能開發引入的 Bug。

18:10 整理監控和日志

整理當前已知監控和日志信息,分析問題的表面原因和發生時間

1、監控信息

odin 監控只能看到最近兩個月的內存使用曲線,從監控上可以得到三點信息:

  • 兩個月前已經發生內存泄漏

  • 內存泄漏不是持續發生的,是由于某次事件觸發的

  • 內存泄漏量大,主實例使用內存800MB,從實例使用內存10MB

21c89a9b5aa7a9dda8229161a4fcaea6.png

2、日志信息

排查發生內存泄漏的容器日志:? ? ? ?

36a6a8d5612614e074cccaef63fa96f0.png

07161cf1671c59ba5012d8eb4adafdc7.png

Redis 在10月11日被創建后,只有在20日出現有大量日志,之后無日志,日志有以下內容:

  • Redis 橫向擴容 slot 遷移

  • 主從切換

  • AOF 重寫

  • 搜索該系統的歷史短信告警,在10月11日11:33分出現三次內存使用率達到100%的告警,因此可以推測出現 key 淘汰

Manager平臺操作信息:

71e708e93d03f80747d14a6a1d8d80fc.png

  • 垂直擴容

  • 橫向擴容

  • Redis 重啟

綜合 Redis 的日志和平臺日志信息,雖然未能直接發現問題原因,可以確定內存泄漏發生在10月20日11:30左右,由以下單個事件或者混合觸發的:

  • 主從切換

  • key 遷移

  • key 驅除

18:00 打印內存 dump 信息

在實例上使用 GDB ?把泄漏實例的所有內存 dump 出來,初步發現內存上有很多 key(647w個),不屬于本節點,info 里數據庫只有1.6W個 key, ?懷疑是slot 遷移有問題。

803061b9a2d6c26ab1d6f227d73e86e1.png

18:30 第一次 diff 代碼

由于3.2.8自研版本有兩個重大修改:

  1. slot 的所屬 key 集合記錄,把跳躍表改為了4.0以后的基數樹結構,從社區的 unstable 分支 backport 下來的;

  2. 支持多活

由于出問題的系統沒有使用多活功能,且恰巧事發時有 slot 遷移,因此重點懷疑 slot 遷移中 rax 樹相關操作有內存泄漏,首先查看了相關代碼,有幾個疑似的地方,但都排除掉了。

20:30 嘗試使用工具定位

  1. memory doctor

    Redis4 引入的內存診斷命令,3系列未實現

  2. 3.2.8版本使用 jemalloc-4.0.3作為內存分配器,嘗試使用 jeprof 工具分析內存使用情況,發現 jemalloc 編譯時需要提前添加--enable-prof編譯選項,此路不通

  3. 使用 perf 抓取 brk 系統調用,未發現異常(實際上最近兩個月也未發生泄漏)

  4. valgrind 作為最后手段,不確定是否可以復現

22:00 組內溝通進展

和組內同學溝通下午的調查情況,仍然懷疑 rax 泄漏,其次多活或者 failover 混合動作觸發的 case 導致泄漏。

第二天10:00 重新整理思路

使用 hexdump 觀察昨天的內存 dump 文件,發現泄漏內存為 SDS 字符串數據類型,且連續分布。

1552109436f54ecc7c44bfc325e97df7.png

每隔4、5行都會出現OO TT SS等字符,對應 SDS 類型的 sdshdr 結構體。? ? ??

5868a6662eb195e238affee34eca7fab.png

每個泄漏的 key 字符串大約在80字節左右,因此使用時 sdshdr8(為了節約內存,sds 的 header 有五種 sdshdr5,sdshdr8、sdshdr16、sdshdr32、sdshdr64,其中8指的是長度小于1<<8的字符串使用的 sdshdr)。? ??

a0b9ea6cd80567ed1d54daee801c945a.png

以TT那行為例,結合 SDS 字符串的 new 函數分析,key 字符串長度為84字節等于0x54,結合代碼看,sh->len和sh->alloc都是0x54,第三個字節標識 type 類型,sdshdr8 的 type 值剛好是0x1,因此可以確認泄漏的是 sds 類型的 key 值,并且排除 rax 樹泄漏的可能,因為內存 dump 和 rax 樹的存儲結構不符。附典型的 rax 存儲結構:???

a5157eef46d9227f7bc3d60c11bbf402.png

14:00 根據dump的分析重新排查代碼

排除了 rax 樹的泄漏,同時綜合 redis 使用 sds key 的情況,此時把懷疑重點放在了 write 等 dict 的釋放方法上,以及 rdb 的加載時 key 的臨時結構體變量。

此時 diff 代碼,不再局限有變更的代碼,以功能為粒度進行走讀代碼,但把重點放在了 failover 時的 flushdb 和 loadRDB 操作上。

17:00 排查slot遷移代碼

在上一輪代碼走讀中,再次排除了 failover,key 淘汰的代碼有內存泄漏的可能,因此重新懷疑 slot 遷移中的某些動作導致 key 字面值的內存泄漏,尤其是 slot 清空等操作。

18:30 找到根因

在 slot 遷移過程中,會遍歷舊節點中的所有 key,然后把遍歷得到的 key 從舊節點遷移到新節點中。

1e4911620568f73809d11ae28795ff1b.png

這個功能在3.2.8代碼中沒有被改動,但其調用的 getKeysInSlot 函數有了修改。getKeysInSlot 是遍歷 rax 樹,拿到待遷移 key 列表,對每個 key 從 rax 樹中取出完整字符串,來拷貝創建 obj 類型指向 sds 字符串;這些字符串作為數組指針類型返回給了出參 keys,但在上層調用把這些字符串返回給客戶端后,沒有釋放這些字符串,導致了內存泄漏的發生。

原生的3.2.8代碼中 getKeysInSlot 函數,由于使用的是跳躍表,該跳躍表中的每個節點都是一個 key 的 obj 類型,因此只需要返回這個 key 的指針即可,無需內存拷貝動作,因此上層調用中也就不需要內存釋放動作。這個根因查明,也反過來解釋了很多疑問:

  • 為什么剛開始只有老版本才有內存泄漏,新版本未發現。原因是老版本的實例上線時間長,有水平擴容的需求較多,內存泄漏的實例也就較多。

  • 泄漏的內存為什么連續分布?原因是在一次 slot 遷移動作中,這些 key 遍歷動作都是連續進行的。

  • ?這個系統為什么泄漏比例這么高?原因是該系統中 key 占用的內存比 value值更高,key 通常80字節,而 value 大多是0、1等數值。

20:00 修復動作

相比較根因的查找,修復就簡單多了,只需添加一行代碼即可。? ?

cd96b31e0005648bc3d23e7bcecd4a1f.png

后續思考

1、代碼 review 需要從功能視角去走讀代碼,不能只關注 diff 不同。在本次調查中,第一遍走讀代碼只關注 diff 點,是無法發現問題的。

2、對內存泄漏的排查,在代碼設計階段是避免此類問題的效率最優解,代碼 review 階段比測試階段代價要小,測試階段發現要比上線后排查容易得多,越是工程后期修復 bug 越難。具體在該函數設計中,由于內存申請和釋放沒有內聚性,導致內存泄漏很容易出現,而這個函數在3系列使用跳躍表時是沒有問題的,因為不涉及到內存的申請釋放。開發和 QA 在測試中引入工具進行功能覆蓋測試,動態工具如 valgrind、sanitizers 等,線上工具如memleak、perf等。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/163576.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/163576.shtml
英文地址,請注明出處:http://en.pswp.cn/news/163576.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電動機保護方式

3.3.1、電動機溫度保護 溫度保護是利用安裝在電動機內部的溫度繼電器或變換器來實現的。當電動機達到一定溫度時繼電器動作&#xff0c;通過控制電路斷開電動機的主電路。對于單相小容量電動機&#xff0c;可以用繼電器直接斷開動力電路。 根據溫度傳感器的不同可以分為&…

cv2.threshold()函數參數講解

cv2.threshold()函數用于對圖像進行閾值化處理。它的參數如下&#xff1a; src&#xff1a;要處理的輸入圖像&#xff0c;可以是灰度圖像或彩色圖像&#xff0c;類型為uint8。thresh&#xff1a;設定的閾值&#xff0c;如果像素值大于閾值&#xff0c;則將其設為maxval&#x…

【Proteus仿真】【51單片機】籃球比賽計分器

文章目錄 一、功能簡介二、軟件設計三、實驗現象聯系作者 一、功能簡介 本項目使用Proteus8仿真51單片機控制器&#xff0c;使用聲光報警模塊、動態數碼管模塊、按鍵模塊等。 主要功能&#xff1a; 系統運行后&#xff0c;數碼管顯示比賽時間和AB隊得分&#xff1b;系統還未開…

數據中心運維管理:從人工到智能需要走幾步?

一切的變化來自于數據中心規模、復雜度、設備多樣性的挑戰&#xff0c;將運維平臺的重要性推向歷史高點。 此外&#xff0c;基于業務連續性方面的考慮&#xff0c;分布式數據中心成為越來越多客戶的選擇。 一、數據中心面臨的挑戰 運維管理分散&#xff0c;缺乏統一的管理 I…

Win11和NewBing瀏覽器100%開啟Copilot的方法

嚴格按以下步驟來&#xff0c;100%開啟免費的AI&#xff1a; 1.系統升級到Win11最新版&#xff08;不要用家庭版&#xff0c;推薦專業版&#xff09; 升級完成之后的系統信息&#xff08;時間截至2023.11.22&#xff09; 版本號&#xff1a;23H2 操作系統版本&#xff1a;226…

MySQL數據庫_01

Web后端開發_02 數據庫介紹 什么是數據庫&#xff1f; 數據庫&#xff1a;DataBase&#xff08;DB&#xff09;&#xff0c;是存儲和管理數據的倉庫 數據庫管理系統&#xff1a;DataBase Management System (DBMS)&#xff0c;操縱和管理數據庫的大型軟件。SQL&#xff1a;St…

自定義注解+AOP

自定義注解與AOP&#xff08;面向切面編程&#xff09;的結合常常用于在應用程序中劃定切面&#xff0c;以便在特定的方法或類上應用橫切關注點。以下是一個簡單的示例&#xff0c;演示了如何創建自定義注解&#xff0c;并使用Spring AOP來在被注解的方法上應用通知。 如何創建…

java學習part08權限

1.權限表格 外部類都是公有和缺省&#xff0c;因為其他兩種對于外部類沒有意義 一些內部成分都各種權限都可以 2.如何體現java封裝性 答&#xff0c;通過權限控制&#xff0c;保證哪些可以給人看到&#xff0c;哪些不能

手持式無線通信頻譜分析儀 MS2713E

MS2713E 手持式無線通信頻譜分析儀 安立手持式無線通信頻譜分析儀 MS2713E 旨在處理最惡劣的現場條件&#xff0c;使您能夠監控、定位、識別和分析各種蜂窩、2G/3G/4G、陸地移動無線電、Wi-Fi 和廣播信號。多功能 Spectrum Master 在定位和識別寬頻率范圍內的信號時&#xff0…

rust內存優化

背景 在 Rust 中,repr 是一個屬性(attribute),用于指定數據類型在內存中的布局和表現形式 repr 屬性可以用于枚舉、結構體和聯合體的定義,以控制它們的內部表示方式 repr 屬性有多個選項,每個選項對應于一種不同的布局方式 常見的選項包括: C 將類型按照 C 語言的規則…

3D人臉掃描設備助力企業家數字人復刻,打破商業邊界

京都薇薇推出數字人VN&#xff0c;以京都薇薇董事長為原型制作&#xff0c;賦能品牌直播、短片宣傳、線上面診等活動&#xff0c;進一步增強消費者對品牌的交互體驗&#xff0c;把元宇宙與品牌相融合&#xff0c;推動品牌線上服務與線下服務實現數字一體化&#xff0c;打造一個…

「X」Embedding in NLP|一文讀懂 2023 年最流行的 20 個 NLP 模型

在上一篇文章中&#xff0c;我們已經科普了什么是自然語言處理&#xff08;NLP&#xff09;、常見用例及其與向量數據庫的結合。今天&#xff0c;依然是「X」Embedding in NLP 系列專題&#xff0c;本文為初階第二篇&#xff0c;我們將深入介紹在 2023 年爆火的大語言模型 NLP …

小白也看的懂的爬取視頻操作

1.獲取一段視頻 可以直接從抖音下&#xff0c;也可以從b站上爬取&#xff08;注意法律謝謝&#xff09; 保護原創 b站的視頻 直接復制網址鏈接到嗶哩嗶哩(bilibili)視頻解析下載 - 保存B站視頻到手機、電腦 去就好了&#xff0c;

Docker的入門

Docker的入門 防火墻Docker的命令鏡像相關的命令運行容器容器相關的命令 Docker作為一個軟件集裝箱化平臺&#xff0c;可以讓開發者構建應用程序時&#xff0c;將它與其依賴環境一起打包到一個容器中&#xff0c;然后很容易地發布和應用到任意平臺中。 docker有3大核心&#xf…

如何弱化市場大環境帶來的影響?私域電商和裂變營銷引來新趨勢!

弱化市場大環境帶來的影響需要從多個方面入手&#xff0c;包括深入了解市場和行業、建立品牌優勢、多元化經營、優化供應鏈管理、加強客戶關系管理、靈活應對市場變化等。同時需要注意不同領域和行業的市場變化和政策調整&#xff0c;及時調整經營策略和業務結構&#xff0c;保…

WPF實戰項目十七(客戶端):數據等待加載彈框動畫

1、在Common文件夾下新建文件夾Events,新建擴展類UpdateLoadingEvent public class UpdateModel {public bool IsOpen { get; set; }}internal class UpdateLoadingEvent : PubSubEvent<UpdateModel>{} 2、新建一個靜態擴展類DialogExtensions來編寫注冊和推送等待消息…

獲取ip屬地(ip2region本地離線包-超簡單)

背景 最近有涉及要顯示ip屬地&#xff0c;但我想白嫖&#xff0c;結果就是白嫖的api接口太慢了&#xff0c;要延遲3到4秒左右&#xff0c;很影響體驗&#xff0c;而且不一定穩定。 結果突然看到了這個【ip2region】開源項目&#xff0c;離線識別ip屬地&#xff0c;精度自己測…

Public Key Retrieval is not allowed

出現這個錯誤的原因可能是 MySQL 連接配置中的某些設置限制了公鑰的檢索。要解決這個問題&#xff0c;可以嘗試以下方法&#xff1a; 設置參數&#xff1a;在 MySQL 連接 URL 或連接配置中添加參數 ?allowPublicKeyRetrievaltrue?。 更新 MySQL 驅動程序&#xff1a;如果您使…

編譯安裝報錯:configure: error: cannot guess build type; you must specify one

1、編譯安裝報錯 configure: error: cannot guess build type; you must specify one 該報錯信息翻過過來的意思是&#xff1a;無法猜測編譯 操作系統類型,請指定一個 2、解決方法 在原本的編譯安裝語句后面加上一句&#xff1a; “--buildarm-linux ” &#xff0c;這句話…

詳解開源數據庫審計平臺Yearning

基本概念 數據庫審計&#xff08;簡稱DBAudit&#xff09;能夠實時記錄網絡上的數據庫活動&#xff0c;對數據庫操作進行細粒度審計的合規性管理&#xff0c;對數據庫遭受到的風險行為進行告警&#xff0c;對攻擊行為進行阻斷。它通過對用戶訪問數據庫行為的記錄、分析和匯報&…