天天看點

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

RedKV是小紅書自研的一款基于NVMeSSD的分布式NoSQL KV存儲系統,支援無中心和有中心的兩種管控架構,旨在解決公司内實時落盤的KV存儲需求。RedKV1.0基于Gossip協定做節點管理,在全公司已經大規模使用,實時QPS接近1億/秒,存儲量在數PB級别。RedKV2.0采用中心Shard管理架構,支援全球多雲多副本線上彈性伸縮,異地容災和服務秒級切換。

通過分層優化,RedKV對比開源同類産品,聚合寫吞吐能力平均提升3倍,讀1.5倍;對标HBase,成本優化近40%。RedKV部分相容Redis協定,string/hash/zset等主要資料類型很好的的支援了公司的絕大部分線上存儲業務,優化了早期Redis叢集部署産生的成本問題以及HBase帶來的性能及穩定性問題。RedKV和Hive數倉的資料互通能力為離線資料業務提供了一種解決方案。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

小紅書是年輕人的生活記錄、分享平台,使用者可以通過短視訊、圖文等形式記錄生活點滴,分享生活方式。在目前的業務模型下,使用者的畫像資料和筆記資料用來做風險控制和内容推薦。存儲資料具有對象-屬性的特征、次元多,畫像資料量已經達到數十TB, 線上業務對畫像和筆記資料的通路P99 時延要求非常高。

2020年之前公司選擇的NoSQL存儲産品主要有:Redis、HBase,随着公司DAU的高速增長,早期的存儲方案遇到如下挑戰:

  • Redis叢集主要适用于緩存場景,開啟AOF資料實時落盤對性能有比較大的影響,同時每個節點需要額外挂載雲盤用于存儲AOF。在叢集節點和存儲容量受限的情況下,單節點的資料量設定過大會導緻故障後節點資料的failover時間太長,單節點資料量設定小會導緻gossip協定在穩定性高要求下節點個數受限,同時考慮突發流量的壓力,Redis叢集在部署上需要做一些空間預留,帶來成本高的問題。
  • HBase作為一款生态完善的NoSQL存儲系統,在高QPS下也産生了諸多的性能和穩定性問題,如:Zookeeper壓力大時穩定性難以保障(節點探活,服務注冊等都依賴 Zookeeper);HBase的資料檔案和WAL日志檔案直接寫HDFS,節點故障後,重放HDFS上的WAL速度慢;Java GC會導緻Zookeeper誤殺RegionServer,同時産生毛刺;Major Compaction 會導緻I/O飙升,産生長尾效應;受限HDFS的複雜性,黑盒運維對工程師來說比較困難;在小紅書的業務實戰中,百萬QPS下HBase延時不太理想,核心資料用大記憶體機型兜住,也引發成本高的問題。

随着業務的持續增長,開源存儲産品已經不能很好的滿足公司的業務發展需求, 公司需要一款穩定的高性能KV系統支撐内部業務,一方面要滿足業務對功能和性能的需求,另一方面要優化成本。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

2.1. 高QPS和低延時讀取特性

特征資料存儲場景:

  • 寫入帶寬達到數十GB/s,要求實時寫入性能和讀取性能都很高。

圖檔緩存的場景:

  • 資料量很大,要求讀取時延低。可以接受故障場景下少量資料丢失。

高性能(P99 < 10ms):

  • 模型資料存儲服務。記錄過去一段時間使用者訓練模型資料,對P99時延要求非常高,資料量在幾十TB。
  • 去重存儲服務。資料量在幾十TB,P99<10ms, P999<20ms。
  • 風控資料存儲服務。QPS目前達到千萬級别,P999 < 30ms。

2.2. 低成本的緩存特性

對标Redis:

  • 相容Redis協定,性能比Redis慢一些,但資源成本降低50%+。

典型場景:

  • 廣告的關鍵詞存儲和反作弊業務,解決大資料量、低QPS的存儲模型。

2.3. NoSQL存儲特性

