下降大數據開發難度,輕量級計算實例

輕量級大數據計算java


沉重的大數據計算


如今的大數據平臺有個明顯的問題就是愈來愈沉重。一方面單機性能再也不被關注,而是轉向集羣規模的堆砌,硬件方面則儘量的避開外存計算困難,期望巨大的內存。另外一方面框架的體系愈加龐大複雜,試圖一應俱全。算法


輕量級計算需求數據庫


從技術角度來看大數據的核心主要是快速的計算以及性能的提升。這兩點其實並不必定只在大數據量的狀況下才有需求,即便是幾G,幾十G這樣的規模在高併發的狀況下也須要快速的計算和高性能。並且對於臨時性的數據處理也不適合建設大數據平臺。json


大數據計算開發難度大


目前不少大數據平臺都將努力的方向轉向了SQL,由於它是性能比拼的主要性能。通常咱們在學習的時候使用的SQL大多在三、5行以內,而實際開發過程當中碰到的可能更可能是三、5百行的SQL語句,優化SQL性能其實幾乎無助於下降這種SQL的開發難度。這時就會致使仍需大量底層的編碼,要常常編寫UDF。其實提升性能本質上是下降開發難度,若是複雜運算的自動化優化靠不住,那麼就須要快速編寫高性能的算法。數組


集羣透明化


如今的大數據平臺也在努力實現集羣的透明化,讓單機和集羣有更高的一致性,提升代碼的兼容性,必定程度上下降開發難度。可是在集羣不是很大的時候並不必定能得到最優的性能,由於高性能的計算方案因場景而異,其中有些多是相互矛盾的。且透明化只採起最保險的方法,通常是這些方法中性能較差的那個。性能優化


輕量級計算的技術特徵


輕量級計算主要有這樣一些特徵。首先它是面向過程計算,強調可集成性;其次數據源是開放的,並不必定都是在數據庫中;另外它更注重單機優化,會盡量的壓榨單個機器的計算能力,而後纔會考慮集羣。最後使用的時候要在集羣透明和高性能之間進行權衡,好比節點文件存儲,不用網絡文件系統;多個單機運算,不用統一集羣框架。網絡


漏斗運算舉例多線程

問題解釋



電商漏斗分析可以幫助運營人員分析多步驟過程當中每一步的轉化和流失狀況,上圖展現了依次觸發登陸、搜索商品、查看商品、生成訂單事件的人羣轉化流式狀況。併發


關鍵點


關於上述的功能實現有幾個關鍵點。第一是時間過濾,一次性抽取全部階段的數據是沒有必要的,咱們所關心的每每是某一個時間段內的事件數據。第二是時間窗口,即目標事件得在規定時間內發生纔算有效。第三目標事件要具有有序性,如先瀏覽商品後放入購物車,反之則不算。最後事件屬性也須要過濾,好比咱們目前只統計某一品牌的商品。app


以上關鍵隱藏着幾個難點。首先是事件窗口跨天,好比2017年1月1日12點至2017年1月6日12點也被當作5天。其次是目標事件的順序不肯定,由於是由參數決定目標事件的順序,所以可能會出現「瀏覽商品」->「放入購物車」,也可能出現「放入購物車」->「瀏覽商品」。另外是事件屬性不肯定,瀏覽商品時間有brand屬性,登陸事件可能啥屬性都沒有,而屬性又是以json串的形式存在。


傳統計算方案


針對以上這些問題,顯然採用SQL是沒法解決的,有序計算是SQL的軟肋,所以不適合寫這類過程計算。常規的作法是使用UDF,毫無疑問這種方式理論上能夠提升性能,可是開發工做量太大了,且缺少通用性,後續的維護很是麻煩。


聚合算法



