关键字:
Hive, 万亿级, 大表, join, 联合查询
摘要:
随着大数据技术日趋成熟, 行业生态愈发完善, 腾讯云大数据团队服务的大客户越来越多. 在笔者服务的众多大客户之中, PB 级海量数据已经成为常态. 笔者负责大数据技术支持的某个腾讯云大数据项目, 单张数据表的行数超过万亿级, 数据量 PB 级, 而且还需要对万亿级数据表做表与表的多维分析. 比如本文介绍的故障排查过程, 客户提交的就是 "万亿级大表 join 普通表" 的海量数据关联多维分析任务. 这类任务, 如果不对大数据平台进行优化, 往往很容易运行失败, 而且排查过程异常艰难.
一, 故障现象
客户的离线海量数据分析任务, 底层使用 Hive 数仓进行存储, 并使用 hive sql 进行分析. 因为某些项目原因, hive sql 的执行引擎, 没有选择 spark 和 tez, 最终使用的是 mr. 客户在半个月前反馈一个故障信息: 他们有一类 sql 任务, 无论提交多少次, 都会 100% 失败; 其它类型的 sql 任务, 均能运行成功. 根据客户反馈的这个信息, 可以基本判断, 腾讯云大数据平台 TBDS 内部集成的 Hive 集群功能绝对是正常的, 只是这一类 sql 任务需要进行 "某方面的故障排查和优化".
二, 故障分析
2.1 sql 语句分析
既然是这一类任务均失败, 则我们只需要从这一类任务入手分析即可. 我们通过项目现场的驻场工程师拿到了 sql 语句, 如下所示:
- create table dbname.tablename row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compression'='snappy')
- as
- SELECT begintime, callduration, usernum, homearea, relatenum, relatehomeac, calltype, callflag, calldesc, imsi, imei, neid, curarea, routerout, routerin, lai, ci, origfileid, relatenumip, longitude, latitude, msid, ipaddress, nai, hamip, pdsnip, pcfip, bsid, esn, roamingid, phs_id, usernum_curarea, relate_lai, relate_curarea, accountno, origin_data, import_time, action_time
- FROM
- (
- SELECT * FROM
- (SELECT begintime, callduration, usernum, homearea, relatenum, relatehomeac, calltype, callflag, calldesc, imsi, imei, neid, curarea, routerout, routerin, lai, ci, origfileid, relatenumip, longitude, latitude, msid, ipaddress, nai, hamip, pdsnip, pcfip, bsid, esn, roamingid, phs_id, usernum_curarea, relate_lai, relate_curarea, accountno, origin_data, import_time, action_time FROM (select begintime,callduration,usernum,homearea,relatenum,relatehomeac,calltype,callflag,calldesc,imsi,imei,neid,curarea,routerout,routerin,lai,ci,origfileid,relatenumip,longitude,latitude,msid,ipaddress,nai,hamip,pdsnip,pcfip,bsid,esn,roamingid,phs_id,usernum_curarea,relate_lai,relate_curarea,accountno,origin_data,import_time,action_time from simba.dc_cdr) table_1) table_1
- INNER JOIN
- (SELECT field_3 FROM (select field_1,field_2,field_3,import_time from simba.t_res_20190226145527) table_2) table_2
- ON
- table_1.relatenum = table_2.field_3
- ) join_result;
稍微分析上面的 sql 语句, 可以知道这个任务主要对两张表做 join, 然后把 join 的结果存储到一张新表, 类似于:
create table t1 as select xxx from ( t2 join t3)
按照一般的情况, 这类任务也不复杂, 属于非常普通的 sql 任务, 为什么会 100% 失败呢?
我们通过客户那里了解到, sql 语句设计到的两张 Hive 表: simba.dc_cdr 是一张大表, 大概有 1.2 万亿行, 40 列; simba.t_res_20190226145527 属于普通表, 数据量很小, 只有几万行数据. 因此, 这类任务属于 "万亿级大表 join 普通表" 的海量数据关联多维分析任务.
得到这个信息之后, 我们大概知道排查的方向, 任务失败多半是因为 mapreduce 运行过程中, 因为某些原因使得部分 container 出现了 OutOfMomery(OOM), 这在大表做 join 的过程中属于常见的故障. 至于产生 OOM 的原因, 一般都是两类: 数据倾斜导致 shuffle 过程中部分 container 数据量过大, 超过 container 内存; 另一个原因就是配置的 mapper 和 reducer 内存太小. 顺着这个思路, 我们进行排查.
2.1.1 提高 mapper 和 reducer 的参数
我们让现场的同事自己做测试, 通过把 mapper 和 reducer 的参数从 4GB 提高到 16GB, 然后做测试. 5 个 sql 任务, 全部失败; mapper 和 reducer 的参数再次提高到 32GB, 依然全部失败. 内存参数不足导致的 OOM, 这个可能的原因被排除了. 当然为了保险起见, 我们还是建议客户至少设置 mapper 和 reducer 的参数不能低于 8GB, 防止其他 sql 可能出现内存不足导致的 OOM.
2.1.2 排查数据倾斜情况
我们通过排查 simba.dc_cdr 这张万亿级大表存储在 HDFS 内部的所有分区的数据量, 发现:
(1) 这张表每个分区的数据量非常均匀, 并不存在数据切斜的情况;
(2) 几个关键字段取值, 在 1.2 万亿行中, 也不存在明显的取值分布不均衡的情况. 因此, 数据倾斜这个因素也被排除了.
2.2 任务日志分析
我们通过分析 AM 和部分 container 的日志, 确实发现了部分 container 存在 OOM 情况. 但是, 这部分失败的任务都迁移到其它 container 执行, 而且成功. 因此, OOM 不是这类 "万亿级大表 join 普通表" 的海量数据关联多维分析失败的根本原因.
我们通过现场工程师将日志传递回公司, 经过日志分析, 发现了这类任务都有一个重要的特征: 任务在 yarn 上面运行的最终状态不是 FAILED, 全部都是 KILLED. 这类任务最终是被 "KILLED" 进而导致失败的. 非常诡异的现象呀!
失败的任务状态信息
我们通过分析几个任务的 AM 日志, 也得到以下信息:
AM 日志信息
AM 日志信息
- 2019-03-05 01:07:11,046 INFO [IPC Server handler 0 on 3860] org.apache.hadoop.mapreduce.v2.App.client.MRClientService:
- Kill job job_1551324751809_8924 received from simba (auth:TOKEN) at 115.15.3.200
2.3 应用程序排查
既然任务都是被 KILLED 导致的失败, 那么最大的可能就是被应用程序 KILLED 杀掉了. 我们通过与应用程序开发者详细讨论, 他们的确使用 yarn application -kill 命令去杀掉任务的情况, 但是仅限于以下两种情况:
(1) 任务运行的任务模型本身有误, 被甲方客户在应用系统界面点击了 "停止运行按钮", 然后应用系统下发了 yarn application -kill appid 命令杀死任务;
(2) 任务运行过程中出现问题, 比如无法获取任务的进度, 无法获取任务的状态等, 此时应用程序会下发 yarn application -kill appid 命令杀死任务.
我们在现场的同事自己做 sql 测试, 不会触发上述两种情况, 因此, 这一类任务被 "KILLED", 显然不是被应用程序, 也不是被人为下发 kill 命令的.
三, 最终确定故障原因
3.1 找出下发 kill 任务关键信息
既然不是应用程序下发的 killed 命令, 也不是人为下发的 kill 命令, 那么只可能是系统自己下发的 kill 命令. 我们通过排查 yarn,hive,mr 的源代码, 发现主动下发 kill application 命令的地方很多, 如果逐一赛选可能会耗时太久. 因此, 最方便的方法, 还是通过分析日志得到.
首先我们通过查看任务的 AM 日志, yarn resourcemanager 的日志. 对照日志分析, 应该不是由 yarn kill 的:
(1) 任务是 client 发起 kill 的, 打日志的地方都在 mr 的 client 代码里面;
(2)yarn 本身也无法获取到具体用户的认证信息, 所以无法以某个用户的身份 kill 任务, 如果 kill 的话只能以 yarn 用户身份. 因此, 确定 yarn 不会主动发起 kill 命令;
因此, 最可能发起主动 kill 任务命令的就是 hive 或者 mr. 为了得到 mr 和 hive 客户端的日志信息, 防止 hiveserver2 的干扰, 我们跳过 hiveserver2, 直接采用原生 hive shell 进行 sql 测试, 并且读取 hive shell 客户端的日志. 通过日志, 我们终于发现问题所在的关键点:
hive 客户端关键日志信息
[Fatal Error] total number of created files now is100004, which exceeds100000. Killing the job.
也就是说, hive sql 创建的文件数量运行到此时已经达到 100004, 超过 100000 个, 因此下发了 kill 任务的命令.
我们查看 hive 的源码, 确实也发现了这一段:
Hive 源码
这段源码在 Hive 的源码处:\apache-hive-2.2.0-src\ql\src\java\org\apache\hadoop\hive\ql\exec\mr\HadoopJobExecHelper.java, 第 316 到 320 行:
- if (fatal = checkFatalErrors(ctrs, errMsg)) {
- console.printError("[Fatal Error]" + errMsg.toString() + ". Killing the job.");
- rj.killJob();
- continue;
- }
因此, 我们对比日志信息和 hive 源码信息, 可以基本确定, 上述日志才是程序真正下发 kill 命令的根源.
3.2 错误的根源所在
那么怎么解释上述错误的原因呢? 这个错误的原因是因为 Hive 对创建文件的总数有限制 (hive.exec.max.created.files), 默认是 100000 个. 而客户运行的 SQL 任务,"万亿级大表 join 普通表" 的关联多维分析任务, 在 yarn 上面观察启动了 114486 个 mapper 任务, 0 个 reducer 任务:
map 任务超过 11 万个
因为每个 mapper 任务会在 HDFS 上面创建一个临时文件, 因此整个任务需要创建的临时文件也是 11 万多个, 超过了 hive.exec.max.created.files 默认设置的 100000 个, 因此才会触发 hive 源码里面的 kill 命令. 为了能够成功地运行上述的 SQL, 最简单的方法就是加大 hive.exec.max.created.files 参数的设置.
同时, 考虑到分区表的限制因此, 我们通过设置以下参数:
- hive.exec.dynamic.partition=true;
- hive.exec.max.dynamic.partitions=5000000;
- hive.exec.max.dynamic.partitions.pernode=500000;
- hive.exec.max.created.files=5000000;
将 hive.exec.max.created.files 提高到 500 万, 动态分区参数提高到 500 万 (单节点 50 万), 再次运行任务. 最终发现, 客户提交的就是 "万亿级大表 join 普通表" 的海量数据关联多维分析任务运行成功:
几个测试任务都运行成功
四, 总结
本次客户反馈的 "万亿级大表 join 普通表" 的海量数据关联多维分析任务运行失败故障, 也给腾讯云大数据技术支持工作足足上了一堂课: 对于真正的海量数据分析场景, 系统优化需要做到极致. 就拿本次 "万亿级大表 join 普通表" 的 hive sql 任务而言, 如此海量数据分析的任务, 使用 Hive 集群默认参数肯定是要吃亏的. Hadoop 的基础组件 HDFS,Yarn,mapreduce,Hive,HBase 等, 在海量数据场景下各种参数都是需要优化到极致的. 因此, 本次故障排查与优化过程, 确确实实让我们意识到, 在以后的腾讯云 toB 大数据项目技术支持过程中, 提前对超过 100 台服务器, 数据量超过百 TB(甚至 PB 级) 的大型集群进行各种性能优化.
当然, 除了大数据集群自身优化以外, 数据本身优化也挺重要. 比如对于 HDFS 而言, 严格控制小文件数量; 对于 Hive 而言, 做好分区以及数据切斜控制; 对于 HBase 而言, 设计好 regionserver 的 GC 机制等. 这些都是需要在部署完成大数据集群以后, 提前进行优化.
来源: https://www.qcloud.com/developer/article/1400833