對标HBase:

  • 支援資料多版本,列存行取等特性,比HBase成本減少30%+,P99時延提升6倍。
  • 支援KKV級别的TTL。
  • 強一緻:目前RedKV1.0采用的主從雙副本,資料寫入成功,可以通過配置同步模式來保障2副本寫成功,讀主寫主保障強一緻。對于寫性能要求高的場景,可以打開異步寫,寫主成功則傳回,依賴增量同步從節點資料。

典型場景:

  • 風控服務。實時查詢對P999要求極高,千萬QPS下HBase已經不能滿足性能需求,時延抖動比較大。
  • 畫像存儲服務。資料的次元多,字段讀取的業務方多,對時延要求敏感。
小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

RedKV整體架構分3層,接入層相容Redis協定,支援各種語言的社群版SDK和公司定制的中間件版;接入代理層支援千萬QPS的讀寫能力,無狀态擴充;存儲層提供高可靠讀寫服務。RedKV1.0架構如下圖1,下面我們詳細的展開3層元件的介紹。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖1. RedKV1.0整體架構

3.1. Client接入層

RedKV叢集部署完成後,通過公司内部提供的Service Mesh元件做服務發現,對Client提供服務。

3.2. Proxy

Proxy層由一個無狀态CorvusPlus程序組成。它相容老的Redis Client,擴縮容、更新對無Client和後端叢集無感,支援多線程、IO多路複用和端口複用特性。對比開源版本,CorvusPlus增強了自我防護和可觀測特性,實作了可線上配置的功能特性:

  • Proxy限流
  • 資料線上壓縮
  • 線程模型優化
  • backup-read優化長尾
  • 大key檢測

3.2.1. Proxy限流

小紅書目前的業務模型比較多,用戶端行為無法預期,可能出現的發版錯誤、系統問題及網絡抖動引發用戶端重試,突發的qps會影響服務穩定性。在高QPS壓力下,Proxy處理用戶端讀寫逾時,大量重試會導緻雪崩,業務高峰期單個 Proxy 帶寬可能超過機器的出入帶寬限制,而存儲叢集隻能保證在有限的資源内提供穩定可靠的服務。針對這類場景,我們需要保證流量過載時,Proxy和RedKV服務不被打崩,能保障高可用。

基于以上問題和目标,對比原生的Redis Cluster模式,RedKV基于令牌桶的流控算法支援了對連接配接數、帶寬和QPS多元度限流。在高QPS下,我們的Proxy限流防止了雪崩,如圖2;在大帶寬場景下,我們優化了時延,如圖3。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖2. 雪崩場景下的限流

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖3. 大帶寬場景下的限流

3.2.2. 資料線上壓縮

Proxy層本身隻做路由轉發,對CPU的消耗非常低。在大帶寬的場景下,我們可以充分利用Proxy的CPU資源優化帶寬和毛刺。在解析Redis協定時,使用LZ4算法對寫入資料進行線上壓縮,讀取時線上解壓。在推薦緩存的使用場景中網絡帶寬和存儲空間壓縮40%以上(如圖4),總體時延并沒有明顯的下降。因為網絡帶寬和寫入讀取資料的減少,時延毛刺也變小了。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖4. Proxy開壓縮後的帶寬優化

3.2.3. 線程模型的優化

Proxy采用IO多路複用技術,每個連接配接維護一個請求處理隊列和響應隊列,保序的傳回給用戶端。Proxy在收到RedKV Server的回應之後,如果沒有收到所有發送的cmd的傳回,則會一直等待所有cmd的傳回後再發送給client,對于讀的場景這種模式非常不友好。經過改造,如果某個cmd之前的cmd都已經正常響應,則可以馬上響應給client,不需要再等後面的所有cmd請求完成。

3.2.4. backup-read優化長尾

