基于HBase做Storm 實時計算指標存儲

jopen 9年前發布 | 25K 次閱讀 HBase Storm 分布式/云計算/大數據

Hi,大家好!我是祝威廉,本來微博也想叫祝威廉的,可惜被人占了,于是改名叫·祝威廉二 世。然后總感覺哪里不對。目前在樂視云數據部門里從事實時計算,數據平臺、搜索和推薦等多個方向。曾從事基礎框架,搜索研發四年,大數據平臺架構、推薦三 年多,個人時間現專注于集群自動化部署,服務管理,資源自動化調度等方向。

這次探討的主題是:

基于 HBase 做 Storm 實時計算指標存儲

HBase 實時指標存儲是我入職樂視云后對原有的實時系統改造的一部分。部分分享內容其實還處于實施階段。架構方案設計的話應該是仁者見仁智者見智,也會有很多考慮 不周的地方,歡迎大家批評指正。說不定大家聽完分享后好的提議我們會用到工程上,也為后面的實際課程做好準備。

我之前做過一次大數據的課,比較 Naive,但是也包含了我對數據平臺的一些看法。
參看:http://www.stuq.org/course/detail/999

好了,步入正文,O(∩_∩)O~

  • HBase 存儲設計
  • Storm 結果如何存儲到 HBase
  • HBase 寫入性能優化
  • 與傳統方案 (Redis/MySQL) 對比

樂視云內部用 Storm 做 CDN,點播,直播流量的計算,同時還有慢速比,卡頓比等統計指標。相應的指標會由指標名稱,業務類型,客戶,地域,ISP 等多個維度組成。指標計算一個比較大的問題是 Key 的集合很大。

舉個例子,假設我們有客戶 10w,計算指標假設 100 個,5 個 ISP,30 個地域,這樣就有億級以上的 Key 了,我們還要統計分鐘級別,小時級別,天級別,月級別。所以寫入量和存儲量都不小。

如果采用 Redis/Memcached 寫入速度是沒有問題的,畢竟完全的內存操作。但是 key 集合太大,其實壓力也蠻大的,我去的時候因為加了指標,結果導致 Memcache 被寫爆了,所以緊急做了擴容。

首先是 Redis 查起來的太麻煩。客戶端為了某個查詢,需要匯總成千上萬個 Key。。。業務方表示很蛋疼,我們也表示很蛋疼
其次,內存是有限的,只能存當天的。以前的數據需要轉存。

第三,你還是繞不過持久化存儲,于是引入 MySQL,現在是每天一張表。那 Redis 導入到 MySQL 本身就麻煩。所以工作量多了,查詢也麻煩,查一個月半年的數據就吐血了。

鑒于以上原因,我們就想著有沒有更合適的方案。

我們首先就想到了 HBase,因為 HBase 還是具有蠻強悍的寫入性功能以及優秀的可擴展性。而事實上經過調研,我們發現 HBase 還是非常適合指標查詢的,可以有效的通過列來減少 key 的數量。

舉個例子,我現在想繪制某一個視頻昨天每一分鐘的播放量的曲線圖。如果是 Redis,你很可能需要查詢 1440 個 Key。如果是 HBase,只要一條記錄就搞定。
我們現在上圖:


HBase存儲結構設計.png
</div>

這里,我們一行可以追蹤某個指標一天的情況。如果加再加個維度,無非增加一條記錄。而如果是 redis,可能就多了一倍,也就是 2880 個 key 了。

假設該視頻是 A,已經在線上 100 天了。我們會記錄這個視頻所有的 1 分鐘播放數,用 Redis 可能有 100*1440 個 key,但是 HBase只要獲取 100 條記錄就可以找出來,我們把時間粒度轉化為了 hbase 的列,從而減少行 (Key)。

我們知道 HBase 是可以多列族,多 Column,Schemaless 的。所以這里,我們建了一個列族,在該列族上,直接建了 1440 個 Column。Column 的數目和時間粒度有關。如果是一分鐘粒度,會有 1440 個,如果是五分鐘粒度的會有 288 個,如果是小時粒度的,會有 24 個。不同的粒度,我們會建不同的表。

寫入的時候,我們可以定位到 rowkey,以及對應的 column,這里一般不會存在并發寫。當然 HBase 的 increment 已經解決了并發問題,但是會造成一定的性能影響。

