如何實現上億級資料的精準計數?

閒魚技術發表於2018-11-29

背景

關係型資料庫在執行計數任務時,其執行效率會隨著資料量級的增長而降低;當資料量達到億級別時,計數任務的執行效率已經低到令人不忍直視。在閒魚團隊的關係系統中,我們採用了這樣一種方式來實現億級別資料的毫秒級計數。

挑戰

閒魚現有的業務場景中,使用者收藏寶貝、關注他人的資料量,已經達到億級別。傳統的關係型資料庫如mysql,在執行有條件的count命令時,效率較低,在資料量較大的場景下,無法有效支撐線上業務的要求。

如何實現上億級資料的精準計數?如上圖,在億級別資料量級的關係型資料庫儲存中按分表key執行count操作,光是響應時長這一個屬性就已經遠遠無法滿足線上業務的要求,更不要說頻繁執行這種效能較低的查詢語句對資料庫效能的影響。 業內針對海量資料的計數場景,通常採用的解決方案有計數器和定時離線計算兩種。兩種方式各有優劣:

如何實現上億級資料的精準計數?本文提出了一種基於離線批處理+線上增量統計的設計方案,將複雜耗時的資料庫計數操作,替換為多次KV儲存的讀取和對接操作,線上上業務場景中實現了QPS峰值時響應保持在毫秒級別(10ms以內),成功率也始終接近100%的效果,有力的支撐了業務的開展。

方案

本文提出的計數方案,簡而言之,就是定時的將資料全量同步到離線庫中進行批處理,實時線上上對增量資料保持統計,最終合併兩者的結果得到精準計數值。

如何實現上億級資料的精準計數?

離線批處理

閒魚目前儲存關係資料的方式如下(省略與本文無關欄位):

如何實現上億級資料的精準計數?

如果想要統計某個使用者的某種關係數量,如A使用者收藏寶貝數量,只需要獲取到source為A且status為0的資料的總條數即可。藉助資料離線處理能力,我們首先想到的方案是按時間點對資料進行切割,即:某個時間點(如每天凌晨0點)之前的資料同步到離線儲存進行計算,之後再與今天的增量資料進行合併從而得到最終結果,如下圖:

如何實現上億級資料的精準計數?

下文中將指定的切割時間點稱為“預期快照讀時間”。在執行上圖中的離線資料同步任務時,閒魚團隊採用的阿里雲ODPS離線同步任務,採用掃表的方式完成資料離線。在單表的場景下,藉助於mysql的快照讀機制可以準確的獲取到某個時間點的快照,從而實現資料的離線、線上精準切割。

離線批處理的困境

在實際場景中,海量資料往往必須採用分庫分表的方式進行儲存。而分庫分表後,為了降低對線上業務的影響,離線資料同步任務往往是分批執行,無法保證所有表執行快照讀的時間一致,如下圖:

如何實現上億級資料的精準計數?

多個分表的快照讀時間不一致的情況下,快照讀的實際執行時間必然會和預期快照讀時間之間存在偏差,如下圖:

如何實現上億級資料的精準計數?假設在預期快照讀時間和快照讀實際執行時間的間隔內產生了一條資料變化(在上圖中,此變化具體是在凌晨30分時,該關係的狀態從0變為了1),實際執行的快照讀將無法讀取到預期快照讀時間的快照,而是最新的資料狀態。換言之,當預期快照時間≠資料實際讀取時間時,在預期快照讀時間到快照讀實際執行時間之間產生的資料變化將會汙染統計結果。然而由於資料量級過大,必須採用分庫分表的儲存、分批執行離線任務,於是按照時間點對資料切割的思路就行不通了。

解決方案

為了規避這個問題,必須捨棄固定時間點的資料切割方式,改為將同步任務開始時間作為資料快照讀時間,並依據此時間對資料進行切割。使用這樣的同步方式,獲取到的離線統計結果將如下圖所示:

如何實現上億級資料的精準計數?對不同source值計算後得到計數結果的同時,還可以得到該關係的最新更改時間,且不同關係的最新修改時間各不相同。本文將離線統計得到的包含該關係在某個時刻的最新計數值(所有關係中最新一條的修改時間+修改後的合計值),記做offlineTotal。與一般離線任務產出的結果不同的是,offlineTotal中額外包含了該關係的最新一條修改時間,下文中的線上增量資料統計方案,將基於此時間來完成資料的對接與合併。

線上實時增量統計

結合離線資料計數的結果,線上增量資料需要包含如下資訊:

  1. 可以與離線計算結果匹配上的時間;

  2. 在該時間之後這個關係計數值的變化情況。

在閒魚的實現中,我們採用KV儲存的方式,來記錄關係的變化情況。具體記錄的值為:每一個source每一天的總增量dailyIncrTotal,以及每一次發生關係更新的那一時刻的增量modifiedTimeIncr,如下表:

如何實現上億級資料的精準計數?

統計計數值時,首先使用離線計數值offlineTotal加上當天的總增量dailyIncrTotal得到一個合計值,如下圖:

如何實現上億級資料的精準計數?可以看到,由於離線任務統計的資料並不是嚴格按照時間點進行切割(通常離線任務會在每天凌晨0點至1點之間執行),離線計數值和當日增量之間會存在一些資料上的重合。此時,再根據離線計數值中的最新修改時間,取到在該時刻的增量,從合計值中去掉這部分資料即可:

如何實現上億級資料的精準計數?整理這段計算邏輯,可以得到如下公式如何實現上億級資料的精準計數?

注:ΣdailyIncrTotal表示從離線記錄中得到的最新修改時間的日期一直到計數時的日期。

至此,一次完整的計數請求,被替換為2+N次KV儲存的查詢(N為離線計算結果距當前時間的日期差,一般不會超過一天)。本方案不僅擁有和實時計數器基本相同的響應速度,同時在遇到異常情況時也可以藉助於離線批處理的能力,重複執行離線任務,滾動訂正資料。

總結

本文介紹了一種在億資料量級場景下實現快速精準計數的方案,採用離線批處理來減少線上壓力、提高計算效率,同時使用KV儲存實時記錄增量資料快照,實現了計數結果毫秒級響應,且可依賴離線資料訂正,希望能夠給讀者帶來一些使用不同角度來思考問題的啟發。有時候一些看似非常耗時、難以最佳化的場景,換個角度來思考,可能會有意想不到的收穫。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2222097/,如需轉載,請註明出處,否則將追究法律責任。

相關文章