瞄一眼,帶你走進SparkSQL的世界
02-10
作者:范欣欣(本篇文章僅限知乎內部分享,如需轉載,請取得作者同意授權。)最近想來,大數據相關技術與傳統型資料庫技術很多都是相互融合、互相借鑒的。傳統型資料庫強勢在於其久經考驗的SQL優化器經驗,弱勢在於分散式領域的高可用性、容錯性、擴展性等,假以時日,讓其經過一定的改造,比如引入Paxos、raft等,強化自己在分散式領域的能力,相信一定會在大數據系統中佔有一席之地。相反,大數據相關技術優勢在於其天生的擴展性、可用性、容錯性等,但其SQL優化器經驗卻基本全部來自於傳統型資料庫,當然,針對列式存儲大數據SQL優化器會有一定的優化策略。本文主要介紹SparkSQL的優化器系統Catalyst,上文講到其設計思路基本都來自於傳統型資料庫,而且和大多數當前的大數據SQL處理引擎設計基本相同(Impala、Presto、Hive(Calcite)等),因此通過本文的學習也可以基本了解所有其他SQL處理引擎的工作原理。SQL優化器核心執行策略主要分為兩個大的方向:基於規則優化(CRO)以及基於代價優化(CBO),基於規則優化是一種經驗式、啟發式地優化思路,更多地依靠前輩總結出來的優化規則,簡單易行且能夠覆蓋到大部分優化邏輯,但是對於核心優化運算元Join卻顯得有點力不從心。舉個簡單的例子,兩個表執行Join到底應該使用BroadcastHashJoin還是SortMergeJoin?當前SparkSQL的方式是通過手工設定參數來確定,如果一個表的數據量小於這個值就使用BroadcastHashJoin,但是這種方案顯得很不優雅,很不靈活。基於代價優化就是為了解決這類問題,它會針對每個Join評估當前兩張表使用每種Join策略的代價,根據代價估算確定一種代價最小的方案。本文將會重點介紹基於規則的優化策略,後續文章會詳細介紹基於代價的優化策略。下圖中紅色框框部分將是本文的介紹重點:



Parser
Parser簡單來說是將SQL字元串切分成一個一個Token,再根據一定語義規則解析為一棵語法樹。Parser模塊目前基本都使用第三方類庫ANTLR進行實現,比如Hive、 Presto、SparkSQL等。下圖是一個示例性的SQL語句(有兩張表,其中people表主要存儲用戶基本信息,score表存儲用戶的各種成績),通過Parser解析後的AST語法樹如右圖所示:



上圖左邊是經過Analyzer解析後的語法樹,語法樹中兩個表先做join,之後再使用age>10對結果進行過濾。大家知道join運算元通常是一個非常耗時的運算元,耗時多少一般取決於參與join的兩個表的大小,如果能夠減少參與join兩表的大小,就可以大大降低join運算元所需時間。謂詞下推就是這樣一種功能,它會將過濾操作下推到join之前進行,上圖中過濾條件age>0以及id!=null兩個條件就分別下推到了join之前。這樣,系統在掃描數據的時候就對數據進行了過濾,參與join的數據量將會得到顯著的減少,join耗時必然也會降低。




至此,筆者通過一個簡單的示例完整的介紹了Catalyst的整個工作流程,包括Parser階段、Analyzer階段、Optimize階段以及Physical Planning階段。有同學可能會比較感興趣Spark環境下如何查看一條具體的SQL的整個過程,在此介紹兩種方法:
1. 使用queryExecution方法查看邏輯執行計劃,使用explain方法查看物理執行計劃,分別如下所示:


4. 一個Spark SQL作業的一生:http://ks.netease.com/blog?id=3125
推薦閱讀:
※如何使用 parquet file 建立一個 RDD 的同時讀進 case class 結構?
※矽谷之路 48: 深入淺出Spark(五)數據怎麼存
※如何評價spark的機器學習框架 和 tensorflow的機器學習系統?
※Spark Streaming + Elasticsearch構建App異常監控平台
※spark2.1 NIO_BUFFER_LIMIT常量定義的作用?