查詢的時候,可根據天的區間查出一條相應的記錄。我們是直接把記錄都取出來,Column 只是一個 Int/Long 類型,所以 1440 個 Column 數據也不算大。
Storm 計算這一塊,還有一個比較有意思的地方。假設 A 指標是五分鐘粒度的,也就是說我們會存儲 A 指標每個五分鐘的值。但是在實際做存儲的時候,他并不是五分鐘結束后就往 HBase 里存儲,而是每隔(幾秒/或者一定條數后)就 increment 到 HBase 中,然后清除重新計數。

這里其實我要強調的是,到 HBase 并不是覆蓋某個 Rowkey 特定的 Cloumn 值,而是在它原有的基礎上,做加法。這樣做可以防止時間周期比較長的指標,其累計值不會因為有拓撲當掉了而丟失數據(其實還是會丟的,但可能損失的計數比較少而已)。

丟數據比如你 kill-9 了。

大家可以想象一下,如果我計算一個五分鐘的指標,到第三分鐘掛掉了,此時累計值是 1000,接著拓撲重啟了,五分鐘還沒完,剩下的兩分鐘它會接著累計,此時是 500。如果是覆蓋寫,就會得到不正確的結果,實際上整個完整的計數是 1500。

防止拓撲當掉并不是這樣設計的主要原因,還有一點是計算延時了,比如某個數據片段因為某個原因,延時了十分鐘才到 Storm 實時計算集群,這個時候新得到的值還可以加回去,如果是覆蓋,數據就錯誤了。

所以 HBase 存儲這塊就變成做加法操作而不僅僅是簡單的更新了。目前 HBase 添加了計數的功能 (Increment),而另外一個比較神奇的接口設計的地方是,竟然沒有從名字上看的出是批量increment接口,一開始我以為沒有,后面是去看源 碼,才發現是有的,就是batch接口,put,increment等都可以使用這種接口去批量提交,提高查詢效率。

另外 HBase 的 Client 也是非常的奇特,比如 HTablePool 竟然是對象池而不是真實的Connection連接池,多個 HTable 對象是共享一個 Connection 鏈接的。當然,這里 HTable 的 Connection 會比較復雜,因為要連 Zookeeper 還有各個 Region。如果過多了,可能會對Zookeeper造成壓力,這倒也問題不大。

如果不過批量接口,客戶端的寫入量死活是上不去。16 臺 32G,24 核的服務器,我做了預分區 (60個左右),用了四十個進程,300 個左右的線程去寫,也就只能寫到 60000/s 而已。

但實際并發應該是只有 40 左右的。300 個線程并沒有起到太多作用。

還有就是,HBase 的 incrementColumnValue 的性能確實不高。至少和批量 Put 差距很大。所以一定要使用Batch接口。性能可以提升很多倍。

我們的測試中,還是比較平穩的,整個寫入狀態。抖動不大。

在整個過程中,有兩點要注意:

  1. 預分區
  2. rowkey的設計要滿足兩個均勻,數量分布均勻,讀寫分布均勻。尤其是第二個均勻。

預分區是要看場景的,在我們這個場景下是預分區是非常重要的。否則一開始都集中在一臺機器的一個 Regin 上寫,估計很快寫的進程就都堵住了。上線就會掛。

所以我事先收集了幾天的 key,然后預先根據 key 的分布做了分區。我測試過,在我們的集群上,到了 60 個分區就是一個瓶頸,再加分區已經不能提升寫入量。

寫入我們也做了些優化,因為寫的線程和 Storm 是混用的(其實就是 Storm 在寫)。我們不能堵住了 Storm。這點我們是通過rowkey的設計來解決,保證寫入和讀取都能均勻的分布在HBase的各個Regin上。

上面的設計稿中,大家可以看到Rowkey的組成。我的建議是這樣

真實key的md5 + 時間(精確到天) + 真實的key

因為md5還是有可能碰撞,所以真實的key必須存在,這點很重要,否則一旦有碰撞,計費就出問題了。

我們總結下上面的內容:

Redis/Mysql 存儲方案存在的一些缺點。
HBase 表結構設計,充分李永樂 HBase 自身的特點,有效的減少Key的數量,提高查詢效率。
Storm 寫入方案,用以保證出現數據延時或者 Storm 拓撲當掉后不會導致數據不可用。

我們再看看整個存儲體系完整的拓撲圖。


實時落地流程設計.png
</div>

第五個圓圈是為了在實時計算出錯時,通過 Spark/MR 進行數據恢復。
第二個圓圈和第四個圓圈是為了做維度復制,比如我計算了五分鐘的值,這些值其實可以自動疊加到對應的小時和天上。我們稱為分裂程序
第三個圓圈就是對外吐出數據了,由我們的統一查詢引擎對外提供支持查詢支持了。

