1. 程式人生 > >Kylin實踐之使用Hive檢視

Kylin實踐之使用Hive檢視

為什麼需要使用檢視

       Kylin在使用的過程中使用hive作為cube的輸入,但是有些情況下,hive中的表定義和資料並不能滿足分析的需求,例如有些列的值需要進行處理,有些列的型別不滿足需求,甚至有時候在建立hive表時為了圖方便,hive中的所有列都被定義成了string,因此很多情況下在使用Kylin之前需要對hive上的資料格式進行適當的修剪,但是使用alter table的方式修改hive原始的schema資訊未免會對其它依賴hive的元件有所影響(例如可能導致資料匯入失敗),於是不得不另闢蹊徑,而此時使用hive的檢視就是一個非常好的方案。

       當然,除了Hive資料來源本身schema的限制之外,Kylin對於hive的使用還有一定的限制,這也間接的導致我們需要使用檢視:
1、同一個專案下使用相同表(可能根據不同的filter條件過濾,或者設定了不同的維度等)建立了不同的cube,會導致查詢的時候定位到錯誤的cube。
2、只支援星狀模型

如何使用檢視

       Hive目前只支援邏輯檢視,而我們需要的僅僅是對Hive原始的schema資訊的修改,而並非希望通過物化檢視優化查詢速度(當然如果有就更好了~),因此目前Hive對檢視的支援可以滿足Kylin的需要。下面根據不同的場景分別介紹一下如何建立檢視作為Kylin的輸入源。

1、分表的情況:兩個表具有相同的結構,但是儲存不同的資料,例如一個表儲存Android端的訪問資料,一個訪問IOS端的資料,可以通過view搞定。
例如在我們的環境中有一個使用者有兩張表product_android和product_ios,這兩個表具有相同的表結構,使用者需要將平臺(android或者IOS)作為一個維度進行分析,因此我們為其建立了這樣的view:
create view product_cube as
select userid, eventid, label, day, ‘android’ as platform from product_android WHERE category=’a’
UNION ALL
select userid, eventid, label, day, ‘ios’ as platform from product_ios WHERE category=’a’;
這樣可以將product_cube作為事實表建立cube而platform作為其中的一個維度

2、自定義函式,Apache Calcite是支援自定義函式的,但是Kylin支援自定義函式的代價比較大,因此如果需要使用自定義函式,可以在Hive中建立view得到每一個希望的欄位。

3、雪花狀模型的支援,目前kylin僅支援星狀模型,而通過建立view可以輕易地把雪花狀模型轉換成星型模型,甚至生成一個大寬表。

