多數(shù)介紹數(shù)據(jù)傾斜的文章都是以大篇幅的理論為主,并沒(méi)有給出具體的數(shù)據(jù)傾斜案例。當(dāng)工作中遇到了傾斜問(wèn)題,這些理論很難直接應(yīng)用,導(dǎo)致我們面對(duì)傾斜時(shí)還是不知所措。
今天我們不扯大篇理論,直接以例子來(lái)實(shí)踐,排查是否出現(xiàn)了數(shù)據(jù)傾斜,具體是哪段代碼導(dǎo)致的傾斜,怎么解決這段代碼的傾斜。
當(dāng)執(zhí)行過(guò)程中任務(wù)卡在 99%,大概率是出現(xiàn)了數(shù)據(jù)傾斜,但是通常我們的 SQL 很大,需要判斷出是哪段代碼導(dǎo)致的傾斜,才能利于我們解決傾斜。通過(guò)下面這個(gè)非常簡(jiǎn)單的例子來(lái)看下如何定位產(chǎn)生數(shù)據(jù)傾斜的代碼。
表結(jié)構(gòu)描述
先來(lái)了解下這些表中我們需要用的字段及數(shù)據(jù)量:
表的字段非常多,此處僅列出我們需要的字段
第一張表:user_info (用戶(hù)信息表,用戶(hù)粒度)
| 字段名 | 字段含義 | 字段描述 |
|---|---|---|
| userkey | 用戶(hù) key | 用戶(hù)標(biāo)識(shí) |
| idno | 用戶(hù)的身份證號(hào) | 用戶(hù)實(shí)名認(rèn)證時(shí)獲取 |
| phone | 用戶(hù)的手機(jī)號(hào) | 用戶(hù)注冊(cè)時(shí)的手機(jī)號(hào) |
| name | 用戶(hù)的姓名 | 用戶(hù)的姓名 |
user_info 表的數(shù)據(jù)量:1.02 億,大?。?3.9G,所占空間:41.7G(HDFS三副本)
第二張表:user_active (用戶(hù)活躍表,用戶(hù)粒度)
| 字段名 | 字段含義 | 字段描述 |
|---|---|---|
| userkey | 用戶(hù) key | 用戶(hù)沒(méi)有注冊(cè)會(huì)分配一個(gè) key |
| user_active_at | 用戶(hù)的最后活躍日期 | 從埋點(diǎn)日志表中獲取用戶(hù)的最后活躍日期 |
user_active 表的數(shù)據(jù)量:1.1 億
第三張表:user_intend(用戶(hù)意向表,此處只取近六個(gè)月的數(shù)據(jù),用戶(hù)粒度)
| 字段名 | 字段含義 | 字段描述 |
|---|---|---|
| phone | 用戶(hù)的手機(jī)號(hào) | 有意向的用戶(hù)必須是手機(jī)號(hào)注冊(cè)的用戶(hù) |
| intend_commodity | 用戶(hù)意向次數(shù)最多的商品 | 客戶(hù)對(duì)某件商品意向次數(shù)最多 |
| intend_rank | 用戶(hù)意向等級(jí) | 用戶(hù)的購(gòu)買(mǎi)意愿等級(jí),級(jí)數(shù)越高,意向越大 |
user_intend 表的數(shù)據(jù)量:800 萬(wàn)
第四張表:user_order(用戶(hù)訂單表,此處只取近六個(gè)月的訂單數(shù)據(jù),用戶(hù)粒度)
| 字段名 | 字段含義 | 字段描述 |
|---|---|---|
| idno | 用戶(hù)的身份證號(hào) | 下訂單的用戶(hù)都是實(shí)名認(rèn)證的 |
| order_num | 用戶(hù)的訂單次數(shù) | 用戶(hù)近六個(gè)月下單次數(shù) |
| order_amount | 用戶(hù)的訂單總金額 | 用戶(hù)近六個(gè)月下單總金額 |
user_order 表的數(shù)據(jù)量:640 萬(wàn)
1. 需求
需求非常簡(jiǎn)單,就是將以上四張表關(guān)聯(lián)組成一張大寬表,大寬表中包含用戶(hù)的基本信息,活躍情況,購(gòu)買(mǎi)意向及此用戶(hù)下訂單情況。
2. 代碼
根據(jù)以上需求,我們以 user_info 表為基礎(chǔ)表,將其余表關(guān)聯(lián)為一個(gè)寬表,代碼如下:
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
fromuser_infoa
leftjoinuser_activebona.userkey=b.userkey
leftjoinuser_intendcona.phone=c.phone
leftjoinuser_orderdona.idno=d.idno;
執(zhí)行上述語(yǔ)句,在執(zhí)行到某個(gè) job 時(shí)任務(wù)卡在 99%:

這時(shí)我們就應(yīng)該考慮出現(xiàn)數(shù)據(jù)傾斜了。其實(shí)還有一種情況可能是數(shù)據(jù)傾斜,就是任務(wù)超時(shí)被殺掉,Reduce 處理的數(shù)據(jù)量巨大,在做 full gc 的時(shí)候,stop the world。導(dǎo)致響應(yīng)超時(shí),超出默認(rèn)的 600 秒,任務(wù)被殺掉。報(bào)錯(cuò)信息一般如下:
AttemptID:attempt_1624419433039_1569885_r_000000 Timed outafter 600 secs Container killed by the ApplicationMaster. Container killed onrequest. Exit code is 143 Container exited with a non-zero exit code 143
3. 傾斜問(wèn)題排查
數(shù)據(jù)傾斜大多數(shù)都是大 key 問(wèn)題導(dǎo)致的。
如何判斷是大 key 導(dǎo)致的問(wèn)題,可以通過(guò)下面方法:
1. 通過(guò)時(shí)間判斷
如果某個(gè) reduce 的時(shí)間比其他 reduce 時(shí)間長(zhǎng)的多,如下圖,大部分 task 在 1 分鐘之內(nèi)完成,只有 r_000000 這個(gè) task 執(zhí)行 20 多分鐘了還沒(méi)完成。

注意:要排除兩種情況:
-
如果每個(gè) reduce 執(zhí)行時(shí)間差不多,都特別長(zhǎng),不一定是數(shù)據(jù)傾斜導(dǎo)致的,可能是 reduce 設(shè)置過(guò)少導(dǎo)致的。
-
有時(shí)候,某個(gè) task 執(zhí)行的節(jié)點(diǎn)可能有問(wèn)題,導(dǎo)致任務(wù)跑的特別慢。這個(gè)時(shí)候,mapreduce 的推測(cè)執(zhí)行,會(huì)重啟一個(gè)任務(wù)。如果新的任務(wù)在很短時(shí)間內(nèi)能完成,通常則是由于 task 執(zhí)行節(jié)點(diǎn)問(wèn)題導(dǎo)致的個(gè)別 task 慢。但是如果推測(cè)執(zhí)行后的 task 執(zhí)行任務(wù)也特別慢,那更說(shuō)明該 task 可能會(huì)有傾斜問(wèn)題。
2. 通過(guò)任務(wù) Counter 判斷
Counter 會(huì)記錄整個(gè) job 以及每個(gè) task 的統(tǒng)計(jì)信息。counter 的 url 一般類(lèi)似:
http://bd001:8088/proxy/application_1624419433039_1569885/mapreduce/singletaskcounter/task_1624419433039_1569885_r_000000/org.apache.hadoop.mapreduce.FileSystemCounter
通過(guò)輸入記錄數(shù),普通的 task counter 如下,輸入的記錄數(shù)是 13 億多:


而 task=000000 的 counter 如下,其輸入記錄數(shù)是 230 多億。是其他任務(wù)的 100 多倍:

4. 定位 SQL 代碼
1. 確定任務(wù)卡住的 stage
-
通過(guò) jobname 確定 stage:
一般 Hive 默認(rèn)的 jobname 名稱(chēng)會(huì)帶上 stage 階段,如下通過(guò) jobname 看到任務(wù)卡住的為 Stage-4:

-
如果 jobname 是自定義的,那可能沒(méi)法通過(guò) jobname 判斷 stage。需要借助于任務(wù)日志:
找到執(zhí)行特別慢的那個(gè) task,然后 Ctrl+F 搜索 “CommonJoinOperator: JOIN struct” 。Hive 在 join 的時(shí)候,會(huì)把 join 的 key 打印到日志中。如下:

上圖中的關(guān)鍵信息是:struct<_col0:string, _col1:string, _col3:string>
這時(shí)候,需要參考該 SQL 的執(zhí)行計(jì)劃。通過(guò)參考執(zhí)行計(jì)劃,可以斷定該階段為 Stage-4 階段:

2. 確定 SQL 執(zhí)行代碼
確定了執(zhí)行階段,即 stage。通過(guò)執(zhí)行計(jì)劃,則可以判斷出是執(zhí)行哪段代碼時(shí)出現(xiàn)了傾斜。還是從此圖,這個(gè) stage 中進(jìn)行連接操作的表別名是 d:

就可以推測(cè)出是在執(zhí)行下面紅框中代碼時(shí)出現(xiàn)了數(shù)據(jù)傾斜,因?yàn)檫@行的表的別名是 d:

5. 解決傾斜
我們知道了哪段代碼引起的數(shù)據(jù)傾斜,就針對(duì)這段代碼查看傾斜原因,看下這段代碼的表中數(shù)據(jù)是否有異常。
傾斜原因:
本文的示例數(shù)據(jù)中 user_info 和 user_order 通過(guò)身份證號(hào)關(guān)聯(lián),檢查發(fā)現(xiàn) user_info 表中身份證號(hào)為空的有 7000 多萬(wàn),原因就是這 7000 多萬(wàn)數(shù)據(jù)都分配到一個(gè) reduce 去執(zhí)行,導(dǎo)致數(shù)據(jù)傾斜。
解決方法:
- 可以先把身份證號(hào)為空的去除之后再關(guān)聯(lián),最后按照 userkey 連接,因?yàn)?userkey 全部都是有值的:
witht1as(
select
u.userkey,
o.*
fromuser_infou
leftjoinuser_ordero
onu.idno=o.idno
whereu.idnoisnotnull
--是可以把where條件寫(xiě)在后面的,hive會(huì)進(jìn)行謂詞下推,先執(zhí)行where條件在執(zhí)行l(wèi)eftjoin
)
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
fromuser_infoa
leftjoinuser_activebona.userkey=b.userkey
leftjoinuser_intendcona.phone=c.phone
leftjoint1dona.userkey=d.userkey;
- 也可以這樣,給身份證為空的數(shù)據(jù)賦個(gè)隨機(jī)值,但是要注意隨機(jī)值不能和表中的身份證號(hào)有重復(fù):
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
fromuser_infoa
leftjoinuser_activebona.userkey=b.userkey
leftjoinuser_intendcona.phone=c.phone
leftjoinuser_orderdonnvl(a.idno,concat(rand(),'idnumber'))=d.idno;
其他的解決數(shù)據(jù)傾斜的方法:
1. 過(guò)濾掉臟數(shù)據(jù)
如果大 key 是無(wú)意義的臟數(shù)據(jù),直接過(guò)濾掉。本場(chǎng)景中大 key 有實(shí)際意義,不能直接過(guò)濾掉。
2. 數(shù)據(jù)預(yù)處理
數(shù)據(jù)做一下預(yù)處理(如上面例子,對(duì) null 值賦一個(gè)隨機(jī)值),盡量保證 join 的時(shí)候,同一個(gè) key 對(duì)應(yīng)的記錄不要有太多。
3. 增加 reduce 個(gè)數(shù)
如果數(shù)據(jù)中出現(xiàn)了多個(gè)大 key,增加 reduce 個(gè)數(shù),可以讓這些大 key 落到同一個(gè) reduce 的概率小很多。
配置 reduce 個(gè)數(shù):
setmapred.reduce.tasks=15;
4. 轉(zhuǎn)換為 mapjoin
如果兩個(gè)表 join 的時(shí)候,一個(gè)表為小表,可以用 mapjoin 做。
配置 mapjoin:
sethive.auto.convert.join=true;是否開(kāi)啟自動(dòng)mapjoin,默認(rèn)是true
sethive.mapjoin.smalltable.filesize=100000000;mapjoin的表size大小
5. 啟用傾斜連接優(yōu)化
hive 中可以設(shè)置 hive.optimize.skewjoin 將一個(gè) join sql 分為兩個(gè) job。同時(shí)可以設(shè)置下 hive.skewjoin.key,此參數(shù)表示 join 連接的 key 的行數(shù)超過(guò)指定的行數(shù),就認(rèn)為該鍵是偏斜連接鍵,就對(duì) join 啟用傾斜連接優(yōu)化。默認(rèn) key 的行數(shù)是 100000。
配置傾斜連接優(yōu)化:
sethive.optimize.skewjoin=true;啟用傾斜連接優(yōu)化
sethive.skewjoin.key=200000;超過(guò)20萬(wàn)行就認(rèn)為該鍵是偏斜連接鍵
6. 調(diào)整內(nèi)存設(shè)置
適用于那些由于內(nèi)存超限任務(wù)被 kill 掉的場(chǎng)景。通過(guò)加大內(nèi)存起碼能讓任務(wù)跑起來(lái),不至于被殺掉。該參數(shù)不一定會(huì)明顯降低任務(wù)執(zhí)行時(shí)間。
配置內(nèi)存:
setmapreduce.reduce.memory.mb=5120;設(shè)置reduce內(nèi)存大小
setmapreduce.reduce.java.opts=-Xmx5000m-XX:MaxPermSize=128m;
附:Hive 配置屬性官方鏈接:https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7297瀏覽量
93522 -
SQL
+關(guān)注
關(guān)注
1文章
789瀏覽量
46110 -
代碼
+關(guān)注
關(guān)注
30文章
4924瀏覽量
72419 -
hive
+關(guān)注
關(guān)注
0文章
12瀏覽量
4077
原文標(biāo)題:實(shí)操 : Hive 數(shù)據(jù)傾斜問(wèn)題定位排查及解決
文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
鄂電故障定位超聲傳感器如何實(shí)現(xiàn)快速定位與排查
Web頁(yè)面白屏問(wèn)題的排查步驟和解決方法
傾斜儀測(cè)量數(shù)據(jù)的讀取與分析指南
VirtualLab 應(yīng)用:傾斜光柵的參數(shù)優(yōu)化及公差分析
分布式存儲(chǔ)數(shù)據(jù)恢復(fù)—虛擬機(jī)上hbase和hive數(shù)據(jù)庫(kù)數(shù)據(jù)恢復(fù)案例
使用福祿克示波表排查電氣故障的案例分析
5G網(wǎng)絡(luò)優(yōu)化中,信令測(cè)試儀如何幫助故障排查?
CAN總線故障排查:從問(wèn)題到解決的實(shí)戰(zhàn)案例
GPS無(wú)法定位?一定要排查的情況來(lái)啦~
數(shù)據(jù)采集網(wǎng)關(guān)出現(xiàn)斷網(wǎng)可能是什么原因?如何排查?
機(jī)智云歷史數(shù)據(jù)導(dǎo)出與排查指南
輸電線路桿塔傾斜在線監(jiān)測(cè)裝置,桿塔傾斜預(yù)警大師
北斗輸電線路桿塔傾斜在線監(jiān)測(cè)裝置 高精度差分定位 雙天線
電桿傾斜監(jiān)測(cè)裝置 桿塔傾斜監(jiān)測(cè)裝置 支持數(shù)據(jù)實(shí)時(shí)讀取 精確預(yù)警

簡(jiǎn)述Hive 數(shù)據(jù)傾斜問(wèn)題定位排查及解決
評(píng)論