在公網環境下,一個CVM虛拟機和其他多個虛拟機共享一台實體機。當某個客戶的CVM占用大量資源時,很容易影響到其他CVM的P99時延(由于QOS和隔離性做的不夠好,SMI中斷和記憶體CE)。在網絡吞吐較大的情況下,某雲的DPDK容易被打爆,出現母機OOB。而在RedKV的内部實作中,如果Server請求比較大,某些key的查詢時延比較高的時候,容易産生排隊堆積,或者compaction之後的block cache失效,很容易造成IO長尾。是以,RedKV的P99讀時延的毛刺很難避免,但毛刺是偶爾發生的,目前我們的主從節點一定是離散部署在不同的母機上,同時出現P99毛刺的可能很小。基于這點,我們在Proxy層做了backup read功能,優化了RedKV的p99時延問題。

針對以上模型,我們的優化思路:

  • 檢查節點的狀态和過去的延時
  • 選擇2個節點中狀态好的那個節點發送請求
  • 計算P99時延,超過P95時延則向另外一個節點發送一定數目的backup read請求數
  • 兩個請求中任意一個請求傳回成功則成功,如果逾時則繼續重試
小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖5. Backup-read 消峰

因為backup read轉發不需要複制記憶體,通過索引來保證生命周期,而且隻有超過P95時延的封包會被檢查是否能發送backup read,是以,隻要5%的封包會發送兩次,對叢集基本不會增加壓力。圖6為一個叢集中 P999從35ms降低到4ms左右,效果非常明顯。對比HBase同樣的業務場景,用戶端在同樣的timeout的配置下,我們的方案提高了用戶端的成功率。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖6. Backup-read P999優化對比

3.2.5. 大Key檢測

我們線上很多叢集在業務使用過程中會偶發的産生一些毛刺,通過抓包發現,這裡毛刺有很大一部分原因是因為大Key造成的。為了甄别這類問題,我們在Proxy層支援的大Key的可觀測名額。Proxy在解析Redis的cmd可以附帶統計KV的大小。對于string讀類型的command,讀到的val值大于 big-string-size 判定為大key;對于寫類型的command, 請求值大于 big-string-size 判定為大key;對于hash/zset則為一次讀取的kv總數大小。通過增加read_size(所有讀請求總共讀到的位元組數) 和 write_size (所有寫請求總共寫入的位元組數)監控,rate(read_size) / rate(total_req_amount) 可以計算出平均請求值大小。大Key和熱Key是KV系統不可避免的2個場景,針對大Key,我們提供了Proxy層的資料壓縮能力;對于熱Key, 我們在Server層基于HeavyKeeper[3]算法做了topK統計和處理。

3.3. RedKV Cluster

公司的存儲需求場景比較多,如廣告業務存儲的标簽和資料模型很多,同時是非常核心的業務,業務需要做資源隔離。為了減少節點故障縮小資料的爆炸半徑 ,這裡業務我們采用無中心管控的架構,即RedKV1.0架構,它能在部署和運維上能大大簡化。無中心的叢集架構采用的是Gossip協定,存儲節點采用多程序多執行個體部署,如圖7。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖7. Gossip管控的KV Cluster

推薦模型訓練的資料量非常大,上下遊業務很多,承載的QPS高,對應叢集的節點也比較多,在故障處理和擴縮容方面會觸發gossip抖動。針對大叢集的節點管理,我們采用有中心管控的架構,即RedKV2.0架構。基于Shard管理的中心架構能更好的支援資料遷移和叢集擴縮容,存儲節點采用單程序多執行個體部署,在多活場景中可以支援副本數彈性擴充,如圖8。RedKV2.0的相關元件會在後續的技術文章中詳細介紹。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖8. 基于中心管控的KV Cluster

3.3.1. Gossip優化

RedKV1.0采用Gossip協定通信,節點故障時主從節點的切換,最長影響時間為30s。一個節點出現故障時,叢集中正常節點将故障節點标記為 fail 狀态需要經過一段收斂時間。在這段時間内,Proxy層有可能将使用者請求轉發給已經 fail 的節點,導緻請求失敗。減小叢集收斂時間能有效減少Proxy層錯誤請求數量,提高叢集的穩定性和可用性。

