天天看點

(課程)基于HBase做Storm 實時計算名額存儲

hi,大家好!我是祝威廉,本來微網誌也想叫祝威廉的,可惜被人占了,于是改名叫·祝威廉二世。然後總感覺哪裡不對。目前在樂視雲資料部門裡從事實時計算,資料平台、搜尋和推薦等多個方向。曾從事基礎架構,搜尋研發四年,大資料平台架構、推薦三年多,個人時間現專注于叢集自動化部署,服務管理,資源自動化排程等方向。

這次探讨的主題是:

<b>基于 hbase 做 storm 實時計算名額存儲</b>

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

好了,步入正文,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做Storm 實時計算名額存儲

hbase存儲結構設計.png

這裡,我們一行可以追蹤某個名額一天的情況。如果加再加個次元,無非增加一條記錄。而如果是 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接口。性能可以提升很多倍。

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

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

預分區

rowkey的設計要滿足兩個均勻,<b> 數量分布均勻,讀寫分布均勻</b>。尤其是第二個均勻。

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

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

寫入我們也做了些優化,因為寫的線程和 storm 是混用的(其實就是 storm 在寫)。我們不能堵住了 storm。這點我們是通過rowkey的設計來解決,保證寫入和讀取都能均勻的分布在hbase的各個regin上。如果寫入出現問題(比如hbase出現堵塞),一個可選的方案是将資料回寫到kafka,然後再起一個拓撲嘗試重新寫。第二個就是hbase的主從高可用,這個有機會以後再談。

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

<b>真實key的md5 + 時間(精确到天) + 真實的key</b>

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

我們總結下上面的内容:

redis/mysql 存儲方案存在的一些缺點。

hbase 表結構設計,充分利用了 hbase 自身的特點,有效的減少key的數量,提高查詢效率。

storm 寫入方案,用以保證出現資料延時或者 storm 拓撲當掉後不會導緻資料不可用。

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

(課程)基于HBase做Storm 實時計算名額存儲

實時落地流程設計.png

第五個圓圈是為了在實時計算出錯時,通過 spark/mr 進行資料恢複。第二個圓圈和第四個圓圈是為了做次元複制,比如我計算了五分鐘的值,這些值其實可以自動疊加到對應的小時和天上。我們稱為分裂程式第三個圓圈就是對外吐出資料了,由我們的統一查詢引擎對外提供支援查詢支援了。

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

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

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

上面的整體架構中,分裂程式是為了緩解實時寫入 hbase 的壓力,同時我們還利用 mr/spark 做為恢複機制,如果實時計算産生問題,我們可以在小時内完成恢複操作,比如日志的收集程式、分揀程式、以及格式化程式。格式化程式處理完之後是 kafka,storm 對接的是 kafka 和 hbase。

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

<b>q:海量存儲容災備份怎麼做?</b>

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

<b>q:祝同學現在的工作主要是哪些?我也是做雲伺服器的,想請教下以後的職業發展。</b>

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

<b>q:祝老師能介紹下架構中資料恢複的機制麼?</b>

a:資料恢複是通過離線 mr/spark 完成的。其實就是對原始日志重新做一遍處理。這個主要是應對實時計算出現故障,補錄資料用的。

<b>q:distinctcount,是該如何計算,比如在這一個月 ip 數?</b>

a:通過 redis 來去重的。

<b>q:祝老師,您好,對于初學者進入打資料領域學習,有什麼建議于指導,是否需要這麼大量的支撐,平時可能遇不到您說的那種情況。</b>

a:對于大資料,我覺得首先要有個一個正确的理念。這個參看我之前的課程:↓請點選“閱讀原文”檢視,第一節講的就是如何正确認識大資料。通常會有五個方向:

平台架構

基于平台之上的應用開發

算法

bi/可視化

資料分析

目前比較炙手可熱的是算法,薪資較高。其實各有各的挑戰。做的好都行。除了你自己想要的做的,公司的發展其實對你的職業發展影響也會比較大。

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

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

<b>q:針對你們的一分鐘設計,如果列值比較複雜,比如要分析使用者數,使用者來源,使用者 ip 等等,這個時候怎麼設計表結構?</b>

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

<b>q:hbase 是否适合做實時統計分析,比如 group by 操作如何能夠支撐高并發?</b>

a:不适合。隻适合簡單的 key 查詢或者 rowkey 的 range 查詢。我不建議在其之上做複雜運算。

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

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