我們知道,大部分spark計(jì)算都是在內(nèi)存中完成的,所以spark的瓶頸一般來自于集群(standalone, yarn, mesos, k8s)的資源緊張,cpu,網(wǎng)絡(luò)帶寬,內(nèi)存。spark的性能,想要它快,就得充分利用好系統(tǒng)資源,尤其是內(nèi)存和cpu。有時(shí)候我們也需要做一些優(yōu)化調(diào)整來減少內(nèi)存占用,例如將小文件進(jìn)行合并的操作。
一、問題現(xiàn)象
我們有一個(gè)15萬條總數(shù)據(jù)量133mb的表,使用select * from bi.dwd_tbl_conf_info全表查詢耗時(shí)3min,另外一個(gè)500萬條總數(shù)據(jù)量6.3g的表ods_tbl_conf_detail,查詢耗時(shí)23秒。兩張表均為列式存儲(chǔ)的表。
大表查詢快,而小表反而查詢慢了,為什么會(huì)產(chǎn)生如此奇怪的現(xiàn)象呢?
二、問題探詢
數(shù)據(jù)量6.3g的表查詢耗時(shí)23秒,反而數(shù)據(jù)量133mb的小表查詢耗時(shí)3min,這非常奇怪。我們收集了對(duì)應(yīng)的建表語句,發(fā)現(xiàn)兩者沒有太大的差異,大部分為string,兩表的列數(shù)也相差不大。
createtableifnotexists`bi`.`dwd_tbl_conf_info`(`corp_id`stringcomment\\\’\\\’,`dept_uuid`stringcomment\\\’\\\’,`user_id`stringcomment\\\’\\\’,`user_name`stringcomment\\\’\\\’,`uuid`stringcomment\\\’\\\’,`dtime`datecomment\\\’\\\’,`slice_number`intcomment\\\’\\\’,`attendee_count`intcomment\\\’\\\’,`mr_id`stringcomment\\\’\\\’,`mr_pkg_id`stringcomment\\\’\\\’,`mr_parties`intcomment\\\’\\\’,`is_mr`tinyintcomment\\\’r\\\’,`is_live_conf`tinyintcomment\\\’\\\’) createtableifnotexists`bi`.`ods_tbl_conf_detail`(`id`string,`conf_uuid`string,`conf_id`string,`name`string,`number`string,`device_type`string,`j_time`bigint,`l_time`bigint,`media_type`string,`dept_name`string,`updatetime`bigint,`createtime`bigint,`user_id`string,`useragent`string,`corp_id`string,`account`string)
因?yàn)閮蓮埍砭鶠楹芎?jiǎn)單的select查詢操作,無任何復(fù)雜的聚合join操作,也無udf相關(guān)的操作,所以基本確認(rèn)查詢慢的應(yīng)該發(fā)生的讀表的時(shí)候,我們將懷疑的點(diǎn)放到了讀表操作上。通過查詢兩個(gè)查詢語句的dag和任務(wù)分布,我們發(fā)現(xiàn)了不一樣的地方。
查詢快的表,查詢時(shí)總共有68個(gè)任務(wù),任務(wù)分配比如均勻,平均7~9s左右,而查詢慢的表,查詢時(shí)總共1160個(gè)任務(wù),平均也是9s左右。如下圖所示:
至此,我們基本發(fā)現(xiàn)了貓膩所在。大表6.3g但文件個(gè)數(shù)小,只有68個(gè),所以很快跑完了。而小表雖然只有133mb,但文件個(gè)數(shù)特別多,導(dǎo)致產(chǎn)生的任務(wù)特別多,而由于單個(gè)任務(wù)本身比較快,大部分時(shí)間花費(fèi)在任務(wù)調(diào)度上,導(dǎo)致任務(wù)耗時(shí)較長(zhǎng)。
那如何才能解決小表查詢慢的問題呢?
三、業(yè)務(wù)調(diào)優(yōu)
那現(xiàn)在擺在我們面前就存在現(xiàn)在問題:
為什么小表會(huì)產(chǎn)生這么小文件 已經(jīng)產(chǎn)生的這么小文件如何合并
帶著這兩個(gè)問題,我們和業(yè)務(wù)的開發(fā)人員聊了一個(gè)發(fā)現(xiàn)小表是業(yè)務(wù)開發(fā)人員從原始數(shù)據(jù)表中,按照不同的時(shí)間切片查詢并做數(shù)據(jù)清洗后插入到小表中的,而由于時(shí)間切片切的比較小,導(dǎo)致這樣的插入次數(shù)特別多,從而產(chǎn)生了大量的小文件。
那么我們需要解決的問題就是2個(gè),如何才能把這些歷史的小文件進(jìn)行合并以及如何才能保證后續(xù)的業(yè)務(wù)流程中不再產(chǎn)生小文件,我們指導(dǎo)業(yè)務(wù)開發(fā)人員做了以下優(yōu)化:
使用insert overwrite bi.dwd_tbl_conf_info select * from bi.dwd_tbl_conf_info合并下歷史的數(shù)據(jù)。由于dli做了數(shù)據(jù)一致性保護(hù),overwrite期間不影響原有數(shù)據(jù)的讀取和查詢,overwrite之后就會(huì)使用新的合并后的數(shù)據(jù)。合并后全表查詢由原來的3min縮短到9s內(nèi)完成。 原有表修改為分區(qū)表,插入時(shí)不同時(shí)間放入到不同分區(qū),查詢時(shí)只查詢需要的時(shí)間段內(nèi)的分區(qū)數(shù)據(jù),進(jìn)一步減小讀取數(shù)據(jù)量。