10億級儲存挑戰!看一看、微信廣告、微信支付、小程式都在用的儲存系統究竟是怎麼扛住的?!

騰訊雲加社群發表於2019-08-07

| 導語 10億級,是微信使用者的數量級。這個龐大數字的背後,是“看一看”、“微信廣告”、“微信支付”、“小程式”等業務對資料庫10億級的讀寫需求。那麼,在此場景下誕生的 FeatureKV,究竟是怎樣強悍的一個儲存系統呢?

背景:兩個十億級的挑戰

PaxosStore 是微信內廣泛應用的強一致性的分散式儲存系統,它廣泛支撐了微信的線上應用,峰值過億TPS,執行在數千臺伺服器上,線上服務場景下效能強悍。但在軟體開發中沒有銀彈,在面對離線產出、線上只讀的資料場景,PaxosStore 面臨了兩個新的十億挑戰:

10億 / 秒 的挑戰:

“看一看”團隊需要一個儲存系統來存放CTR過程需要用到的模型,實現儲存和計算分離,使得推薦模型的大小不會受限於單機記憶體。

每次對文章的排序打分,ctrsvr 會從這個儲存系統中拉取成千上萬個特徵,這些特徵需要是相同版本的,PaxosStore 的 BatchGet 不保證相同版本。

業務方預估,這個儲存系統需要支援10億/秒的QPS,PaxosStore 的副本數是固定的,無法增加只讀副本。

這個儲存系統需要有版本管理和模型管理的功能,支援歷史版本回退。

10億 / 小時 的挑戰:

微信內部不少團隊反饋,他們需要把10億級(也就是微信使用者的數量級)資訊,每天定期寫到 PaxosStore 中,但 PaxosStore 的寫入速度無法滿足要求,有時候甚至一天都寫不完,寫太快還會影響現網的其他業務。

PaxosStore 是一個保證強一致性的儲存系統,為線上業務設計,其效能也能滿足線上業務的需求。但面對這種離線灌庫、線上只讀、不要求強一致性保證的場景,就需要很高的成本才能滿足業務的需求了。

基於資料的應用越來越多,這類的資料儲存需求也越來越多,我們需要解決這個問題,把10億級key量的資料寫入時間控制在1個小時左右。

上述場景具有定時批量寫、線上只讀的特點,為了解決這些場景的痛點問題,我們基於效能強大的WFS(微信自研分散式檔案系統)和穩如磐石的Chubby(微信自研後設資料儲存),設計並實現了 FeatureKV,它是一個高效能 Key-Value 儲存系統,具有以下特點:

高效能且易於擴充套件

優秀的讀效能: 在B70機型上,全記憶體的表可以有千萬級的QPS;在TS80A機型上,資料存放於SSD的表可以有百萬級的QPS。

優秀的寫效能: 在遠端檔案系統效能足夠的情況下,可以在1小時內完成十億個key、平均ValueSize是400Byte的資料的寫入。

易於擴充套件: 水平擴容(讀效能)和縱向擴容(容量)可以在數小時內完成,寫效能擴容只是擴容一個無狀態的模組(DataSvr),可以在分鐘級完成。

對批量寫支援友好

任務式的寫介面: 支援以 WFS/HDFS 上的檔案作為輸入,業務方無需編寫、執行灌資料工具,支援失敗重試、告警。

支援增量更新/全量更新: 增量更新是在上一個版本的基礎上,對一批新輸入的 Key-Value 進行覆蓋寫,輸入中沒有的 key 則保持不變。而全量更新則是丟棄上一個版本的資料,灌入一批新的 Key-Value 。

支援TTL: 支援過期自動刪除功能。

具有版本管理功能

事務的 BatchGet 介面: 保證一次 BatchGet 得到的資料都是同一個版本的。

支援歷史版本回退: 一次更新會產生一個遞增的版本,支援歷史版本回退,包括增量更新生成的版本。

當然,在軟體開發中沒有銀彈,FeatureKV 在設計上它做了取捨:

不支援線上寫入資料,當資料量較小時(GB級),FeatureKV 可以做到十分鐘級的更新頻率。

不保證強一致性,保證最終一致性,並且在大部分時間裡可以保證順序一致性。

FeatureKV 現在在微信內部已經廣泛應用,包括看一看、微信廣告、微信支付、小程式等業務,接下來會闡述 FeatureKV 的設計,並具體說明如何解決上述兩個十億挑戰。

總體設計

  1. 系統架構

FeatureKV 涉及的外部依賴有三個:

Chubby:用來儲存系統中的後設資料。FeatureKV 內很多地方是通過對 Chubby 內的後設資料輪詢來實現分散式協同、通訊。

USER_FS:業務側的分散式檔案系統,可以是 WFS/HDFS ,因為 FeatureKV 的寫介面是任務式的,輸入是一個分散式檔案系統上的路徑。

FKV_WFS:FeatureKV 使用的分散式檔案系統,用來存放 DataSvr 產出的、可以被 KVSvr 使用的資料檔案。可以儲存多個歷史版本,用於支援歷史版本回退。

這三個外部依賴都可以和其他業務共用。FKV_WFS 和 USER_FS 可以是同一個模組。FKV_WFS 可以使用 HDFS 替代。Chubby 可以使用 etcd 替代。

DataSvr:

主要負責寫資料,把 USER_FS 的輸入,經過資料格式重整、路由分片、建索引等流程,生成 KVSvr 可用的資料檔案,寫到 FKV_WFS 中。

它是一個無狀態的服務,寫任務的狀態資訊儲存在 Chubby 中,擴容 DataSvr,可以增加系統的寫效能。

一般部署2臺就好,部分場景寫任務較多可以適當擴容。

KVSvr:

對外提供讀服務,通過輪詢 Chubby 來感知資料更新,再從 WFS 拉取資料到本地,載入資料並提供只讀服務。

它是一個有狀態服務,一個 KVSvr 模組會由 K 個 Sect 和 N 個 Role 組成,共 K * N 臺機器。

每個 Sect 都有全量的資料,每次 BatchGet 只需要發往某一個 Sect,增加 Sect 可以擴容讀效能,而並不會增加 BatchGet 的 rpc 次數。

相同的 Role 負責的資料切片都是一樣的,單機故障時 Batch 請求直接換機重試就好。

K 最少是2,用以保證系統的容災能力,包括在變更時候的可用性。

N 不能是任意一個數字,可以看下面第二部分。

寫入流程:

FeatureKV 只支援批量寫入資料,每次寫任務可以是增量更新/全量更新的,每次寫入的資料量大小無限制。離線的批量寫介面設計,我們踩過一些坑:

一開始我們打算封一些類/工具,打算讓業務端直接用我們的類/工具,打包Key-Value資料,直接寫到 FKV_WFS 的目錄上。該方案最省頻寬,但是這樣做讓我們後續的資料格式升級變得很麻煩,需要讓所有業務方配合,所以這個方案就廢棄了。

然後,我們起了一個新模組 DataSvr,在 DataSvr 上面開了一個 tcp svr,業務側輸出 Key-Value,寫入工具會把 Key-Value 資料發過來這個 tcp svr 完成打包,但是還是有下面這些問題:

寫入的速度與業務方的程式碼質量、機器資源有關,曾經碰到過的情況是,業務方的程式碼裡面用 std::stringstreams 解析浮點數輸入,這個函式佔用了 90%+ 的 CPU(用 std::strtof 會快很多),或者業務方跑寫入工具的機器,被別的程式用了 90%+ 的 CPU ,最後反饋 FeatureKV 寫得很慢。

DataSvr 的日常變更或機器故障,會導致任務失敗。前端工具發包的方法無法對任務進行重試,因為 Key-Value 的輸入流無法重放。

最終,我們設計了一個任務式的介面,以 USER_FS 上的路徑作為輸入:

業務側把資料按照約定好的格式,放在 USER_FS 中,向 DataSvr 提交一個寫任務。

