當前位置: 妍妍網 > 碼農

TB級分布式KV儲存設計:總算把Redis效能用到了極致……

2024-03-22碼農

目錄

一、前言

二、自建 Redis 架構及核心元件

1、ConfigServer

2、Redis-Proxy

3、Redis-Server

三、自動化運維平台

1、運維平台架構

2、例項自動化部署

3、例項擴容

4、資源管理

5、診斷與分析

四、監控與告警

1、監控

2、告警

五、穩定性治理

1、資源隔離

2、巡檢

3、故障演練

六、總結

一、前言

自建 Redis 系統是得物 DBA 團隊自研高效能分布式 KV 緩存系統,目前管理的 ECS 記憶體總容量超過數十TB,數百多個 Redis 緩存集群例項,數萬多個 Redis 數據節點,其中記憶體規格超過 1T 的大容量集群多個。

自建 Redis 系統采用 Proxy 架構,包含 ConfigServer、Proxy 等核心元件,還包括一個具備例項自動化部署、資源管理、診斷與分析等重要功能在內的完善的自動化運維平台。

本文將從系統架構及核心元件、自建 Redis 支持的重要特性、自動化運維平台的重要功能等多方面為大家介紹自建 Redis 系統。

二、自建 Redis 架構及核心元件

自建 Redis 分布式 KV 緩存系統由 ConfigServer、Redis-Proxy、Redis-Server 等核心元件構成,整體架構圖如下所示:

下面為大家逐一介紹自建 Redis 各核心元件支持的重要功能和特性。

1、ConfigServer

ConfigServer 是自建 Redis 系統中關鍵元件之一,跨多可用區多節點部署,采用 Raft 協定實作 ConfigServer 元件高可用;ConfigServer 主要負責三方面職責:

負責 Proxy 添加與刪除、Group 建立與刪除、Redis-Server 例項添加與刪除、Redis-Server 例項手動主從切換、水平擴容與數據遷移等功能操作。

集群拓撲發生變化時,主動向 Redi-Proxy 更新集群拓撲。

負責 Redis-Server 例項故障檢測與自動故障轉移(主節點故障後自動主從切換)。

ConfigServer 系統結構圖如下所示:

每個自建 Redis 集群會對應部署一組獨立的 ConfigServer 元件,並且每組 ConfigServer 采用至少三節點部署,分布在三個不同的可用區,保證自建 Redis 系統高可用:

  • ConfigServer 多節點多可用區部署,保證了 ConfigServer 元件自身的高可用。

  • 同時,也保證 Redis-Server 的高可用,任意一個可用區故障,剩余 ConfigServer 依然能高效完成 Redis-Server 的宕機判定、選主與故障 Failover。

  • 相對於開源 Redis 原生 Cluster 模式,也不會因多數 Redis-Server 主節點故障而無法完成故障判定與 Failover。

  • 1)故障檢測與轉移

    ConfigServer 負責 Redis-Server 節點故障檢測與自動故障轉移,ConfigServer 會對每一個 Group 的 Master 節點進行定期探活,如果發現某一個 Group 的 Master 節點不可用,就會執行 Failover 流程,選擇該 Group 內一個可用的 Slave 節點提升為新的 Master 節點,保證該 Group 可繼續對外提供服務。

    實作思路參考開源 Redis Sentinel,不過由於 ConfigServer 是 Golang 實作,而開源 Redis Sentinel 是使用 C 語言實作,所以在部份細節上略有不同。

    多協程: 在 Redis Sentinel 中,是在一個單執行緒中定時檢測所有 Redis-Server 節點,而由於 Golang 支持協程,在 ConfigServer 中是為例項的每個 Group 開啟一個協程,在協程中定時檢測該 Group 對應的 Redis-Server 狀態。

    自訂通訊協定: 在 Redis Sentinel 中,Sentinel 之間透過 Redis-Server 節點交換後設資料資訊,來發現負責同一個 Redis-Server 的 Sentinel 節點,以及交換觀察的節點狀態;而在 ConfigServer 中,ConfigServer 之間是采用自訂 TCP 協定直接通訊,交換資訊更高效,也能縮短故障切換時間。

    ①故障檢測

  • 每個 ConfigServer 定期給該例項的所有 Redis-Server 節點發送 Ping 和 Info 命令,用於檢測節點的可用性和發現新的從節點。

  • 當 ConfigServer 向 Redis-Server 節點發送命令超時時,將節點標記為主觀下線,並傳播節點的主觀下線狀態。

  • ConfigServer Leader 節點發現某節點處於主觀下線時,會主動查詢其他 ConfigServer 對該節點的狀態判定,如果多數 ConfigServer 節點都將該 Redis-Server 節點標記為主觀下線,則 Leader 節點將該 Redis 節點標記為客觀下線,並行起故障轉移流程。

  • ②故障轉移

    從故障 Redis 主節點的所有從節點中選擇一個最優的從節點,選擇策略包含:

  • 過濾掉不健康的從節點,比如處於主觀下線或者客觀下線狀態。

  • 選擇 Slave-Priority 最高的從節點。

  • 選擇復制偏移量最大的從節點。

  • 選擇 Runid 最小的從節點。

  • 將選取出來的從節點提升為新的主節點,即向該節點執行 slaveof no one 命令。

  • 將其他從節點設定為新主節點的從節點。

  • 並保持對舊主節點的狀態關註,如果舊主節點恢復,將舊主節點也更新為新主節點的從節點。

  • 2、Redis-Proxy

    Redis-Proxy 元件是自建 Redis 系統中的代理服務,負責接受客戶端連線,然後轉發客戶端命令到後端相應的 Redis-Server 節點,使得後端 Redis-Server 集群部署架構對業務透明,Proxy 支持 Redis RESP 協定,業務存取 Proxy 就像存取一個單點 Redis 服務一樣,業務可以把一個自建 Redis 集群當作一個容量無限大的單點 Redis 例項即可。

    自建 Redis 為每個例項部署一組獨立的 Proxy 節點,Proxy 是一個無狀態服務,可以很方便的進行水平擴容,提高業務存取自建 Redis 系統的 QPS。

    在自建 Redis 中,每個集群可能包含多個 Group,每個 Group 對應一組主從 Redis-Server 節點,每個 Group 負責一部份 Key,同時,整個集群劃分為 1024 個槽(slot),每個 Group 負責其中一部份槽(slot),使用者寫入的 Key 透過以下演算法來計算對應的 slot,然後儲存到對應節點。

    slot 計算公式:slot(key) = crc32(key) % 1024

    Proxy 接收到使用者命令後,提取存取的 Key,透過上面同樣演算法計算相應的 slot,獲取負責該 slot 的對應 Redis-Server 例項的連線,再將使用者命令轉發到對應的 Redis-Server 例項上執行,讀取 Redis-Server 返回的結果發送給使用者。

    DBA 團隊針對 Proxy 元件做了大量效能最佳化,相比市面上開源的支持 Redis RESP 協定的 Proxy 版本,臨時物件記憶體分配減少了約 20 倍,極大的減輕 GC 壓力以及 GC 消耗的 CPU;大量短連結場景下,QPS 提升約10%。

    同時,自建 Redis-Proxy 還支持同城雙活、異步雙寫等特色功能。

    1)同城雙活

    自建 Redis 為了保證數據的高可用和高可靠,每個 Redis 例項中每個分組采用至少一主一從的部署方案,且主從節點分別部署在不同可用區,在預設的存取模式下,業務讀寫都存取主節點,從節點僅僅作為熱備節點。

    為了提升業務在多可用區部署場景下存取緩存效能與降低延遲,以及最大化利用從節點的價值,自建 Redis-Proxy 支持同城雙活功能。自建 Redis 同城雙活采用 單寫就近讀 的方案實作,實作原理圖如下所示:

    註:需要透過以下方式之一來實作動態配置

    透過容器的 ServiceName 實作同 AZ Proxy 節點優先存取 (優先)

    透過雲廠商的 PrivateZone 實作智慧 DNS 解析(容器和非容器套用都行)。

    Redis-Server

    采用至少一主一從的部署方案,並且主從節點跨可用區部署,分別部署在與業務對應的可用區。

    Redis-Proxy

    Redis-Proxy 同樣采用多可用區部署,與業務可用區相同。

    各可用區 Proxy 將寫請求自動路由到主節點,依然寫主節點。

    讀請求優先就近存取本可用區從節點,本可用區無可用從節點時,支持自動存取主節點或優先存取其他可用區從節點。

    2)異步雙寫

    針對業務從雲 Redis 遷移到自建 Redis、以及大集群拆分場景,對於數據可靠性要求高的業務,Proxy 支持雙寫功能。

    以從雲 Redis 遷移到自建 Redis 為例,遷移期間 Proxy 同時寫雲 Redis 和自建 Redis,保證兩邊數據即時一致,從而可以隨時回滾,做到平滑遷移。

    Proxy 雙寫功能具備以下特性:

    Proxy 雙寫功能采用異步雙寫操作實作,效能優異,雙寫操作對業務幾乎無影響。

    Proxy 支持轉發、唯讀、雙寫等多種模式,業務接入自建 Redis 後,自建 Redis 透過線上動態更改配置,平滑的完成整個切換過程,無需業務頻繁更改配置或者重新開機等。

    Proxy 雙寫支持雲 Redis 優先或者自建 Redis 優先(以雲 Redis 遷移為例),且可線上動態調整。

    同時,提供數據比對工具,用於雙寫期間數據對比,隨時觀察兩邊數據一致性情況;數據對比支持多種策略,包括對比型別、對比長度或元質數量。

    3、Redis-Server

    Redis-Server 元件為開源 Redis 版本基礎上,增加槽 slot 同步遷移與異步遷移等相關功能;支持原生開源 Redis 的所有特性,比如支持 String、Hash、List、Set、ZSet 等常用數據結構,AOF 持久化、主從復制、Lua指令碼等等。

    Share-Nothing 架構: 自建 Redis 系統中,Redis-Server 服務采用集群化部署,整個集群由多個 Group 共同組成,每個 Group 中包含一主 N 從多個 Redis-Server 例項,Group 之間的 Redis-Server 節點相互沒有通訊,為 Share-Nothing 架構。同時,整個集群劃分為 1024 個槽(slot),每個 Group 負責其中一部份槽(slot),使用者寫入的 Key 透過上面提到的演算法來計算對應的 slot,然後儲存到負責該 slot 的 Group 中的 Redis-Server 節點上。查詢 Key 時,透過同樣的演算法去對應的節點上查詢。

    1)Async-Fork 特性

    在 Redis 中,在 AOF 檔重寫、生成 RDB 備份檔以及主從全量同步過程中,都需要使用系統呼叫 Fork 建立一個子行程來獲取記憶體數據快照,在 Fork() 函式建立子行程的時候,內核會把父行程的「頁表」復制一份給子行程,如果頁表很大,在現有常見作業系統中,復制頁表的過程耗時會非常長,那麽在此期間,業務存取 Redis 讀寫延遲會大幅增加。

    自建 Redis 系統中,Redis-Server 透過最佳化並適配最新的支持 Async-Fork 特性的作業系統,極大的提升 Fork 操作效能:

    Fork 命令耗時大幅減小,並且不隨數據量增長而增長, 基本穩定在 200 微秒左右;

    TP100 抖動得到明顯改善,TP100 值也不隨數據量增長而變大,基本在 1-2 毫秒左右;

    相比原生 Redis Fork 耗時減少 98%。 日常運維中,添加從節點、主從切換、RDB 離線分析等常見運維操作均對業務無感知,不會造成業務效能抖動。

    註:該圖表使用了雙縱座標

    詳細內容可閱讀

    2)數據遷移

    為什麽需要做數據遷移?主要是為了水平擴容,自建 Redis 將每個集群例項劃分為固定數量的槽 slot,每個 Group 負責一部份槽,當進行水平擴容時,需要重新分配 slot 的分布,並且將一部份 slot 從原有節點遷移到新節點,同時將由 slot 負責的數據一起遷移到新節點。

    數據遷移過程包括在源節點將 Key 對應的 Value 使用 Dump 命令進行序列化,然後將數據發送到目標節點,目標節點使用 Restore 命令將 Key 和 Value 儲存到本地 Redis 中,最後源節點將該 Key 刪除。

    自建 Redis 支持同步遷移與異步遷移兩種方式。

    同步遷移: 即在上述整個遷移過程中,源端的 Migrate 命令處於阻塞狀態,直到目標端成功載入數據,並且返回成功,源節點刪除該 Key 後,Migrate 命令才響應客戶端成功。由於 Redis 數據操作是一個單執行緒模型,所有命令的處理的都在主執行緒中處理,因此 Migrate 命令會極大地影響其他正常業務的存取。

    異步遷移: Migrate 命令僅阻塞 Dump 序列化數據、並且異步發送到目標節點,然後即返回客戶端成功;目標節點接收到數據後,使用 Restore 命令進行保存,保存成功後,主動給源節點發送一個 ACK 命令,通知源節點將遷移的 Key 刪除。異步遷移減少源端 Migrate 命令的阻塞時間,減少了 slot 遷移過程中對業務的影響。

    三、自動化運維平台

    自建 Redis 系統擁有功能完善的自動化運維平台。其主要功能包括:緩存例項自動化部署、快速水平擴容與垂直擴容、多維度資源管理、診斷與分析等。

    1、運維平台架構

    自建 Redis 自動化運維平台包括Redis 管控平台、Kv-Admin、Kv-Agent、Prometheus 等元件,部署架構如下圖所示:

    1)Redis 管控平台

    Redis 管控平台是自建 Redis 綜合運維管理平台,自建 Redis 的視覺化操作均在 Redis 管控平台上完成,包括例項部署、擴容、數據遷移等在內的所有日常運維操作均可在管理平台上完成。

    2)Kv-Admin

    Kv-Admin 是自動化運維平台的核心元件,負責處理所有前端發送過來的請求,核心功能包括:

  • 負責完成例項部署時任務排程、機器推薦、埠分配、SLB 推薦與繫結。

  • 例項列表展示、例項基本資訊查詢。

  • 數據離線分析與保存。

  • 資源池管理及生成資源報表。

  • 3)Kv-Agent

    每個 ECS 上會部署一個 Kv-Agent 元件,Kv-Agent 負責完成例項部署、例項啟停等操作;基於心跳後設資料的 Exporter 自動註冊與解除;

    同時,Kv-Agent 包含 Exporter 模組,負責 Redis-Server 節點的監控資訊采集,然後將數據規範化後透過端點暴露給 Prometheus。

    4)APM / Prometheus

    APM 監控平台或者 Prometheus 負責從 Exporter 暴露的端點拉取監控數據;自建 Redis 監控與告警均接入公司 APM 平台。

    2、例項自動化部署

    一個緩存例項的部署非常復雜,涉及機器選擇、配置檔準備、節點安裝與啟動、槽位分配、主從關系設定、SLB 分配與繫結、以及相關元件的部署等一系列動作。

    自動化運維平台支持安裝包版本管理,在部署頁面選擇合適的包版本、配置對應例項可用區、規格等基本資訊後,一鍵操作即可完成 ConfigServer、Redis-Proxy、Redis-Server 等元件的部署,自動完成元件部署過程中涉及的上述機器推薦、配置檔準備、節點安裝與啟動等所有過程。

    為了保證例項中所有元件的高可用,自動化部署過程中包含一些 必要的部署規則:

  • ConfigServer 元件會部署在三個不同的可用區。

  • 避免同一個集群例項的多個 Redis-Server、Redis-Proxy 節點部署在相同的 ECS 上,每個 ECS 上可部署的同一個集群例項的 Server 或 Proxy 元件數量可配置。

  • 每個 ECS 最多分配總記憶體容量的 90%,預留一定的數據增長空間。

  • 根據 ECS 可用記憶體,優先推薦剩余可用記憶體多的 ECS。

  • 根據 Group 數量,自動均衡分配每個 Group 負責的 slot 數量。

  • 根據 SLB 近三天的流量峰值,自動繫結最優的 SLB。

  • 3、例項擴容

    當業務數據增長導致例項記憶體使用率超過一定閾值後,根據單節點分配的最大記憶體、例項的 Group 數量等情況綜合考慮,運維可選擇為例項進行垂直擴容或者水平擴容。

    垂直擴容, 即動態修改單節點的 Maxmemory 參數,提高單節點的容量。

    水平擴容, 即增加例項的 Group 數量,對應增加主從節點數量,然後重新進行 slot 分配和對應的數據遷移動作。

    一般來說,當單節點規格小於 4G 時,會優先考慮垂直擴容,簡單快速,對業務無任何影響。

    自動化運維平台支持方便的垂直擴容和水平擴容操作。

    對於 垂直擴容, 運維平台支持批次調整例項 Redis-Server 節點的容量。

    對於 水平擴容, 同例項初始部署一樣,運維平台支持新增 Redis-Server 節點的自動化部署、主從關系設定等,同時支持按照例項總節點數重新自動均衡分配每個 Group 負責的 slot 數量,並自動完成數據遷移,數據遷移進度視覺化。

    4、資源管理

    自建 Redis 運維平台目前管理的 ECS 超過數千台,運維平台支持 ECS 資源 批次添加、資源分配、資源釘選、資源推薦 等資源管理功能,以及提供 資源利用率報表。

    運維平台支持 ECS 可用記憶體的管理與分配,運維平台記錄每台 ECS 的剩余可分配記憶體容量,Redis-Server 例項部署時,優先推薦剩余可分配記憶體多的 ECS,分配出去的 ECS 更新對應的可用記憶體容量。

    Redis-Proxy 和 ConfigServer 部署時,優先推薦部署的例項數量較少的 ECS。

    5、診斷與分析

    計算例項綜合得分: 運維平台透過分析所有例項關鍵監控指標前一天的峰值,再根據各項指標的權重,每天自動計算所有例項的得分,根據各例項的得分即可快速了解各例項的使用健康度。

    參與計算得分的關鍵指標包括: Proxy CPU、Redis CPU、Redis 記憶體使用率、Proxy 占用記憶體、Proxy GC 次數、最大 RT、Redis 流入流量、Redis 流出流量等。

    慢日誌: 運維平台支持 Redis-Server 記錄的慢日誌和 Redis-proxy 記錄的慢日誌查詢。

    RDB 離線分析: 運維平台支持生成 RDB 檔,並自動進行數據離線分析。分析結果包含 Top100 Value 最大的 Key 和元素個數最多的 Key;每種型別 Key,不同 Key 字首包含的 Key 數量等。

    四、監控與告警

    自建 Redis 系統接入 APM 監控平台,提供了各種維度的監控指標和告警功能,及時對異常情況進行預警,當出現問題時,也能幫助快速定位問題。

    1、監控

    ECS: CPU、系統 Load、記憶體、網路流量、網路丟包、磁盤 IO 等。

    Proxy: QPS、TP999/TP9999/TP100、連線數、CPU、 記憶體、GC、Goroutine 數量等。

    Server: CPU、記憶體、網路、連線數、QPS、Key 數量、命中率、存取 RT等。

    2、告警

    自建 Redis 包含大量的告警指標:

    ECS CPU 使用率、記憶體使用率、系統 Load(5 分鐘平均值)、流量。

    SLB 流量。

    Server、Proxy、ConfigServer 節點宕機。

    主節點缺失從節點、主節點可用區不一致、主從角色切換。

    五、穩定性治理

    1、資源隔離

    自建 Redis 目前所有元件都是部署在 ECS 上,為了提高資源利用率節約成本,大部份業務域的 Redis 集群都是混布的。自建 Redis 運維平台目前管理 ECS 超過數千台,接入的業務也涵蓋行銷、風控、演算法、投放、社群、大數據等等,每個業務的例項等級、QPS、流量等指標各有差異,少數業務 Redis 可能存在突發寫入流量高、Proxy CPU毛刺等現象,可能會引起相同 ECS 上其他例項效能抖動。

    按標簽分類: 為了方便資源隔離與資源分配時管理,所有 ECS 資源 按標簽進行分類管理, 針對特殊需求業務、大流量例項、通用資源池等劃分不同的資源標簽,例項部署時選擇合適的標簽、或者頻繁出現告警時調整到對應資源池進行隔離,避免相互影響。

    Proxy CPU 限制: 為了防止單個 Redis-Proxy 行程搶占過多 CPU 資源,Redis-Proxy 支持配置 Golang GOMAXPROCS 參數來設定單個行程允許使用的最大 CPU 核數。

    2、巡檢

    為了提前發現潛在的風險,除了日常告警外,我們支持自動巡檢工具和巡檢資訊頁面,建立了定期巡檢機制。

    例項綜合得分排名: 運維平台透過分析所有例項關鍵監控指標前一天的峰值,再根據各項指標的權重,每天自動計算所有例項的得分,在巡檢資訊頁面進行展示,透過得分排序即可快速發現風險例項。

    ECS 資源大盤: 即時展示所有 Redis-Proxy 和 Redis-Server 使用 ECS 的重要指標,透過排序即可快速瀏覽各 ECS 各項重要指標,如 CPU 使用率、記憶體使用率、IOPS 使用率、磁盤讀取/寫入、上傳/下載頻寬等。

    自動巡檢工具: 定期檢查所有例項的配置、節點數量等基本資訊,對於如從節點缺失、可用區不一致、節點配置不一致等異常情況進行提示。

    3、故障演練

    為了提高自建 Redis 系統在異常場景下的可用性、檢驗自建 Redis 系統在各異常場景的恢復時間,我們非週期性進行多次故障演練。

    故障演練涵蓋所有元件在ECS、網路、磁盤等出現故障的場景,以及多個元件組合約時出現這些故障的場景。

    經過故障演練檢驗,自建 Redis 系統 Redis-Server 主節點故障恢復時間 12s,Redis-Proxy 故障業務恢復時間 5s 內。

    六、總結

    本文詳細介紹了自建 Redis 架構和現有一些重要特性,我們還在持續不斷地叠代最佳化和豐富支持的特性,為我們的業務提供一個功能更強大、效能更優異的分布式緩存系統。未來我們還會叠代的方向可能包括且不限於:

    目前 Redis-Server 為了相容雲上版本和分布式架構做了很多客製化,所以未使用最新社群版本,未來會升級到最新的社群版本如 7.0。

    熱 Key 存取一直是 Redis 使用中對業務影響較大的一個問題,未來我們考慮支持熱 Key 統計與讀熱 Key 本地緩存。

    Golang 版本 Proxy 在高 QPS 請求時,GC 是效能提升的一個瓶頸,未來考慮使用 Rust 重構 Proxy。



    作者丨 Miro

    來源丨公眾號:得物技術(ID:gh_13ba5621e65c)

    dbaplus社群歡迎廣大技術人員投稿,投稿信箱: [email protected]