聚合算法能夠算是一個比較好的解決方案,上圖爲聚合算法(單個用戶)的相關參數定義。M表示的是在規定時間窗口也就是T內,所觸發的最大事件的序號,當M的值等於K,即等於目標事件個數時,就算完成了一個流程。由於整個流程中可能會不少的序列,且還有可能重複,好比出現12123這樣的形式,明顯12的事件觸發了兩次,因而咱們就用A來記錄子序列的最大事件序號,這是分別是2和3。S記錄的是當前事件的時間戳,若是S超出了T規定的時間窗口,那麼其所在的序列將被斷定爲無效。



上圖經過集算器編寫的聚合算法的實際代碼。


優化


執行聚合算法須要對原始數據進行一些整理,好比獲取事件和用戶的總數。簡單的作法就是直接使用SQL語句的group_by,由於要同時查詢用戶碼錶和事件碼錶,因此咱們須要group_by所有數據兩次。衆所周知大數據運算的性能瓶頸常常在於硬盤,而在關係型數據庫中進行兩次group_by,即便是針對同一個表理論上仍是要遍歷兩次,CPU的計算時間能夠忽略不計,可是涉及硬盤的時間則不能忽略。



爲此咱們在集算器中設計了一種管道機制。數據在進入遊標進行group的時候,同時會有一種機制將數據壓入到管道中,在管道中繼續進行group,這樣一次運算就可以輸入兩份結果。



這就是實際的代碼和效果對比,很明顯時間縮短了將近一半。


事件ID


整個過程當中查詢事件必不可少,原先事件的ID相似於10000七、1000四、10010這樣的形式,事件順序的判斷依據這些ID編號,這種方式對在數組查找成員相對來講仍是很慢的。



咱們的作法是直接將事件ID序號化,預處理階段的時候原先的ID號被轉換爲了簡單的id序號,實際計算階段就能夠直接根據id來查找到相關的事件。一樣的由於原先的用戶ID也是一長串的數字,因此也應該進行序列化以節省查詢時間。


事件屬性


事件屬性主要存在兩個問題,一是每一個事件的屬性項不一樣,二是屬性是以json串的形式保存。作過調優的朋友應該都知道文本分析的性能都很是差,不只如此,字符串的比對也很慢,且難以實現複雜的過濾需求,轉成json對象或序表又浪費存儲空間。



因此咱們將原先的json串形式轉成了數組,造事件碼錶的時候把事件屬性整理好,事先規定好屬性順序,這樣連屬性名也省了,大量節約存儲空間,也加快了讀取速度。



這是關於用戶ID,事件ID序號化,事件屬性數組化的全部代碼。


多線程和多進程


解決完以上問題以後,再來看下如何對單機性能進行優化。咱們一開始採用的是多線程的方案,由於相對來講比較簡單,可是通過測試發現當多線程並行數達到必定數量後性能卻不升反降。在改用多進程以後,並行數一直升到機器核數,性能始終呈上升態勢。



因爲咱們使用的是java語言,形成這種狀況的緣由多是因爲多進程的內存事先已經分配好了,以後不會產生衝突。而多線程在分配內存的時候有可能會加鎖、搶資源,其餘的線程就須要等待,當內存足夠大時衝突還不會顯現,一旦內存較小就會形成衝突。


單機和多機


圖片


接下來咱們就開始搭建集羣。最初作的是4臺機器的集羣,每臺機器16個核,採用多進程的方式也就是有64個進程,經過主程序指揮,這裏主機要和64個進程進行通訊,相對來講成本有些高。


圖片


後來咱們又從新構建了個三層結構,讓每一個機器都有一個子主程序,由子主程序和上端的子程序通信,這樣機器間的通信就只剩4次。


總結體會


經過對上述案例的總結,咱們有這樣幾個體會。首先過程計算其實是一個廣泛問題,雖然大數據平臺都在努力優化SQL,但卻無助於此類問題的解決,而過分依賴UDF又會使平臺自己失去意義。另外性能優化是個不斷試錯迭代的過程,須要敏捷的開發工具快速的作出原先測試,因爲開發太慢UDF就變的不太適合了。

相關文章
相關標籤/搜索