我們對查詢做一個推演。如果我要給用戶繪制流量的一個月曲線圖。曲線的最小粒度是小時,小時的值是取 12 個五分鐘里最高的值,我們看看需要取多少條記錄完成這個查詢。

我們需要取 31 條五分鐘的記錄,每條記錄有 288 個點,對這 288 個點分成 24 份(具體就是把分鐘去掉 groupBy 一下),求出每份里的最大值(每組 SortBy 一下),這樣就得到了 24 個值。

我取過兩天的,整個 HTTP 響應時間可以控制 50ms 左右(本機測試)。

上面的整體架構中,分裂程序是為了緩解實時寫入 HBase 的壓力,同時我們還利用 MR/Spark 做為恢復機制,如果實時計算產生問題,我們可以在小時內完成恢復操作,比如日志的收集程序、分揀程序、以及格式化程序。格式化程序處理完之后是 kafka,Storm 對接的是 Kafka 和 HBase。

上面就是今天分享的內容了。
感謝大家。

課程 Q&A

Q:海量存儲容災備份怎么做?

A:這個問得比較大。我只能從 HBase 的角度大概說下。HBase 是基于 HDFS 做的,HDFS 本身數據就會有 replication。通常是 3 份。所以一般機器故障是沒什么問題的。但是要做到災備,可能就要涉及到多機房問題了。比如冷備或者所謂的多活等方案。

Q:祝同學現在的工作主要是哪些?我也是做云服務器的,想請教下以后的職業發展。

A:目前現階段主要工作是實時計算的架構調整,以及數據平臺的構建,為未來的更詳細的數據分析和推薦等做好準備。云服務這塊,我覺得方向可以多參看 DaoCloud,數人科技。
深入容器技術或者資源調度,或者整合現有技術做完整解決方案。在整個大數據領域,算法工程師最吃香,架構也不錯。

Q:祝老師能介紹下架構中數據恢復的機制么?

A:數據恢復是通過離線 MR/Spark 完成的。其實就是對原始日志重新做一遍處理。這個主要是應對實時計算出現故障,補錄數據用的。

Q:distinctcount,是該如何計算,比如在這一個月 ip 數?

A:通過 Redis 來去重的。

Q:祝老師,您好,對于初學者進入打數據領域學習,有什么建議于指導,是否需要這么大量的支撐,平時可能遇不到您說的那種情況。

A:對于大數據,我覺得首先要有個一個正確的理念。這個參看我之前的課程:↓請點擊“閱讀原文”查看,第一節講的就是如何正確認識大數據。通常會有五個方向:

  • 平臺架構
  • 基于平臺之上的應用開發
  • 算法
  • BI/可視化
  • 數據分析
    目前比較炙手可熱的是算法,薪資較高。其實各有各的挑戰。做的好都行。除了你自己想要的做的,公司的發展其實對你的職業發展影響也會比較大。

Q:老師我對您那個架構有一個問題既然有 1在計算為啥還要有 2 和 4?

A:我們是做實時計算的。但是實時計算可能出現故障,比如 crash 或者有些 bug,這個時候就需要 2/4 離線補錄重算。

Q:針對你們的一分鐘設計,如果列值比較復雜,比如要分析用戶數,用戶來源,用戶 ip 等等,這個時候怎么設計表結構?

A:用戶來源,用戶 ip 應該設計在 key 里而不是列里。列里存的是某個 key 在某天的某個一分鐘里產生的數。對于 HBase 理論上其實我也是不怕 key 多的,它本來就是為了海量存儲設計的。

Q:HBase 是否適合做實時統計分析,比如 group by 操作如何能夠支撐高并發?

A:不適合。只適合簡單的 key 查詢或者 rowkey 的 range 查詢。我不建議在其之上做復雜運算。

Q:祝老師您好,我最近要一個協處理器的功能,但是業務需要區別 hbase 的新增和更新,我在 Google 找到 incrementcolumnvalue 可以做到,但是目前并沒有試成功,請問您有沒有這方面的經驗或者建議呢?謝謝!

A:無法使用是版本問題么?incrementcolumnvalue 就是新增,不存在則視初始值為 0,并且它會直接返回新增后的結果值,并且能保證原子操作。

</div> 來自:http://www.jianshu.com/p/1f9153cfde67

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!