#.參考鏈接
refer: https://kylin.apache.org/docs20/howto/howto_optimize_build.html
refer:?https://kylin.apache.org/cn/docs/howto/howto_optimize_build.html
refer:?https://kylin.apache.org/cn/docs/tutorial/cube_build_performance.html
1.創(chuàng)建Hive的中間平表
這一步將數(shù)據(jù)從源Hive表提取出來(lái)(和所有join的表一起)并插入到一個(gè)中間平表。如果Cube是分區(qū)的,Kylin會(huì)加上一個(gè)時(shí)間條件以確保只有在時(shí)間范圍內(nèi)的數(shù)據(jù)才會(huì)被提取。你可以在這個(gè)步驟的log查看相關(guān)的Hive命令,比如:
hive -e "USE default;
DROP TABLE IF EXISTS kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34;
CREATE EXTERNAL TABLE IF NOT EXISTS kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34
(AIRLINE_FLIGHTDATE date,AIRLINE_YEAR int,AIRLINE_QUARTER int,...,AIRLINE_ARRDELAYMINUTES int)
STORED AS SEQUENCEFILE
LOCATION 'hdfs:///kylin/kylin200instance/kylin-0a8d71e8-df77-495f-b501-03c06f785b6c/kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34';
SET dfs.replication=2;
SET hive.exec.compress.output=true;
SET hive.auto.convert.join.noconditionaltask=true;
SET hive.auto.convert.join.noconditionaltask.size=100000000;
SET mapreduce.job.split.metainfo.maxsize=-1;
INSERT OVERWRITE TABLE kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34 SELECT
AIRLINE.FLIGHTDATE
,AIRLINE.YEAR
,AIRLINE.QUARTER
,...
,AIRLINE.ARRDELAYMINUTES
FROM AIRLINE.AIRLINE as AIRLINE
WHERE (AIRLINE.FLIGHTDATE >= '1987-10-01' AND AIRLINE.FLIGHTDATE < '2017-01-01');
在Hive命令運(yùn)行時(shí),Kylin會(huì)用conf/kylin_hive_conf.properties里的配置,比如保留更少的冗余備份和啟用Hive的mapper side join。需要的話可以根據(jù)集群的具體情況增加其他配置。
如果cube的分區(qū)列(在這個(gè)案例中是”FIGHTDATE”)與Hive表的分區(qū)列相同,那么根據(jù)它過(guò)濾數(shù)據(jù)能讓Hive聰明地跳過(guò)不匹配的分區(qū)。因此強(qiáng)烈建議用Hive的分區(qū)列(如果它是日期列)作為cube的分區(qū)列。這對(duì)于那些數(shù)據(jù)量很大的表來(lái)說(shuō)幾乎是必須的,否則Hive不得不每次在這步掃描全部文件,消耗非常長(zhǎng)的時(shí)間。
如果啟用了Hive的文件合并,你可以在conf/kylin_hive_conf.xml里關(guān)閉它,因?yàn)镵ylin有自己合并文件的方法(下一節(jié)):
<property>
? ? <name>hive.merge.mapfiles</name>
? ? <value>false</value>
? ? <description>Disable Hive's auto merge</description>
</property>
2.重新分發(fā)中間表
在之前的一步之后,Hive在HDFS上的目錄里生成了數(shù)據(jù)文件:有些是大文件,有些是小文件甚至空文件。這種不平衡的文件分布會(huì)導(dǎo)致之后的MR任務(wù)出現(xiàn)數(shù)據(jù)傾斜的問(wèn)題:有些mapper完成得很快,但其他的就很慢。針對(duì)這個(gè)問(wèn)題,Kylin增加了這一個(gè)步驟來(lái)“重新分發(fā)”數(shù)據(jù),這是示例輸出:
total input rows = 159869711
expected input rows per mapper = 1000000
num reducers for RedistributeFlatHiveTableStep = 160
重新分發(fā)表的命令:
hive -e "USE default;
SET dfs.replication=2;
SET hive.exec.compress.output=true;
SET hive.auto.convert.join.noconditionaltask=true;
SET hive.auto.convert.join.noconditionaltask.size=100000000;
SET mapreduce.job.split.metainfo.maxsize=-1;
set mapreduce.job.reduces=160;
set hive.merge.mapredfiles=false;
INSERT OVERWRITE TABLE kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34 SELECT * FROM kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34 DISTRIBUTE BY RAND();
"
首先,Kylin計(jì)算出中間表的行數(shù),然后基于行數(shù)的大小算出重新分發(fā)數(shù)據(jù)需要的文件數(shù)。默認(rèn)情況下,Kylin為每一百萬(wàn)行分配一個(gè)文件。在這個(gè)例子中,有1.6億行和160個(gè)reducer,每個(gè)reducer會(huì)寫一個(gè)文件。在接下來(lái)對(duì)這張表進(jìn)行的MR步驟里,Hadoop會(huì)啟動(dòng)和文件相同數(shù)量的mapper來(lái)處理數(shù)據(jù)(通常一百萬(wàn)行數(shù)據(jù)比一個(gè)HDFS數(shù)據(jù)塊要小)。如果你的日常數(shù)據(jù)量沒(méi)有這么大或者Hadoop集群有足夠的資源,你或許想要更多的并發(fā)數(shù),這時(shí)可以將conf/kylin.properties里的kylin.job.mapreduce.mapper.input.rows設(shè)為小一點(diǎn)的數(shù)值,比如:
kylin.job.mapreduce.mapper.input.rows=500000
其次,Kylin會(huì)運(yùn)行?“INSERT OVERWRITE TABLE … DISTRIBUTE BY “?形式的HiveQL來(lái)分發(fā)數(shù)據(jù)到指定數(shù)量的reducer上。
在很多情況下,Kylin請(qǐng)求Hive隨機(jī)分發(fā)數(shù)據(jù)到reducer,然后得到大小相近的文件,分發(fā)的語(yǔ)句是”DISTRIBUTE BY RAND()”。
如果你的cube指定了一個(gè)高基數(shù)的列,比如”USER_ID”,作為”分片”維度(在cube的“高級(jí)設(shè)置”頁(yè)面),Kylin會(huì)讓Hive根據(jù)該列的值重新分發(fā)數(shù)據(jù),那么在該列有著相同值的行將被分發(fā)到同一個(gè)文件。這比隨機(jī)要分發(fā)要好得多,因?yàn)椴粌H重新分布了數(shù)據(jù),并且在沒(méi)有額外代價(jià)的情況下對(duì)數(shù)據(jù)進(jìn)行了預(yù)先分類,如此一來(lái)接下來(lái)的cube build處理會(huì)從中受益。在典型的場(chǎng)景下,這樣優(yōu)化可以減少40%的build時(shí)長(zhǎng)。在這個(gè)案例中分發(fā)的語(yǔ)句是”DISTRIBUTE BY USER_ID”:
請(qǐng)注意: 1)“分片”列應(yīng)該是高基數(shù)的維度列,并且它會(huì)出現(xiàn)在很多的cuboid中(不只是出現(xiàn)在少數(shù)的cuboid)。 使用它來(lái)合理進(jìn)行分發(fā)可以在每個(gè)時(shí)間范圍內(nèi)的數(shù)據(jù)均勻分布,否則會(huì)造成數(shù)據(jù)傾斜,從而降低build效率。典型的正面例子是:“USER_ID”、“SELLER_ID”、“PRODUCT”、“CELL_NUMBER”等等,這些列的基數(shù)應(yīng)該大于一千(遠(yuǎn)大于reducer的數(shù)量)。 2)”分片”對(duì)cube的存儲(chǔ)同樣有好處,不過(guò)這超出了本文的范圍。
3.提取事實(shí)表的唯一列
在這一步驟Kylin運(yùn)行MR任務(wù)來(lái)提取使用字典編碼的維度列的唯一值。
實(shí)際上這步另外還做了一些事情:通過(guò)HyperLogLog計(jì)數(shù)器收集cube的統(tǒng)計(jì)數(shù)據(jù),用于估算每個(gè)cuboid的行數(shù)。如果你發(fā)現(xiàn)mapper運(yùn)行得很慢,這通常表明cube的設(shè)計(jì)太過(guò)復(fù)雜,請(qǐng)參考
優(yōu)化cube設(shè)計(jì)來(lái)簡(jiǎn)化cube。如果reducer出現(xiàn)了內(nèi)存溢出錯(cuò)誤,這表明cuboid組合真的太多了或者是YARN的內(nèi)存分配滿足不了需要。如果這一步從任何意義上講不能在合理的時(shí)間內(nèi)完成,你可以放棄任務(wù)并考慮重新設(shè)計(jì)cube,因?yàn)槔^續(xù)下去會(huì)花費(fèi)更長(zhǎng)的時(shí)間。
你可以通過(guò)降低取樣的比例(kylin.job.cubing.inmen.sampling.percent)來(lái)加速這個(gè)步驟,但是幫助可能不大而且影響了cube統(tǒng)計(jì)數(shù)據(jù)的準(zhǔn)確性,所有我們并不推薦。
4.構(gòu)建維度字典
有了前一步提取的維度列唯一值,Kylin會(huì)在內(nèi)存里構(gòu)建字典(在下個(gè)版本將改為MapReduce任務(wù))。通常這一步比較快,但如果唯一值集合很大,Kylin可能會(huì)報(bào)出類似“字典不支持過(guò)高基數(shù)”。對(duì)于UHC類型的列,請(qǐng)使用其他編碼方式,比如“fixed_length”、“integer”等等。
5.保存cuboid的統(tǒng)計(jì)數(shù)據(jù)和創(chuàng)建 HTable
這兩步是輕量級(jí)和快速的。
6.構(gòu)建基礎(chǔ)cuboid
這一步用Hive的中間表構(gòu)建基礎(chǔ)的cuboid,是“逐層”構(gòu)建cube算法的第一輪MR計(jì)算。Mapper的數(shù)目與第二步的reducer數(shù)目相等;Reducer的數(shù)目是根據(jù)cube統(tǒng)計(jì)數(shù)據(jù)估算的:默認(rèn)情況下每500MB輸出使用一個(gè)reducer;如果觀察到reducer的數(shù)量較少,你可以將kylin.properties里的“kylin.job.mapreduce.default.reduce.input.mb”設(shè)為小一點(diǎn)的數(shù)值以獲得過(guò)多的資源,比如:
kylin.job.mapreduce.default.reduce.input.mb=200
7.構(gòu)建N維cuboid
這些步驟是“逐層”構(gòu)建cube的過(guò)程,每一步以前一步的輸出作為輸入,然后去掉一個(gè)維度以聚合得到一個(gè)子cuboid。舉個(gè)例子,cuboid ABCD去掉A得到BCD,去掉B得到ACD。
有些cuboid可以從一個(gè)以上的父cuboid聚合得到,這種情況下,Kylin會(huì)選擇最小的一個(gè)父cuboid。舉例,AB可以從ABC(id:1110)和ABD(id:1101)生成,則ABD會(huì)被選中,因?yàn)樗谋華BC要小。在這基礎(chǔ)上,如果D的基數(shù)較小,聚合運(yùn)算的成本就會(huì)比較低。所以,當(dāng)設(shè)計(jì)rowkey序列的時(shí)候,請(qǐng)記得將基數(shù)較小的維度放在末尾。這樣不僅有利于cube構(gòu)建,而且有助于cube查詢,因?yàn)轭A(yù)聚合也遵循相同的規(guī)則。
通常來(lái)說(shuō),從N維到(N/2)維的構(gòu)建比較慢,因?yàn)檫@是cuboid數(shù)量爆炸性增長(zhǎng)的階段:N維有1個(gè)cuboid,(N-1)維有N個(gè)cuboid,(N-2)維有N*(N-1)個(gè)cuboid,以此類推。經(jīng)過(guò)(N/2)維構(gòu)建的步驟,整個(gè)構(gòu)建任務(wù)會(huì)逐漸變快。
8.構(gòu)建cube
這個(gè)步驟使用一個(gè)新的算法來(lái)構(gòu)建cube:“逐片”構(gòu)建(也稱為“內(nèi)存”構(gòu)建)。它會(huì)使用一輪MR來(lái)計(jì)算所有的cuboids,但是比通常情況下更耗內(nèi)存。配置文件”conf/kylin_job_inmem.xml”正是為這步而設(shè)。默認(rèn)情況下它為每個(gè)mapper申請(qǐng)3GB內(nèi)存。如果你的集群有充足的內(nèi)存,你可以在上述配置文件中分配更多內(nèi)存給mapper,這樣它會(huì)用盡可能多的內(nèi)存來(lái)緩存數(shù)據(jù)以獲得更好的性能,比如:
<property>
? ? <name>mapreduce.map.memory.mb</name>
? ? <value>6144</value>
? ? <description></description>
</property>
<property>
? ? <name>mapreduce.map.java.opts</name>
? ? <value>-Xmx5632m</value>
? ? <description></description>
</property>
請(qǐng)注意,Kylin會(huì)根據(jù)數(shù)據(jù)分布(從cube的統(tǒng)計(jì)數(shù)據(jù)里獲得)自動(dòng)選擇最優(yōu)的算法,沒(méi)有被選中的算法對(duì)應(yīng)的步驟會(huì)被跳過(guò)。你不需要顯式地選擇構(gòu)建算法。
9.將cuboid數(shù)據(jù)轉(zhuǎn)換為HFile
這一步啟動(dòng)一個(gè)MR任務(wù)來(lái)講cuboid文件(序列文件格式)轉(zhuǎn)換為HBase的HFile格式。Kylin通過(guò)cube統(tǒng)計(jì)數(shù)據(jù)計(jì)算HBase的region數(shù)目,默認(rèn)情況下每5GB數(shù)據(jù)對(duì)應(yīng)一個(gè)region。Region越多,MR使用的reducer也會(huì)越多。如果你觀察到reducer數(shù)目較小且性能較差,你可以將“conf/kylin.properties”里的以下參數(shù)設(shè)小一點(diǎn),比如:
kylin.hbase.region.cut=2
kylin.hbase.hfile.size.gb=1
如果你不確定一個(gè)region應(yīng)該是多大時(shí),聯(lián)系你的HBase管理員。
10.將HFile導(dǎo)入HBase表
這一步使用HBase API來(lái)講HFile導(dǎo)入region server,這是輕量級(jí)并快速的一步。
11.更新cube信息
在導(dǎo)入數(shù)據(jù)到HBase后,Kylin在元數(shù)據(jù)中將對(duì)應(yīng)的cube segment標(biāo)記為ready。
12.清理資源
將中間寬表從Hive刪除。這一步不會(huì)阻塞任何操作,因?yàn)樵谇耙徊絪egment已經(jīng)被標(biāo)記為ready。如果這一步發(fā)生錯(cuò)誤,不用擔(dān)心,垃圾回收工作可以晚些再通過(guò)Kylin的StorageCleanupJob完成。
#
refer:https://kylin.apache.org/docs20/howto/howto_optimize_build.html
refer:https://kylin.apache.org/docs20/howto/howto_optimize_build.html