DataSvr 流式讀取 USER_FS 中的資料,對資料進行格式重整、路由分片、建索引,然後把資料寫入 FKV_WFS 中,並更新 Chubby 中的後設資料。其中寫任務的分散式執行、失敗重試等,也需要通過 Chubby 來同步任務狀態。

KVSvr 通過輪詢 Chubby 感知資料更新,把資料拉取到本地,完成載入並提供服務。

圖片描述

  1. 資料路由

考慮擴縮容,FeatureKV 會把一個版本的資料切分為 N 份, N 現在是 2400,通過雜湊 HashFun(key) % N 來決定 key 屬於那份檔案。

KVSvr 載入哪些檔案是由一致性雜湊決定的,角色相同的 KVSvr 會載入相同一批在擴縮容的時候,資料騰挪的單位是檔案。

由於這個一致性雜湊只有 2400 個節點,當 2400 不能被 sect 內機器數量整除時,會出現比較明顯的負載不均衡的情況。所以 FeatureKV 的 sect 內機器數得能夠整除2400。還好 2400 是一個幸運數,它 30 以內的因數包括 1,2,3,4,5,6,8,10,12,15,16,20,24,25,30 ,已經可以滿足大部分場景了。

上圖是 N=6 時候的例子,Part_00[0-5] 表示 6 份資料檔案。從 RoleNum=2 擴容成 RoleNum=3 的時候,只需要對 Part_003 和 Part_005 這兩份檔案進行騰挪,Part_005 從 Role_0遷出至 Role_2,Part_003 從 Role_1 遷出至 Role_2。

由於現網所用的 N=2400 ,節點數較少,為了減少每次路由的耗時,我們列舉了 RoleNum<100 && 2400%RoleNum==0 的所有情況,打了一個一致性雜湊表。

圖片描述

  1. 系統擴充套件性

FeatureKV 的 FKV_WFS 上存有當前可用版本的所有資料,所以擴容導致的檔案騰挪,只需要新角色的機器從 FKV_WFS 拉取相應編號的檔案,舊角色機器的丟棄相應編號的檔案即可。

當 BatchSize 足夠大的時候,一次 BatchGet 的 rpc 數量等價於 Role 數量,這些 rpc 都是並行的。當 Role 數量較大時,這些 rpc 出現最少一個長尾請求的概率就越高,而 BatchGet 的耗時是取決於最慢一個 rpc 的。上圖展示了單次 rpc 是長尾請求的概率是 0.01% 的情況下,不同 Role 數量情況下的 BatchGet 長尾概率,通過公式 1 - (0.999^N) 計算。

增加 Sect(讀效能擴容):

每個 Sect 都有全量的資料,增加一個 Sect 意味著增加一個只讀副本,可以達到讀效能擴容的效果。

由於一個 BatchGet 只需要發往一個 Sect ,RPC 數量是收斂的,不會因為底下的 KVSvr 有 200 臺而發起 200 次 RPC。這種設計可以降低 BatchGet 操作的平均耗時,減少長尾請求出現的概率。

增加 Role(儲存容量+讀效能擴容):

假設每臺機的儲存能力是相等的,增加 Role 的數量便可以增加儲存容量。

由於整個模組的機器都多了,所以讀效能也會增加,整個模組在讀吞吐量上的擴容效果等價於增加 Sect。

但當 Role 數量較大時,一次 BatchGet 涉及的機器會變多,出現長尾請求概率會增大,所以一般建議 Role 的數量不要超過30。

增加 DataSvr(寫效能擴容):

DataSvr 是一個無狀態服務,可以做到分鐘級的擴容速度。

底下的寫任務是分散式的跑,一次寫會切分為多個並行的 job,增加 DataSvr 的例項數,可以增加整個模組的寫效能。

資料遷移都是以檔案為級別,沒有複雜的遷移邏輯,不考慮灰度流程的話,可以在小時級完成,考慮灰度流程一般是一天內。

