寫過blink sql的同窗應該都有體會,明明寫的時候就很順滑,小手一抖,洋洋灑灑三百行代碼,一鼓作氣。結果跑的時候,吞吐量就是上不去。致使數據延遲高,消息嚴重積壓,被業務方瘋狂吐槽。這時候,老鳥就會告訴你,同窗,該優化優化你的代碼了,再丟過來一個連接,而後留下一臉懵逼的你。筆者就是這麼過來的,但願本文能幫助到跟我有過一樣困惑,如今還束手無策的同窗。html
先說一下相關背景吧,筆者做爲一個剛入職阿里的小白,還處在水土不服的階段,就被臨危受命,改造數據大屏。爲何說臨危受命呢,首先是此時距雙十一僅剩一個月,再者,去年的雙十一,這個大屏剛過零點就出現問題,數據一動不動,幾個小時後開始恢復,但仍然延遲嚴重。此前,筆者僅有的實時計算開發經驗是storm,用的是stream API,對於blink這種sql式的API徹底沒接觸過。接到這個需求的時候,腦子裏是懵的,靈魂三問來了,我是誰?我即將經歷什麼?我會死得有多慘?不是「此時此刻,非我莫屬」的價值觀喚醒了我,是老大的一句話,在阿里,不是先讓老闆給你資源,你再證實你本身,而是你先證實你本身,再用結果贏得資源,一席話如醍醐灌頂。而後就開始了一段有趣的故事~sql
要找性能問題出在哪兒,最好的方法就是壓測。這裏默認你們都對節點反壓有必定的瞭解,不瞭解的請先移步典型的節點反壓案例及解法。數據結構
一開始是跟着大部隊進行壓測的,壓測的結果是不經過!!!一塊兒參加壓測的有三十多個項目組,就我被點名。雙十一演練的初夜,就這樣傷心地流走了(╯°□°)╯︵ ┻━┻。西湖的水,全是個人淚啊。不過痛定思痛,我也是經過此次壓測終於定位到了瓶頸在哪裏。多線程
數據傾斜
在作單量統計的時候,不少時候都是按商家維度,行業維度在作aggregate,按商家維度,不可避免會出現熱點問題。併發
hbase寫瓶頸
當時我在調大source分片數,而且也無腦調大了各個算子的資源以後,發現輸出RPS仍是上不去,sink節點也出現了消息積壓。當時就判斷,hbase有寫瓶頸,這個我是無能爲力了。後來的事實證實我錯了,hbase的確有寫瓶頸,但緣由是咱們寫的姿式不對。至於該換什麼姿式,請繼續看下去。框架
先來分析一下咱們的數據結構(核心字段)biz_date, order_code, seller_id, seller_layer, order_status, industry_id
函數
咱們group by的典型場景有高併發
CREATE VIEW order_day_view AS SELECT industry_id, seller_layer, biz_date, count(distinct order_code) AS salesCount FROM order_view GROUP BY industry_id,seller_id,seller_layer,biz_date ;
總結下來就是,按賣家維度,行業維度什麼的,都很是容易出現數據傾斜。性能
數據傾斜其實有不少解法,這裏我不展開討論,只講咱們這個案例的解法。
傾斜的緣由,無非就是group by的字段出現了熱點,大量的消息都集中在了該字段少數幾個取值上。一般的解法是,在消息中選擇具有惟一性,或者預估會分佈比較均勻的字段。若是這個字段是整型的,能夠直接取模(模數通常是節點的併發數),若是是字符串,能夠先進行哈希計算,再取模,獲得一個分片地址(本文取名爲bucket_id)。在接下來的全部aggregate算子中,都要把他做爲group by的key之一。學習
在咱們這個案例中,咱們選擇了order_code這個具有惟一性的字段。首先在源頭把分片地址算出來,加到消息裏面,代碼以下:
SELECT o.biz_date, o.order_code, o.seller_id, o.seller_layer, o.order_status, o.industry_id, o.bucket_id FROM (select *,MOD(hash_code(order_code), 32) AS bucket_id from order_stream) o
而後把這個bucket_id層層傳遞下去,在每個須要group by的地方都在後面帶上bucket_id,例如:
CREATE VIEW order_day_view AS SELECT industry_id, seller_layer, biz_date, count(distinct order_code) AS salesCount, bucket_id FROM order_view GROUP BY industry_id,seller_id,seller_layer,biz_date,bucket_id ;
事實上,我一開始想到的是用下面tips裏的方法,結果就杵進垃圾堆裏了,性能問題是解了,可是計算出來的數據都翻倍了,明顯是錯的。至於我是怎麼發現這個問題,並分析其緣由,再換了解法,又是另外一段故事了。能夠提早預告一下,是踩了blink撤回計算的坑,後面會再出一個專題來說述這個故事噠~
這裏還想再延伸一下,講講個人學習方法。若是讀者中有跟我同樣的小白,可能會奇怪,一樣是小白,爲什麼你這麼秀,一上來就搞壓測,還能準確地分析出性能的瓶頸在哪裏。其實有兩方面的緣由,一方面是我有過storm的開發經驗,對實時計算中會遇到的坑仍是有必定的認識;另外一方面,是我沒說出來的多少個日日夜夜苦逼學習充電的故事。個人學習習慣是喜歡追根溯源,就找了不少介紹flink基本概念,發展歷史,以及跟流式和批處理計算框架橫向對比的各種博客。並且帶着kpi去學習和什麼包袱都沒有去學習,心態和學習效率是不同的。前者雖然效率更高,可是是以損害身心健康爲代價的,由於學習過程當中不可避免的會產生急躁情緒,而後就會不可避免的加班,熬夜,咖啡,再而後他們的好朋友,黑眼圈,豆豆,感冒就全來了。後者雖然輕鬆,可是什麼包袱都沒有,反而會產生懈怠,沒有壓力就沒有動力,這是人的天性,拗不過的。這就是矛盾的點,因此在阿里,常常提到「既要也要還要」,其實宣揚的是一種學會平衡的價值觀。至於怎麼平衡,嘻嘻,天知地知我知。對,只能本身去領悟怎麼平衡,別人教不會的。
概念有了必定的認知,下面就開始實踐了。整個實踐的過程,其實就是在不斷的試錯。我是一開始連反壓的概念都不知道的,一直在無腦的調大CU,調大內存,調高併發數,調整每兩個節點之間的併發數比例。寄但願於這樣能解決問題,結果固然是不管我怎麼調,吞吐量都是都風雨不動安如山。如今想一想仍是太年輕呀,若是這樣簡單的作法能解決問題,那那個前輩就絕對不會搞砸了,還輪的到我今天來解決。後來也是在無盡的絕望中想通了,不能再這麼無腦了,我要找其餘法子。想到的就是在代碼層面動刀子,固然試錯的基本路線沒有動搖,前面也提到過,我一開始是想到的「加鹽」,也是在試錯。
學習方式決定了我作什麼事,都不可能一次成功。甚至有不少狀況,我明知道這樣作是錯的,但我就是想弄明白爲何行不通,而故意去踩這個坑。不過也正是由於試了不少錯,踩了不少坑,才挖出了更多的有價值的知識點,擴大了知識的邊界。
此時無聲勝有聲,送上幾句名言,與諸君共勉
因禍得福,焉知非福。---淮南子·人間訓
一切過往,皆爲序章。---阿里巴巴·行癲
學習就像跑步同樣,每一步都算數。---百阿·南秋
tips: 若是在消息自己中找不到分佈均勻的字段,能夠考慮給每一條消息加上一個時間戳,直接使用系統函數獲取當前時間,而後再對時間戳進行哈希取模計算,獲得分片地址。至關於強行在時間維度上對消息進行打散,這種作法也被形象的稱爲「加鹽」。
上一段看下來,彷佛只解決了數據傾斜的問題。以前還提到有一個hbase寫瓶頸問題,這個該如何解呢?
仍是接着上面的思路繼續走下去,當咱們把bucket_id一路傳遞下去,到了sink任務的時候,假設咱們要按商家維度來統計單量,可是別忘了,咱們統計的結果還按訂單號來分片了的,因此爲了獲得最終的統計值,還須要把全部分片下的值再sum一下才行,這大概也是大多數人能想到的常規作法。並且咱們現有的hbase rowKey設計,也是每一個維度的統計數據對應一個rowKey的,爲了兼容現有的設計,必須在寫hbase以前sum一下。
可是筆者當時突發奇想,恰恰要反其道而行之,我就不sum,對於rowKey,我也給它分個片,就是在原來rowKey的基礎上,後面再追加一個bucket_id。就至關於原來寫到一個rowKey上的數據,如今把他們分散寫到64個分片上了。
具體實現代碼以下:
INSERT INTO hbase_result_sink SELECT CONCAT(businessRowkey, '|', bucket_id) AS businessRowkey, cast(uopAcceptCount as DECIMAL) from hashBucket_view
這樣一來,API也必須改造了,讀的時候採用scan模式,把全部分片都讀出來,而後求和,至關於把sum的工做轉移到API端了。
這樣作的好處在於,一方面能夠轉移一部分計算壓力,另外一方面,由於rowKey只有一個,而咱們寫rowKey的任務(即sink節點)併發數可能有多個,Java開發者應該都深有體會,多線程併發對一個變量進行累加的時候,是須要加鎖和釋放鎖的,會有性能損耗,能夠猜想,hbase的寫瓶頸就在於此。後來的事實也證實,這種作法將輸出RPS提高了不止一個兩個檔次。
人事已盡,接下來就是關二爺的事了( ̄∇ ̄)。雙十一零點倒計時結束,大屏數字開始飆升起來,隨之一塊兒的,還有個人腎上腺素。再看看數據曲線,延遲正常,流量峯值達平常的10倍。其實結果徹底是在預期以內的,由於從最後一次的壓測表現來看,100W的輸入峯值(平常的333倍),5W的輸出峯值(平常的400倍),都能穩穩的扛下來。出於數(懶)據(癌)安(晚)全(期)的角度考慮,不少大屏和數據曲線的截圖就不放出來了。
其實如今回過頭再看,此時的心裏是平靜如水的。不是大獲全勝後的傲嬌,也不是退隱山林的怯懦。只是看待問題的心態變了。沒有翻不過的山,沒有邁不過的坎。遇事不急躁,走好當下的每一步就好,也沒必要思考是對是錯,由於每一步都算數,最後總能到達終點。
筆者寫文章習慣帶一些有故事趣味性的章節在裏面,由於我以爲純講技術,即便是技術人看起來也會至關乏味,再者純講技術的前提是做者具有真正透進骨髓去講述的功底,筆者自認爲還相差甚遠,只能加點魚目來混珠了。換個角度來看,純技術性的文章,觀賞性和權威性更強,每一句都是精華,這種咀嚼後的知識雖有養分飽滿,可是不是那麼容易消化,消化後能吸取多少,還有待確認。因此我力求展現個人咀嚼過程,更可能是面向跟我同樣的小白用戶,若是以爲冗長,請各位讀者姥爺見諒~
本文做者:王科,花名伏難,前後經歷國內三大電商平臺,蘇寧,京東,阿里,電商大戰深度參與者。現任職阿里巴巴供應鏈事業部,從事業務平臺化改造、實時計算相關工做。信奉技術自由平等,但願經過簡單形象的語言,打破上層建築構建的知識壁壘,讓天下沒有難作的技術。
本文做者:王科
本文爲阿里雲內容,未經容許不得轉載。