4、頻繁修改表字段名,Kylin直接使用hive中的欄位名作為元資料,如果頻繁修改事實表或者維度表的欄位名會導致元資料錯誤([https://issues.apache.org/jira/browse/KYLIN-1173]),因此通過view增加一層對映是比較好的方法,這樣可以使得原生的hive表的欄位名對Kylin的cube透明,此後再需要修改欄位名的時候不會對cube有所影響,只需要修改view的定義。

5、UHC維度,當一個維度的cardinality比較大時可能會出現的問題比較多,首先可能導致extract fact columns這一步時導致reducer出現OOM,其次在建立維度字典樹可能會導致維度字典樹太大佔據大量的記憶體,另外會導致cube的構建速度緩慢,佔用大量的儲存空間,此時就需要思考一下這樣的UHC維度是否是必須的,是否可以提取出部分資訊減小維度,例如timestamp維度,是否可以精確到5分鐘,詳細地址的維度,是否可以精確到縣、鎮,這樣可以大大減小維度數,而且更詳細的資訊並沒有太大的統計意義。例如url型別的維度,是否可以把引數去掉只保留訪問路徑。在我們的實踐中,如果使用者有基於細粒度的時間統計的需求,我們會建議將時間規整到5分鐘或者1分鐘,建立view的時候使用如下的方式得到時間列:from_unixtime((unix_timestamp(wanproxy_stat.ts) - unix_timestamp(wanproxy_stat.ts) % 300), ‘yyyy-MM-dd HH:mm:ss’) as ts

6、維度作為度量,在我們建立的cube中有一個例子是這樣的,表中欄位有時間、位置、速度,其中時間和位置是維度,速度是度量,型別是double,使用者需要統計根據每一個維度或者維度組合中平均速度小於M並且小於N的平均值,這裡就需要根據度量進行過濾,因此還需要將速度作為維度,但是速度的cardinality實在太大,因此我們將速度取整之後將其按5取整,這樣的話,就可以大大降低這一個維度的取值。例如我們的一個使用者有根據速度進行過濾的需求,那麼我們為其建立瞭如下的維度列用於速度的過濾:
floor(cast(wanproxy_stat.datasize / wanproxy_stat.uploaderusetime as int) / 5) * 5 as velocity
而原始的速度列cast(wanproxy_stat.datasize / wanproxy_stat.uploaderusetime as int) as speed直接作為度量,那麼在查詢的時候可以執行這樣的sql:
select ts, sum(speed) / count(1) from table where velocity < N and velocity > M group by ts;

7、hive表中型別修改,有時候在建立hive表時為了方便,把所有的表字段都定義成string,但是calcite中對型別的限制比較嚴格,例如extract函式只能對date之類的型別進行操作,所以有時在定義cube之前需要對hive表中的欄位進行轉換,建立view就是一個很好的辦法。

8、複合資料型別處理,由於hive中可以定義複雜的資料型別,例如map、struct,而Kylin需要看到的是一個扁平的表結構,所以需要將複雜型別欄位進行拆分出維度和度量。

       在我們目前的實踐中,每一個cube依賴的事實表都是通過view建立的,這樣增加了一層對映,可以減小cube對原始表的依賴,提高靈活性,當然在查詢效能上並沒有找到相關的資料說明hive的view效能較差,在我們的實際使用過程中,並沒有發現使用view對Kylin構建cube過程中速度有明顯的影響。

使用檢視的限制

由於hive的限制使用view時需要注意的問題:
1、由於hive不能對view使用HCatalog獲取資料([https://issues.apache.org/jira/browse/HIVE-10851]),因此當你load一個view的時候Kylin計算表的cardinality的job通常會失敗([https://issues.apache.org/jira/browse/KYLIN-916]),這時就要求使用者知道每一列的cardinality大致的情況,如果實在不確定可以到hive裡面查詢一下(可以只查詢一段時間的資料進行估算)。

2、一般情況下,我們建立cube的時候會指定一個分割槽欄位(假設該欄位為day),Kylin利用這個欄位來增量計算每日的新資料,而對於需要增量計算的cube,在星狀模型中一定有一個表中存在日期欄位,一般為事實表,並且這個日期欄位是hive表的分割槽鍵之一,但是對於日誌資料在每一行資料中通常還會記錄下該日誌的時間(時間戳),一般是timestamp,如果此時需要時間作為維度,假設日期作為其中一個維度,那麼就需要建立view將表中時間戳轉換成日期,並將其作為維度,此時view已經存在了一個日期資料,但是建議使用者還是要將分割槽的時間欄位(day欄位)放入到定義的view中,並且將其作為cube的分割槽欄位,這樣可以大大加快第一步的執行速度!(掃一個分割槽和掃整個表)。
       那麼既然有了分割槽欄位表示日期是否只保留Hive的分割槽欄位而不轉換時間戳欄位了呢?不可以的,因為分割槽欄位只是標識了一個分割槽,它的值和時間戳中日值並不一定是相等的關係,雖然通常情況下是相同的,也不能保證某一個分割槽下的所有時間戳欄位的值都是這個日期的(在我們使用的hive表中的確存在這樣的情況)。例如在2016-01-15這個分割槽中可能前100條資料的時間戳是2014-01-14 23:59:59.xxx,所以你需要在view中保持這兩個欄位,一個作為度量(通過表中timestamp欄位轉換,參見場景5),一個作為cube的分割槽欄位。

       在我們的使用Kylin的過程中遇到了這些問題可以很好的通過Hive的檢視進行解決,當然還會有一些需求通過檢視能夠很好的實現而不再需要大動干戈的修改hive表或者Kylin的元資料,如果有什麼其他的場景還請多多補充。