圖片描述

  1. 系統容災

KVSvr 側:

每個 Sect 的機器是部署在同一個園區的,只需要部署 2 個 Sect 就可以容忍一個園區的機器故障。

具體案例:2019年3月23號,上海南匯園區光纜被挖斷,某個 featurekv 有 1/3 的機器在上面,故障期間服務穩定。

故障期間部分 RPC 超時,導致長尾請求增加。但是換機重試之後大部分請求都成功了,最終失敗出現次數很低。後續全域性遮蔽了南匯園區的機器之後,長尾請求和最終失敗完全消失。

DataSvr/WFS 側:

即便這兩部分整個掛掉, FeatureKV 的 KVSvr 還是可以提供只讀服務,對於大部分 定時批量寫、線上只讀 的場景,這樣已經足夠了。

具體案例:2019年6月3號,某個分散式檔案系統叢集故障,不可用9小時。某個 featurekv 的 USER_FS 和 FKV_WFS 都是這個叢集。故障期間業務方的輸出產出流程也停止了,沒有產生寫任務。整個故障期間,featurekv 的讀服務穩定。

十億每秒的挑戰-線上讀服務的具體設計

  1. KVSvr 讀效能優化

為了提高 KVSvr 的效能,我們採取了下面一些優化手段:

高效能雜湊表:針對部分資料量較少、讀請求很高的資料,FeatureKV 可以用 MemTable 這一個全記憶體的表結構來提供服務。Memtable 底層實現是一個我們自己實現的只讀雜湊表,在 16 執行緒併發訪問的時候可以達到 2800w 的 QPS,已經超過了 rpc 框架的效能,不會成為整個系統瓶頸。

libco aio:針對部分資料量較大、讀請求要求較低的資料,FeatureKV 可以用 BlkTable 或 IdxTable 這兩種表結構來提供服務,這兩表結構會把資料存放在 SSD 中。而 SSD 的讀效能需要通過多路併發訪問才能完全發揮。線上服務不可能開太多的執行緒,作業系統的排程是有開銷的。這裡我們利用了 libco 中對 linux aio 的封裝,實現了協程級的多路併發讀盤,經過壓測在 value_size 是 100Byte 的情況下,TS80A 上 4 塊 SSD 盤可以達到 150w+/s 的QPS。

資料包序列化:在 perf 調優的過程中,我們發現 batch_size 較大的情況下(ctrfeaturekv 的平均 batch_size 是 4k+),rpc 資料包的序列化時耗時會較大,所以這裡我們自己做了一層序列化/反序列化,rpc 層的引數是一段二進位制 buffer。

資料壓縮:不同業務對資料壓縮的需求是不一樣的,在儲存模型的場景,value 會是一段浮點數/浮點數陣列,表示一些非 0. 特徵。這時候如果用 snappy 這類明文壓縮演算法,效果就不太好了,壓縮比不高而且浪費 cpu。針對這類場景,我們引入了半精度浮點數(由 kimmyzhang 的 sage 庫提供)來做傳輸階段的資料壓縮,降低頻寬成本。

  1. 分散式事務 BatchGet 的實現

需求背景:更新分為全量更新和增量更新兩種,一次更新包括多條資料,每次更新都會讓版本號遞增,BatchGet 也會返回 多條資料。業務方希望這些更新都是事務的,BatchGet 的時候如果一個更新沒有全部執行完,那就返回上一個版本的資料,不能返回半新半舊的資料。

RoleNum=1 的情況:

資料沒有分片,都落在同一臺機器上,我們調研後發現有這麼兩種做法:

MVCC: 多版本併發控制,具體實現就是 LevelDB 這樣的儲存引擎,儲存多版本的資料,可以通過 snapshot 控制資料的生命週期,以及訪問指定版本的資料。這種方案的資料結構需要同時支援讀寫操作,後臺也得有執行緒通過清理過期的資料,要支援全量更新也是比較複雜。

