数据仓库迁移中Hive遇到的一次坑
情形:比如用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时。
后果:处理此特殊值的reduce耗时;只有一个reduce任务。默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
解决方式:把数字类型转换成字符串类型
今天,由于公司要进行新老数据仓库的迁移,于是顺便接手了其中一部分的工作,其实大部分迁移工作都比较简单,就是把从ods层-dw层-dm层-sh展示层中涉及到旧仓库的表替换成新仓库的表,并且检查其中字段的差异性,并进行相应的操作。于是撸起袖子开工。
1. 前期的进展很顺利,很快就完成了几张表的迁移,心想着按这个进度,不到一个小时就能完成了。Orz
2. 在进行到第五张表的迁移时,发现很久没有完成,于是检查了日志,发现卡在了reduce阶段,从60%作用开始,基本上每隔10分钟左右进度才增加1%。
3. 遇到这种情况,第一反应就是发生了数据倾斜,于是马上进行数据源的排查,在这里先说明一下查询语句主体,大致如下select xxx from t1 left join on t2 where t1.sid = t2.sid left join t3 on t1.uid = t3.uid。其中t2和t3均已经进行了去重和选定分区的处理。
4. t1以及t2的数据量在百万级别,t3在亿级别作用。然后对t1进行了count(distinct uid)后,发现了异常数据 null的量在千万级别,于是采取了直接剔除null的解决方案,心想这个应该就不会进行数据倾斜了,于是再次运行脚本,然后等待,结果出乎我的意料,这次没有卡在60%,而是卡在了65%,泪崩啊。
5. 再次检查日志,发现reduce个数只有一个,心想大概是hive自动判断reduce个数不准确,于是手动强制设定了reduce的个数set mapred.reduce.tasks=800;。再一次运行,等待,60%--70%--80%--90%--95%...,这下终于正常了。...99%--99%--99%--99%...,在看到一连串的99%不断刷新的同时,我终于接受了还有错误的事实。
6. 到底哪里出错了呢,按说数据量也并不是特别大呀,想了想,还有一个办法,就是使用hive.groupby.skewindata=true;来进行当有数据倾斜时进行负载均衡,其实理性告诉我,和这个关系不大,但是没办法了呀,只能试一下了。结果不出我所料,还是卡在了99%。
7. 还有半小时就到饭点了,怎么办,这时候同事对我随口提了一句,检查一下数据类型,说不定是数据类型不一致造成的呢。"不应该呀",我嘀咕着,抱着死马当活马医的态度,我看了一些表结构,发现还真不一样,t1的是bigint,而t2和t3的是string。但是我记得hive有自动隐式转换机制的呀,为了验证我的观点,我进行了select cast(1010000001000390061 as bigint) = cast(1010000001000390061 as string)查询,结果就是返回true,应该不是这个原因。是的。
8. 但是也想不到别的办法了,于是再去吃饭之前,我还是提交了这个微小的改动,cast(t1.sid as string) = t2.sid,在吃饭的过程中,我一直在想解决的办法,吃完饭回来,奇迹发生了,居然就是这个原因!!!
9. 突然想起来,之前有过一次,好像也是数据类型的原因,在使用join时,对两个bigint类型和string类型进行等值操作时,乍一看,操作没问题,可是仔细一看,返回的数据不对啊。当时也没注意这个细节。
10. 难道是超出了bigint范围?在查询了select cast(10010000001000390061 as bigint)后,惊奇的发现居然返回了9223372036854775807,这下明白了,应该是在join时,由于t3的uid字段超出了bigint的范围,从而使得最后的连接后数据量剧增,想一下,百万 * 百万 * 亿 ,大概有10的20次方,难怪卡在了99%。
回顾这一次填坑之旅,不仅意识到了不能完全依赖于平台提供的便利功能,而疏忽了其背后原理性的东西。如自动转换类型的原理,以及相关的限制。
对于不同层的表的维度字段的定义,尽量保证维度字段的类型保持一致,不然有可能会出现一些奇怪的问题,比如我这次遇到的由于超出范围而造成自动转换的结果不准确的bug。
在遇到问题迟迟不能解决的时候,不妨问问身边的人,俗话说的好,当局者迷,旁观者清嘛。
来源:简书