RedKV1.0通過如下三個步驟加快視圖收斂:

  • 探測時間優化:Redis Gossip協定正常情況下會每隔100ms随機選取一個節點發送ping包,并更新節點的ping_sent值為發送ping包時間。如果叢集很大,節點數很多,那麼故障節點被ping到的機率就會變小,最多超過node_timeout/2時間給故障節點發送ping包。這樣就會導緻節點發生故障時,叢集中正常節點不能第一時間ping到故障節點,進而無法立刻感覺到故障節點發生了故障。為了減少這部分時間,當叢集中有節點超過2s沒有收到故障節點發送的pong封包時,就立馬通知其他節點去ping故障節點。這樣可以把節點故障到正常節點給故障節點發送ping的時間控制在2s左右。
  • 判定PFAIL時間優化:Gossip 協定現有實作方式是超過node_timeout(通常為15s)時間沒有收到pong封包,就将節點狀态置為pfail。本次優化将這個時間設定為3s(可配置),如果24小時内(可配置)首次超過3s沒有收到pong封包,就将節點置為pfail狀态。如果24小時内頻繁出現,那可能是網絡抖動造成,還走原來的路徑等待node_timeout。
  • 減少PFAIL到FAIL的判定時間:隻有一個節點收到叢集1/2的節點的PFAIL資訊的時候,才會将故障節點判定為FAIL狀态。而PFAIL這個資訊是通過Gossip協定互動的,最久需要1/2 node_timeout才會通知到其他節點。是以為了加速PFAIL到FAIL的狀态,所有的節點按照統一的規則選出一個種子節點,PFAIL資訊除了随機發送一個節點意外,還會通知這個種子節點。這樣種子節點能在最快的時間學習到叢集所有節點的PFAIL資訊,進而将故障節點标記為FAIL狀态廣播到叢集。

3.3.2. RedKV Server

RedKV Server配置多個IO線程同時監聽一個端口來接受連接配接請求,每個線程上的連接配接數目會随機均衡。每個線程隻解析自己連接配接上的請求,并将解析出的封包通過key挂到對應的請求隊列上,每個隊列由一個Worker線程處理。這樣同一個key/同一個slot上的請求都會落到同一根Worker線程上處理,避免了對key進行加鎖,減少鎖沖突和線程切換。Worker線程中會對資料進行重編碼,存儲到Rocksdb本地存儲引擎。

RedKV内部的線程模型如下圖9:

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖9. RedKV Server無鎖線程模型

3.3.3. 資料存儲

RedKV目前支援的資料類型有string、hash和zset,資料節點選擇RocksDB[2]作為本地存儲引擎,叢集建立時支援配置多副本,主從節點離散部署。采用hash打散的方式存儲連續slot分片的資料,能比較好的避免熱點key問題。不同的資料類型包含(MetaKey,MetaValue) 和(DataKey, DataValue),設計格式如下:

MetaKey:

MetaValue:

DataKey:

DataValue:

在如上的編碼方式下,key的設計中保留的slot資訊,可以在擴縮容的場景中通過slot靈活的做資料遷移。

4.1. 資料複制

與傳統解決方案引入同步元件的方式不同,我們快速實作了單向資料同步以及叢集擴容需求,整體架構去除了對第三方元件的依賴,通過擴充Redis複制協定實作了RedKV資料節點的直接複制,如圖10。單向複制的限制是擴容需要基于2n做節點同步,擴容完成後背景任務根據3.3.3中定義的key的分片删除不是本節點的資料。

在多活的部署形态下,多雲叢集的一對多的資料複制采用單向複制對主叢集性能侵入較大,是以我們實作了基于中心管控的資料複制政策。該政策支援多個叢集的分片異構部署,通過Checkpoint方式定向同步資料,不再需要額外的背景任務去做資料淘汰,能很好的支援多對多的多雲叢集資料複制、資料破環和擴縮容。

圖10. RedKV的資料複制

4.2. 資料批量導入

