結構化數(shù)據(jù)計算仍是重中之重
大數(shù)據(jù)平臺主要是為了應對海量數(shù)據(jù)存儲和分析的需求,海量數(shù)據(jù)存儲的確不假,除了生產(chǎn)經(jīng)營產(chǎn)生的結構化數(shù)據(jù),還有大量音視頻等非結構化數(shù)據(jù),這部分數(shù)據(jù)很大,占用的空間也很多,有時大數(shù)據(jù)平臺 80% 以上都存儲著非結構化數(shù)據(jù)。不過,數(shù)據(jù)光存儲還不行,只有利用起來才能產(chǎn)生價值,這就要進行分析了。
大數(shù)據(jù)分析要分結構化和非結構化數(shù)據(jù)兩部分討論。
結構化數(shù)據(jù)主要是企業(yè)生產(chǎn)經(jīng)營過程中產(chǎn)生的業(yè)務數(shù)據(jù),可以說是企業(yè)的核心,以往在沒有大數(shù)據(jù)平臺的時候企業(yè)主要或全部在使用的就是這部分數(shù)據(jù)。隨著業(yè)務的不斷積累,這部分數(shù)據(jù)也越來越大,傳統(tǒng)數(shù)據(jù)庫方案面臨很大挑戰(zhàn),建設大數(shù)據(jù)平臺自然要解決這部分核心數(shù)據(jù)分析問題。
有了大數(shù)據(jù)平臺,給大家的想象空間也大了起來,以往無法利用的日志、圖片、音視頻等非結構化數(shù)據(jù)也要產(chǎn)生價值,這就涉及到非結構化數(shù)據(jù)分析了。相對核心業(yè)務數(shù)據(jù)分析,非結構化數(shù)據(jù)分析看起來更像是錦上添花。即使如此,非結構化數(shù)據(jù)分析并不是孤立存在,也還會伴隨大量結構化數(shù)據(jù)處理。采集非結構化數(shù)據(jù)的同時,常常會伴隨著采集許多相關的結構化數(shù)據(jù),比如音視頻的制作人、制作時間、所屬類別、時長、…;有些非結構化數(shù)據(jù)經(jīng)過處理后也會轉變成結構化數(shù)據(jù),比如網(wǎng)頁日志中拆解出訪問人 IP、訪問時刻、關鍵搜索詞等。所謂的非結構化數(shù)據(jù)分析,經(jīng)常實際上是針對這些伴生而出的結構化數(shù)據(jù)。
結構化數(shù)據(jù)分析仍然是大數(shù)據(jù)平臺的重中之重。而結構化數(shù)據(jù)處理技術就比較成熟了,比如我們常用的基于關系數(shù)據(jù)模型的關系數(shù)據(jù)庫(SQL)。
SQL 仍是目前最廣泛的結構化數(shù)據(jù)計算技術
回歸 SQL 卻是當前大數(shù)據(jù)計算語法的一個發(fā)展傾向。在 Hadoop 體系中,早期的 PIG Latin 已經(jīng)被淘汰,而 Hive 卻一直堅挺;Spark 上也在更多地使用 Spark SQL,而 Scala 反而少很多(Scala 易學難精,作為編譯型語言不支持熱部署也有很多不方便之處)。其它一些新的大數(shù)據(jù)計算體系一般也將 SQL 作為首選的計算語法,經(jīng)過幾年時間的混戰(zhàn),現(xiàn)在 SQL 又逐步拿回了主動權。
這個現(xiàn)象,大概有這么兩個原因:
1. 實在沒什么別的好用
關系數(shù)據(jù)庫過于普及,程序員對 SQL 相當熟悉,甚至思維習慣都是 SQL 式的。SQL 用來做一些常規(guī)查詢也比較簡單,雖然用于處理復雜的過程計算或有序運算并不方便,但其它那些替代技術也好不到哪里去,碰到 SQL 難寫的運算一樣要寫和 UDF 相當?shù)膹碗s代碼,反正都是麻煩,還不如繼續(xù)用 SQL。
2. 大數(shù)據(jù)廠商的鼎力支持
大數(shù)據(jù)的技術本質(zhì)是高性能,而 SQL 是性能比拼的關鍵陣地。比性能要面對同樣的運算才有意義,過于專門和復雜的運算涉及的影響因素太多,不容易評估出大數(shù)據(jù)平臺本身的能力。而 SQL 有國際標準的 TPC 系列,所有用戶都看得懂,這樣就有明確的可比性,廠商也會把性能優(yōu)化的重點放在 SQL 上。
兼容 SQL 更利于移植
大數(shù)據(jù)平臺兼容 SQL 的好處是很明顯的,SQL 的應用非常廣泛,會 SQL 的程序員很多,如果繼續(xù)采用 SQL 則可以避免許多學習成本。支持 SQL 的前端軟件也很多,使用 SQL 的大數(shù)據(jù)平臺很容易融入這個現(xiàn)成的生態(tài)圈中。大數(shù)據(jù)平臺打算替代的傳統(tǒng)數(shù)據(jù)庫也是 SQL 語法的,這樣兼容性會很好,移植成本相對較低。
好了,我們說完大數(shù)據(jù)平臺為什么會回歸關系數(shù)據(jù)模型了。那么繼續(xù)使用關系數(shù)據(jù)模型(SQL)會存在哪些問題呢?
問題
性能低
繼續(xù)使用 SQL 的最大問題就是難以獲得大數(shù)據(jù)計算最需要的高性能。
SQL 中缺乏一些必要的數(shù)據(jù)類型和運算定義,這使得某些高性能算法無法描述,只能寄希望于計算引擎在工程上的優(yōu)化。傳統(tǒng)商業(yè)數(shù)據(jù)庫經(jīng)過幾十年的發(fā)展,優(yōu)化經(jīng)驗已經(jīng)相當豐富,但即使這樣仍有許多場景難以被優(yōu)化,理論層面的問題確實很難在工程層面解決。而新興的大數(shù)據(jù)平臺在優(yōu)化方面的經(jīng)驗還遠遠不如傳統(tǒng)數(shù)據(jù)庫,算法上不占優(yōu),就只能靠集群更多的機器獲得性能提升。另外,SQL 描述過程的能力不太好,不擅長指定執(zhí)行路徑,而想獲得高性能常常需要專門優(yōu)化的執(zhí)行路徑,這又需要增加許多特殊的修飾符來人為干預,那還不如直接用過程性語法更為直接,這也會妨礙用 SQL 寫出高性能的代碼。
SQL 發(fā)明之初的計算機硬件能力還比較差,要保證實用性,SQL 的設計必須適應當時的硬件條件,這就導致了 SQL 很難充分利用當代計算機的硬件能力,具體來說就是大內(nèi)存、并行和集群。SQL 中的 JOIN 是按鍵值對應的,而大內(nèi)存情況下其實可以直接用地址對應,不需要計算 HASH 值和比對,性能可以提高很多;SQL 的數(shù)據(jù)表無序,單表計算時還容易做到分段并行,多表關聯(lián)運算時一般就只能事先做好固定分段,很難做到同步動態(tài)分段,這就難以根據(jù)機器的負載臨時決定并行數(shù)量;對于集群運算也是這樣,SQL 在理論上不區(qū)分維表和事實表,JOIN 運算簡單地定義為笛卡爾積后過濾,要實現(xiàn)大表 JOIN 就會不可避免地產(chǎn)生占用大量網(wǎng)絡資源的 HASH Shuffle 動作,在集群節(jié)點數(shù)太多時,網(wǎng)絡傳輸造成的延遲會超過節(jié)點多帶來的好處。
舉個具體的例子,我們想在 1 億條數(shù)據(jù)中取出前 10 名,用 SQL 寫出來是這樣的:
select top 10 x,y from T order by x desc
這個語句中有個 order by,嚴格按它執(zhí)行就會涉及大排序,而排序非常慢。其實我們可以想出一個不用大排序的算法,但用 SQL 卻無法描述,只能指望數(shù)據(jù)庫優(yōu)化器了。對于這句 SQL 描述的簡單情況,很多商用數(shù)據(jù)庫確實都能優(yōu)化,使用不必大排序的算法,性能通常很好。但情況復雜一些,比如在每個分組中取前 10 名,要用窗口函數(shù)和子查詢把 SQL 寫成這樣:
select * from (select y,*,row_number() over (partition by y order by x desc) rn from T)where rn<=10
這時候,數(shù)據(jù)庫優(yōu)化器就會犯暈了,猜不出這句 SQL 的目的,只能老老實實地執(zhí)行排序的邏輯(這個語句中還是有 order by 的字樣),結果性能陡降。
開發(fā)效率低
不僅跑的慢,開發(fā)效率也不高,尤其在復雜計算方面,SQL 實現(xiàn)很繁瑣。比如根據(jù)股票記錄查詢某只股票最長連續(xù)上漲天數(shù),SQL(oracle)的寫法如下:
SELECT MAX(ContinuousDays)-1 FROM ( SELECT code, NoRisingDays, COUNT(*) ContinuousDays FROM ( SELECT code, SUM(RisingFlag) OVER (PARTITION BY code ORDER BY day) NoRisingDays FROM ( SELECT code, day, CASE WHEN price> LAG(price) OVER (PARTITION BY code ORDER BY day) THEN 0 ELSE 1 END RisingFlag FROM stock ) ) GROUP BY NoRisingDays )
用了很繞的方式實現(xiàn),別說寫出來,看懂都要半天。
此外,SQL 也很難實現(xiàn)過程計算。什么是過程性計算呢?就是一步寫不出來,需要多次分步運算,特別是與數(shù)據(jù)次序相關的運算。
我們舉幾個例子來看:
一周內(nèi)累計登錄時長超過一小時的用戶占比,但要除去登錄時長小于 10 秒的誤操作情況
信用卡在最近三個月內(nèi)最長連續(xù)消費的天數(shù)分布情況,考慮實施連續(xù)消費 10 天后積分三倍的促銷活動
一個月中有多少用戶在 24 小時連續(xù)操作了查看商品后加入購物車并購買的的動作,有多少用戶在中間步驟中放棄?
……
(為了便于理解,這些例子已經(jīng)做了簡化,實際情況的運算還要復雜很多)
這類過程性運算,用 SQL 寫出來的難度就很大,經(jīng)常還要寫 UDF 才能完成。如果 SQL 寫都寫不出來,那么 SQL 的使用效果將大打折扣。
開發(fā)效率低導致性能低
復雜 SQL 的執(zhí)行效率往往也很低,這就又回到性能的問題了,實際上開發(fā)效率和計算性能是密切相關的,很多性能問題本質(zhì)上是開發(fā)效率造成。
復雜 SQL 的優(yōu)化效果很差,在嵌套幾層之后,數(shù)據(jù)庫引擎也會暈掉,不知道如何優(yōu)化。提高這類復雜運算的性能,指望計算平臺的自動優(yōu)化就靠不住了,根本手段還要靠寫出高性能的算法。象過程式運算中還常常需要保存中間結果以復用,SQL 需要用臨時表,多了 IO 操作就會影響性能,這都不是引擎優(yōu)化能解決的事情,必須要去改寫計算過程。
所以,本質(zhì)上,提高性能還是降低開發(fā)難度。軟件無法提高硬件的性能,只能想辦法設計復雜度更低的算法,而如果能夠快速低成本地實現(xiàn)這些算法,那就可以達到提高性能的目標。如果語法體系難以甚至沒辦法描述高性能算法,必須迫使程序員采用復雜度較高的算法,那也就很難再提高性能了。優(yōu)化 SQL 運算無助于降低它的開發(fā)難度,SQL 語法體系就是那樣,無論怎樣優(yōu)化它的性能,開發(fā)難度并不會改變,很多高性能算法仍然實現(xiàn)不了,也就難以實質(zhì)性地提高運算性能。
編寫 UDF 在許多場景時確實能提高性能,但一方面開發(fā)難度很大,另一方面這是程序員硬寫的,也不能利用到 SQL 引擎的優(yōu)化能力。而且經(jīng)常并不能將完整運算都寫成 UDF,只能使用計算平臺提供的接口,仍然要在 SQL 框架使用它的數(shù)據(jù)類型,這樣還是會限制高性能算法的實現(xiàn)。
根本的解決方法,還是要讓大數(shù)據(jù)平臺真地有一些更好用的語法。
解法
使用開源集算器 SPL 就可以作為 SQL 很好的替代和延伸,作為大數(shù)據(jù)平臺專用的計算語言,延續(xù) SQL 優(yōu)點的同時改善其缺點。
SPL 是一款專業(yè)的開源數(shù)據(jù)計算引擎,提供了獨立的計算語法,整個體系不依賴關系數(shù)據(jù)模型,因此在很多方面都有長足突破,尤其在開發(fā)效率和計算性能方面。下面來盤點一下 SPL 都有哪些特性適用于當代大數(shù)據(jù)平臺。
強集成性
首先是集成性,不管 SPL 多優(yōu)秀,如果與大數(shù)據(jù)平臺無法結合使用也是白費。要在大數(shù)據(jù)平臺中使用 SPL 其實很方便,引入 jar 包就可以使用(本身也是開源的,想怎么用就怎么用)。SPL 提供了標準 JDBC 驅動,可以直接執(zhí)行 SPL 腳本,也可以調(diào)用 SPL 腳本文件。
…Class.forName(“com.esproc.jdbc.InternalDriver”);Connection conn =DriverManager.getConnection(“jdbc:esproc:local://”);Statement st = connection.();//直接執(zhí)行SPL腳本//ResultSet rs = st.executeQuery(“=100.new(~:baseNum,~*~:square2)”);//調(diào)用SPL腳本文件CallableStatement st = conn.prepareCall(“{call SplScript(?, ?)}”);st.setObject(1, 3000);st.setObject(2, 5000);ResultSet result=st.execute();…
高效開發(fā)
敏捷語法
在結構化數(shù)據(jù)計算方面,SPL 提供了獨立的計算語法和豐富的計算類庫,同時支持過程計算使得復雜計算實現(xiàn)也很簡單。前面舉的計算股票最長連漲天數(shù)的例子,用 SPL 實現(xiàn)是這樣的:
A | |
1 | =db.query(“select * from stock order by day”) |
2 | =A1.group@i(price<price[-1]).max(~.len())-1 |
按交易日排好序,將連漲的記錄分到一組,然后求最大值 -1 就是最長連續(xù)上漲天數(shù)了,完全按照自然思維實現(xiàn),不用繞來繞去,比 SQL 簡單不少。
再比如根據(jù)用戶登錄記錄列出每個用戶最近一次登錄間隔:
A | ||
1 | =ulogin.groups(uid;top(2,-logtime)) | 最后2個登錄記錄 |
2 | =A1.new(uid,#2(1).logtime-#2(2).logtime:interval) | 計算間隔 |
支持分步的 SPL 語法完成過程計算很方便。
SPL 提供了豐富的計算類庫,可以更進一步簡化運算。
直觀易用開發(fā)環(huán)境
同時,SPL 還提供了簡潔易用的開發(fā)環(huán)境,單步執(zhí)行、設置斷點,所見即所得的結果預覽窗口…,開發(fā)效率也更高。
多數(shù)據(jù)源支持
SPL 還提供了多樣性數(shù)據(jù)源支持,多種數(shù)據(jù)源可以直接使用,相比大數(shù)據(jù)平臺需要數(shù)據(jù)先“入庫”才能計算,SPL 的體系更加開放。
SPL 支持的部分數(shù)據(jù)源(仍在擴展中…)
不僅如此,SPL 還支持多種數(shù)據(jù)源混合計算,充分發(fā)揮各類數(shù)據(jù)源自身的優(yōu)勢,擴展大數(shù)據(jù)平臺的開放性。同時,直接使用多種數(shù)據(jù)源開發(fā)實現(xiàn)上也更簡單,進一步提升開發(fā)效率。
熱切換
SPL 是解釋執(zhí)行的,天然支持熱切換,這對 Java 體系下的大數(shù)據(jù)平臺是重大利好?;?SPL 的大數(shù)據(jù)計算邏輯編寫、修改和運維都不需要重啟,實時生效,開發(fā)運維更加便捷。
高計算性能
前面我們說過,高性能與高開發(fā)效率本質(zhì)上是一回事,基于 SPL 的簡潔語法更容易寫出高性能算法。同時,SPL 還提供了眾多高性能數(shù)據(jù)存儲和高性能算法機制,SQL 中很難實現(xiàn)的高性能算法及存儲方案用 SPL 卻可以輕松實現(xiàn),而軟件提高性能關鍵就在于算法和存儲。
例如前面說過的 TopN 運算,在 SPL 中 TopN 被理解為聚合運算,這樣可以將高復雜度的排序轉換成低復雜度的聚合運算,而且很還能擴展應用范圍。
A | ||
1 | =file(“data.ctx”).create().cursor() | |
2 | =A1.groups(;top(10,amount)) | 金額在前 10 名的訂單 |
3 | =A1.groups(area;top(10,amount)) | 每個地區(qū)金額在前 10 名的訂單 |
這里的語句中沒有排序字樣,也不會產(chǎn)生大排序的動作,在全集還是分組中計算 TopN 的語法基本一致,而且都會有較高的性能。