COW: 寫時複製,具體的實現就是雙 Buffer 切換,具體到FeatureKV的場景,增量更新還需要把上一個版本的資料拷貝一份,再加上增量的資料。這種方案的好處是可以設計一個生成後只讀的資料結構,只讀的資料結構可以有更高的效能,缺點是需要雙倍的空間開銷。

為了保證線上服務的效能,我們採用了 COW 的方式,設計了 第一部分 中提到了只讀雜湊表,來做到單機的事務 BatchGet。

RoleNum>1 的情況:
圖片描述

資料分佈在不同機器,而不同機器完成資料載入的時間點不一樣,從分散式的角度去看,可能沒有一個統一的版本。

一個直觀的想法,就是儲存最近N份版本,然後選出每個 Role 都有的、最新的一份版本。

N 的取值會影響儲存資源(記憶體、磁碟)的開銷,最少是2。為了達到這個目的,我們在 DataSvr 側加入了這麼兩個限制:

單個表的更新是序列的。

寫任務開始結束之前,加多一步版本對齊的邏輯,即等待所有的 kvsvr 都載入完最新的版本。

這樣我們就可以在只保留最近 2 個版本的情況下,保證分散式上擁有一個統一的版本。在 COW 的場景下,只要把另外一個 Buffer 的資料延期刪除(直到下次更新才刪),就可以了保留最近 2 個版本了,記憶體開銷也不會變大。
圖片描述

擁有全域性統一的版本之後,事務 BatchGet 應該怎麼實現呢?

先發一輪 rpc 詢問各 role 的版本情況?這樣做會讓QPS翻倍,並且下一時刻那臺機可能就發生資料更新了。

資料更新、版本變動其實是很低頻的,大部分時刻都是返回最新一個版本就行了,並且可以在回包的時候帶上 B-Version (即另外一個 Buffer 的版本),讓 client 端在出現版本不一致的時候,可以選出一個全域性統一的版本 SyncVersion,再對不是 SyncVersion 的資料進行重試。

在資料更新的時候,資料不一致的持續時間可能是分鐘級的,這種做法會帶來一波波的重試請求,影響系統的穩定性。所以我們還做了一個優化就是快取下這個 SyncVersion ,每次 BatchGet 的時候,如果有 SyncVersion 快取,則直接拉取 SyncVersion 這個版本的資料。
圖片描述

  1. 版本回退

每個表的後設資料中有一個回退版本欄位,預設是0表示不處於回退狀態,當這個欄位非0,則表示回退至某個版本。

先考慮如何實現版本回退:

考慮簡單的情況,一個表每次都是全量更新。那麼每次讓都是讓 KVSvr 從 FKV_WFS 拉取指定版本的資料到本地,走正常的全量更新流程就好了。

然後,需要考慮增量的情況。如果一個表每次更新都是增量更新,那麼回退某個版本 Vi,就需要把 V1 到 Vi 這一段都拉到 KVSvr 本地,進行更新重放,類似於資料庫的 binlog,當累計了成千上萬的增量版本之後,這是不可能完成的事。

我們需要有一個非同步的 worker,來把一段連續的增量,以及其前面的全量版本,合併為一個新的全量版本,類似 checkpoint 的概念,這樣就可以保證一次回退不會涉及太多的增量版本。這個非同步的 worker 的實現在 DataSvr 中。

更進一步,這裡有一個優化就是如果回退的版本在本地雙 Buffer 中,那麼只是簡單的切換一下雙 Buffer 的指標就好,可以做到秒級回退效果。實際上很多回退操作都是回退到最後一個正常版本,很可能是上一個版本,在本地的雙 Buffer 中。
圖片描述

處於回退狀態的表禁止寫入資料,防止再次寫入錯誤的資料。

再考慮如何解除回退:

解除回退就是讓某個表,以回退版本的資料繼續提供服務,並且以回退版本的資料為基礎執行後續的增量更新。

直接解除回退狀態,現網會先更新為回退前的版本,如果還有流量的話則會讀到回退前的異常資料,這裡存在一個時間視窗。