小紅書大量的離線業務資料存儲在S3 Hive中,每天會有部分資料需要增量更新,其他的資料會被淘汰。這類場景有幾個挑戰:

  • 批量導入:如小紅書的筆記資料,一般需要小時級别甚至天級别的更新,是以業務需要有快捷的批量導入功能。
  • 快速更新:特征資料的特點就是資料量特别大,以筆記為例,全量筆記在TB 級别資料量。如果通過 Jedis SDK 寫入,那麼存儲叢集需要支援百萬QPS的機器資源。當下小紅書資料平台支援業務把資料從hive通過工作流直接導入RedKV,一般是每天淩晨開始寫資料,等到晚高峰時大量讀取。這種方法實踐下來,經常導緻 RedKV叢集的叢集記憶體OOM,影響穩定性。
  • 性能及穩定:資料在導入的過程中不能影響讀的性能

實作方案如圖11:

  • 自定義擷取叢集視圖和資料編碼的UDTF,支援RedKV1.0的資料格式
  • 将原先的抽資料,編碼,分片和排序整合成一個HiveOperator,執行完成後按指定的OutputFormat輸出SST檔案到一個指定S3目錄
  • 通過Hadoop distcp工具做資料的跨雲傳輸,走離線帶寬不影響線上的讀寫業務
  • RedKV叢集的節點SiderCar作為對象存儲的一個Client,RedKV節點加載本節點的SST并ingest
小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖11. 離線資料BulkLoad

4.3. 資料批量導出

小紅書的業務模型訓練資料通過Hash存儲在RedKV叢集中,業務下遊需要對訓練結果進行離線分析,希望RedKV具有和Hive資料流通的能力。RedKV本身是不支援Schema的,如果要将KV資料導入Hive表,則需要将Hash的KKV資料轉化為一個Table。

RedKV的内部資料按hash打散,導入Hive表則需要提供table關鍵字,先按字首掃描的方式掃描存儲節點,再生成Hive識别的檔案,最後通過Hive Load進行加載。為了更好的相容其他spark任務,我們選擇Hive支援的标準parquet列式存儲檔案,整個I/O鍊路如下圖12:

圖12. RedKV2Hive I/O

示例:RedKV裡面的Key 寫入規定以 {tablename}_ 開始,比如一個artical表

RedKV中的資料采用hmset寫入:

hmset {person}_1 name John quantity 20 price 200.23
hmset {person}_2 name Henry quantity 30 price 3000.45           

通過以上的寫入方式,可以通過配置RedKV2Hive 将KV裡面的資料導入到Hive裡面的Person表 如果單表的資料量很大,可以采用分表寫入,比如把person表分成16份

hmset {person:1}_1 name John quantity 20 price 200.23
hmset {person:1}_2 name Henry quantity 30 price 3000.45
...
hmset {person:16}_100000 name Tom quantity 43 price 234.56           

4.4. 資料的備份和恢複

小紅書的廣告資料存儲在自研的分布式KV系統中,資料安全主要面臨如下挑戰:

  • 基于LSM結構的KV系統,資料compaction導緻的空間放大會翻倍,資料量大後,資料備份需要大容量的磁盤
  • 單叢集故障後,叢集恢複的時間不可控
  • 備份資料依賴第三方系統
  • 廣告系統對資料的及時恢複能力有比較高的要求,通常要求在分鐘級。為了解決上述幾個問題,我們提出了一種中心管控的主備叢集容災政策,通過Proxy接入層的秒級切換能快速切流到一個特定的版本

實作方案如圖13:

  • 先部署一個容災叢集,主叢集對外提供讀寫服務,災備叢集儲存特定數量的快照資料
  • 低峰期,中心管控根據配置的版本數和任務時間會定時的向主叢集發送打快照的服務
  • 快照完成後通過發生遠端rsync指令将快照目錄傳送到容災叢集,主叢集低峰期資料壓縮後資料量可控,這樣災備叢集可以備份指定數量的版本資訊
  • 故障發生後,中心管控可以在災備叢集通過RPC指令指定恢複到一個特定的版本
  • Proxy接入層通過服務注冊與發現主鍵配置2組服務,通過動态的秒級切換可以将流量打向特定版本的叢集,完成服務通路的秒級切換

圖13. 叢集備份

4.5. 跨雲多活

為了應對高速增長的業務需求,公司對雲廠商的服務穩定性要求越來越高,單機房雲服務難以滿足公司穩定性的需求,跨雲多活可以提高服務的穩定性,雙寫雙讀可以實作主備資料中心均對外提供讀寫服務, 這樣既不會造成資料中心的資源浪費又可以實作跨地域容災。我們對業界常用的方案做了一些對比分析:

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

我們綜合調研其他廠商的架構經驗,提出了RedKV雙活設計( Replicator as Sidecar Service同機部署) 方案,如圖14。

  • 同機部署,網絡開銷小;
  • Sidecar Service 對主服務侵入性小;
  • 單獨部署,易于更新

架構靈活,适合日志類存儲系統雙活架構。Redis 以及圖資料庫的多雲方案都可以改造适用,具體的功能元件和實戰場景會在後續技術文章詳細介紹。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖14. 跨雲多活架構

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

正如第2節描述的小紅書業務需求場景,本節通過一個典型的業務場景來展示RedKV在noSQL存儲下的收益。

早期在沒有zprofile中台的場景下,zprofile使用者和筆記資訊都存儲在HBase。為了保證叢集的資料安全和服務穩定性,HBase采用了雙叢集部署,寫入和讀取方通過HBase Client API做資料存儲。HBase的使用者資料在數十TB,在百萬QPS下,P99時延已經在70ms左右,随着QPS的快速增長,時延越來越高,叢集擴容帶來的存儲成本也越來越高,穩定性保障也面臨極大的挑戰。

RedKV1.0上線後,經過半年多的打磨,開始慢慢承接公司的核心業務。推薦平台架構組也開始着手打造zprofile中台服務,收斂上下遊的業務,提供标準統一的讀寫方式。在存儲方案上,平台架構組同學和存儲組經過多次的業務溝通,最終選擇使用RedKV作為底層存儲,主要對接兩類業務方:分别是資料生産者 producer 以及資料消費方 consumer。zprofile最終的中台架構如下圖15:

  • zprofile-write service 對上遊提供統一的資料寫入接口服務,提供使用者和比較的Meta管理,使用者資料寫入redkv-zprofile-user叢集,筆記及其他資料寫入redkv-zprofile-other叢集。
  • zprofile-service對下遊提供統一的資料消費服務,對應時延要求不高的離線服務,RedKV本身支援單向資料複制的能力通過2個offline小叢集提供資料scan業務。

整體架構改造完成後,使用RedKV對接同樣QPS的業務能力,成本節省了36%, P99性能提升了約5倍。

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

圖15. zprofile中台

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

[1] Pinterest 資料複制:

https://medium.com/pinterest-engineering/open-sourcing-rocksplicator-a-real-time-rocksdb-data-replicator-558cd3847a9d

[2] Rocskdb:

https://github.com/facebook/rocksdb/wiki

[3] HeavyKeeper:

HeavyKeeper: An Accurate Algorithm for Finding Top-k Elephant Flows | USENIX

小紅書自研KV存儲架構如何實作萬億量級存儲與跨雲多活

雲哲

小紅書基礎架構存儲組,目前主要負責RedKV1.0架構下的功能開發和穩定性建設,研究方向為分布式KV、持久化記憶體KV存儲和表格存儲。

久美

小紅書基礎架構存儲組,目前主要負責RedKV2.0架構下的功能開發和存儲中間件建設,研究研究方向為分布式KV和存儲中間件。

文書

小紅書基礎架構存儲組,目前主要負責RedKV1.0架構下的功能開發和跨雲多活建設,研究方向為分布式KV和存儲中間件。

來源:微信公衆号:小紅書技術REDtech

出處:https://mp.weixin.qq.com/s/ES9cFTJxw058Ktb66NmKYA

繼續閱讀