資料的版本號要保證連續遞增,這一點在資料更新的流程中會依賴,所以不能簡單粗暴的刪除最後一段資料。

為了避免這個問題,我們借用了COW的思想,先複製一遍。具體的實現就是把當前回退的版本,寫出一個全量的版本,作為最新的資料版本。

這一步需要點時間,但在回退的場景下,我們對解除回退的耗時要求並不高。只要回退夠快,解除回退是安全的,就可以了。
圖片描述

十億每小時的挑戰-離線寫流程的具體設計

  1. 背景

DataSvr 主要的工作是把資料從 USER_FS 寫入 FKV_WFS,在寫入過程需要做路由切分、資料格式重建等工作,是一個流式處理的過程。

FeatureKV 中目前有三種表結構,不同的表結構在寫流程中有不一樣的處理邏輯:

MemTable: 資料全記憶體,索引是無序的雜湊結構,容量受限於記憶體,離線寫邏輯簡單。

IdxTable: 索引全記憶體,索引是有序的陣列,Key量受限於記憶體,離線寫邏輯較為簡單,需要寫多一份索引。

BlkTable: 塊索引全記憶體,索引是有序的資料,記錄著磁碟中一個 4KB 資料塊的 begin_key 和 end_key,容量沒限制,離線寫流程複雜,需要對資料檔案進行排序。

  1. 單機的 DataSvr

一開始,我們只有 MemTable,資料都是全記憶體的。MemTable 的資料最大也就 200+GB,這個資料量並不大,單機處理可以節省分散式協同、結果合併等步驟的開銷,所以我們有了上面的架構:

一次寫任務只由一個 DataSvr 執行。

Parser 每次處理一個輸入檔案,解析出 Key-Value 資料,計算路由並把資料投遞到對應的 Que。

一個 Sender 負責處理一個 Que 的資料,底下會對應多份 FKV_FS 的檔案。FKV_FS 上的一個檔案只能由一個 Sender 寫入。

總的設計思想是,讓可以並行跑的流程都並行起來,榨乾硬體資源。

具體的實現,加入了很多批量化的優化,比如對FS的IO都是帶 buffer 的,佇列資料的入隊/出隊都是 batch 的等,儘量提高整個系統的吞吐能力。

最終,在臺 24 核機器上的寫入速度可以達到 100MB/s,寫入 100GB 的資料只需要 20 分鐘左右。

圖片描述

  1. 分散式的 DataSvr

再往後,FeatureKV 需要處理十億級Key量、TB級的資料寫入,因此我們加入了 IdxTable 和 BlkTable 這兩種表結構,這對於寫流程的挑戰有以下兩點:

生成的資料需要有序,只有有序的資料才能做到範圍索引的效果,讓單機的key量不受記憶體限制。

TB 級的寫速度,100MB/s 是不夠用的,寫入 1TB 需要接近 3 小時的時間,並且這裡是不可擴充套件的,即便有很多很多機器,也是 3 小時,這裡需要變得可以擴充套件。

先考慮資料排序的問題:

我們得先把資料切片跑完,才能把一個 Part 的資料都拿出來,對資料進行排序,前面的資料切片類似於 MapReduce 的 Map,後續的排序就是 Reduce,Reduce 中存在著較大的計算資源開銷,需要做成分散式的。

Map 階段複用上述的單機 DataSvr 邏輯,資料切分後會得到一份臨時的全量結果,然後實現一個分散式的 Reduce 邏輯,每個 Reduce 的輸入是一份無序的資料,輸出一份有序的資料及其索引。

這種做法有一次全量寫和一次全量讀的額外開銷。

具體的流程如下圖所示,DATASVR SORTING 階段由多臺 DataSvr 參與,每個淺藍色的方框表示一個 DataSvr 例項。
圖片描述

再考慮大資料量情況下的擴充套件性:

參考上圖,現在 DataSvr 的排序階段其實已經是分散式的了,唯一一個單點的、無法擴容的是資料切片階段。

實現分散式的資料切片,有兩種做法:

一是每個 DataSvr 處理部分輸入的 User_Part 檔案,每個 DataSvr 都會輸出 2400 個切片後的檔案,那麼當一次分散式切片有 K 個 DataSvr 例項參與,就會生成 2400 * K 個切片後的檔案,後續需要把相同編號的檔案合併,或者直接作為排序階段的輸入。

二是每個 DataSvr 負責生成部分編號的 FKV 檔案,每次都讀入全量的使用者輸入,批處理生成一批編號的 FKV 檔案。

第一種做法如果是處理 MemTable 或者 IdxTable,就需要後接一個 Merging 過程,來把 TMP_i_0, TMP_i_1, TMP_i_2 ... 合併為一個 FKV_i。而處理 BlkTable 的時候,由於其後續是有一個 Sorting 的邏輯的,只需要把 Sorting 的邏輯改為接受多個檔案的輸入即可。故這種做法的壞處是在資料量較少的時候,MemTable 或者 IdxTable 採用分散式資料切片可能會更慢,Merging 階段的耗時會比分散式切片減少的耗時更多;

第二種做法生成的直接就是 2400 個檔案,沒有後續 Merging 流程。但它會帶來讀放大的問題,假設資料被切分成為 T 批,就會有 T-1 次額外的全量讀開銷。在資料量大的情況下,批數會越多,因為排序的資料需要全部都進記憶體,只能切得更小;

在小資料場景,單機的資料分片已經足夠了,所以我們選用了第一種方案。
圖片描述

是否分散式切分,是一個可選項,在資料量較小的情況下,可以不走這條路徑,回到單機 DataSvr 的處理流程。

最終,我們得到了一個可以線性擴充套件的離線處理流程,面對10億、1TB資料的資料:

在實現 BlkTable 之前,這是一個不可能完成的任務。

在實現分散式資料切片之前,這份資料需要 120min 才能完成寫入。

現在,我們只需要 71min 便可以完成這份資料的寫入。

上面這一套流程,其實很像 MapReduce,是多個 Map, Reduce 過程拼接在一起的結果。我們自己實現了一遍,主要是基於效能上的考慮,可以把系統優化到極致。

現網運營狀況

FeatureKV 在現在已經部署了 10+ 個模組,共 270+ 臺機,業務涉及看一看,搜一搜,微信廣告,小程式,微信支付,資料中心使用者畫像,附近的生活,好物圈等各類數業務,解決了離線生成的資料應用於線上服務的痛點問題,支撐著各類資料驅動業務的發展。

最大的一個模型儲存模組有210臺機:

11億特徵/s: 日均峰值 BatchGet 次數是29w/s,平均 BatchSize 是 3900,模組壓測時達到過 30億特徵/s。

15ms: 96.3% 的 BatchGet 請求在 15ms 內完成,99.6% 的 BatchGet 請求在 30ms 內完成。

99.999999%:99.999999% 的事務 BatchGet 執行成功。

微信廣告基於 FeatureKV 實現個性化拉取+個性化廣告位置,推薦策略能夠及時更新。相比於舊的方案,拉取量和收入都取得了較大的增長,拉取+21.8%,收入+14.3%。

微信支付在面對面發券以及支付風控中都有用 FeatureKV,儲存了多份十億級的特徵,之前一天無法更新完的資料可以在數小時內完成更新。

總結

一開始,這類定時批量寫、線上只讀的需求不太普遍,一般業務會用 PaxosStore 或者檔案分發來解決。

但隨著越來越多的應用/需求都與資料有關,這些資料需要定期大規模輸入到線上服務當中,並需要很強的版本管理能力,比如使用者畫像、機器學習的模型(DNN、LR、FM)、規則字典,甚至正排/倒排索引等,因此我們開發了 FeatureKV 來解決這類痛點問題,並取得了良好的效果。

本文轉載自“雲加社群”公眾號:QcloudCommunity

關注“雲加社群”公眾號,獲取更多技術乾貨!
圖片描述

相關文章