1.left join后少数据怎么办?
问题:sqL里互联网门诊科室字段有很多空白数据
分析:首先先看这个数据来源于哪个表,然后根据其left join的相关表一层层往上推,找最上面两个表join的原因,如果找不到再往下找
原因:为什么要这么做,因为某表缺数据,说明前面的表left Join的时候可能没join上,导致后面一层层都缺数据
操作:我们通过查询发现确实是最前面俩表join的时候,右表经常Join不上,然后我们查一下能join上的左表的最大ctime
select
max(e.ctime)
from dwb.healthpal_avatar_healthservicerecords e
LEFT JOIN dwb.onlinehospital_avatar_totalorderitems ti ON ti.orderid=e.providerid AND ti.ordertype=e.providerType
where ti.ordertype is not null
查询到最大的ctime是2022-04-27 01:03:20
然后我们卡住这个ctime,验证一下是不是从这天以后右表就一直join不上了,join不上的时候左表的链接字段分别是什么
select
e.ctime,
e.providerid,
e.providerType,
ti.ordertype,
ti.orderid
from dwb.healthpal_avatar_healthservicerecords e
LEFT JOIN dwb.onlinehospital_avatar_totalorderitems ti ON ti.orderid=e.providerid AND ti.ordertype=e.providerType
where e.ctime > '2022-04-27'
发现此时左表的Porvideid全是0,这是导致右表Join不上的根本原因
那为什么会这样?是这个字段废弃不用了吗?那现在用的是哪个字段呢?
select providertype,count(1),min(ctime),max(ctime) from dwb.healthpal_avatar_healthservicerecords group by providertype;
select sourcetype,count(1),min(ctime),max(ctime) from dwb.healthpal_avatar_healthservicerecords group by sourcetype;
查询到确实从4月27开始我们拿到得类型都为空,说明换字段了,我们猜测一下是否换了sourcetype字段
同样方法查询可知确实sourcetype的BasicOrder的数量最多,且延续至今,因此用的应该是该字段
2.left join少数据一定是表字段废弃吗?
说明:left join少数据并不一定是表字段废弃关联未成功导致,也可能是我们过滤的问题,不妨将过滤条件放开试试
问题:团队工作量中,多行只有baseteamid却缺少用户名等字段
方法:还是老方法,看缺少的这个字段来源于哪个表,他是和谁join,然后找出相应的表排查原因
原因:1. 有些医生注销了,但有其工作量数据,我们不应该过滤掉is_open不为identical的数据
2.机构的工作量不应该存在于内,而机构在tab_space表,在spase_user和spase_user_predoctor表均未收录,所以left join时无法将其取出
解决方式:1.取消过滤条件where is_open = 'identical'
2.我们可以直接过滤掉space_userid为空解决,或额外取出院队名称做为标注也可解决以后的麻烦 where space_user_id is not null group by baseteamid,dth.name
3.关于from_unixtime和get_json_object函数
from_unixtime(get_json_object(get_json_object(content,'$.data'),'$.fld_SpaceHitsCreateTime'),'yyyy-MM-dd')dt解析出来的为字符串,但在Hive里严格要求该参数为int,但为什么这里不强转也可以?
* @param ut A number of a type that is castable to a long, such as string or integer. Can be
* negative for timestamps before the unix epoch
* @return A string, or null if the input was a string that could not be cast to a long
* @group datetime_funcs
* @since 1.5.0
*/
def from_unixtime(ut: Column): Column = withExpr {
FromUnixTime(ut.expr, Literal("yyyy-MM-dd HH:mm:ss"))
}
源码里给了我们想要的答案,其实第一个字段为column类型,他会在底层先转为string,然后再转为long类型统一处理,
所以即使你这里强转为了Int,他还是会重新转为string,所以外部没必要加强转,而后面的格式字段是严格要求为string类型的,当然也可以不传值,底层会给一个默认值
什么是column类型:Column 表示了 Dataset 中的一个列, 并且可以持有一个表达式, 这个表达式作用于每一条数据, 对每条数据都生成一个值
那为什么我的分区字段传错了,表里没有这个字段,只有分区分到了hive默认分区,但是程序却不报错?
因为是get_json_object方法,如果没有那个字段,就返回null,然后在调用null的该方法当然还是null,所以除了content不能写错以外,其他的data和后面的time写错了都不会报错,结果为null,
到了底层的FromUnixTime,接收一个Null,直接返回Null,结果分区字段就是null值,自然分到默认分区了
4.合并任务失败
首次执行:
Caused by: java.net.SocketTimeoutException: Readtimed out
原因:由于这是一个新的分区表,一边写分区文件一边创建新的分区,就会导致一个问题,对于spark引擎,创建新分区数量过多,就会读写超时,大约上限是1000个分区
这里要分几种情况,视情况而定如何解决:
分区多但文件小:使用MR引擎解决,MR不在乎创建多少个分区,也不在乎map数量多与少,但他怕一件事,对reduce端有内存限制40G,由于我们想要的是一个分区下就一个文件,因此只启动一个reduce的情况下, 文件小适用,而如果数据量大则会reduce内存不够;当数据量大时,其实也可以采用该种方式解决,启动多个reduce,但需要最后对reduce端输出的小文件进行合并,也就是单独启动一个reduce任务进行合并处理,但这种方式,仍存在一定局限性,假如数据量就是太大,最后一个reduce内存不够合并也失败呢?而且由于是MR引擎,不会像spark一样直接reduce后立马接一个reduce进行合并,它会再发给map,然后再给reduce,而数据量又大,大量的磁盘IO,这显然是并不太合适的
分区多且文件大:使用分多个任务卡ctime的方式解决,既然一个任务不够处理1000以上的分区,那我分两个或多个任务,每两年一个任务行不行?或使用脚本读取最大和最小的ctime提前将分区自动化创建好,然后一个spark任务进行合并即可;
分区少但文件大:这种情况没得说,直接上单个spark任务即可
分区少且文件小:同上
夜间执行:
Application attempt appattempt_1660014269451_69923_000002 doesn't exist in
ApplicationMasterService cache
unning 34807808B beyond the 'PHYSICAL' memory limit. Current usage: 3.5 GB of
3.5 GB physical memory used; 5.5 GB of 7.3 GB virtual memory used. Killing
container.
原因:最常见的报错,资源紧张,内存不足,appmaster直接被kill,适量增大excutor内存
5.内存错误
合ods历史数据时如果涉及分区过多,可以用hive任务执行,此时需要自己配置参数
msck repair table ods_binlog.ods_binlog_intention_avatar_notifications_di,
set mapreduce.map.memory.mb=2048,
set mapreduce.reduce.memory.mb=20480,
set hive.exec.reducers.max=1,
set mapred.reduce.tasks=1
其中mapred.reduce.tasks和hive.exec.reducers.max设置一个即可,前者代表所有任务中的reduce个数,后者代表每个任务也就是stage的最大个数,当底层公式计算的个数比该值大时,生效该值,小时生效计算值,但这里无论生效哪个,当reducetask个数设置时,直接以reducetask为准,公式为nvl(reducetask, min(reducemax, min(1009, 程序计算)))
因为只给一个reduce所以内存可相应给大点,否则会报gc和oom,map也可以相应增大

但是如果给的太大,例如40g,会直接报错

翻译为所需的REDUCE能力大于集群中支持的最大容器能力。杀死的工作
6.create ods table报错和改表名的一些注意事项
问题:创建instantmessage_avatar_immessages的ods表时一直报错key找不到
原因:我明明在数仓基础表里加入了这一行记录,但找不到,后来查询原因是该表不是mysql表,为mongo表,也就是说我在读取元数据创建ods的时候会找不到相应的元数据,自然找不到key
延伸:该表已经存在dwb目录下,但不是Kudu表,其实Mongo同步过来的hive表,相当于ods层,dwd层也有其相应任务,但其表名不太规范
解决办法:我先将其原名查询哪些任务依赖于该表,然后再改表名并修改数仓存储引擎和依赖于该表的sql
问题:但改表名时发生了一件奇怪的事情,改表名时映射Location和hdfs中的文件名都一起修改了,可我之前这些都需要手动改正
思考:我就怀疑是内外表改表名时,修改逻辑不一样,通过测试并观察文件名和建表语句的location确实验证了我的猜想
CREATE TABLE tmp.test002 ( id INT ) STORED AS TEXTFILE LOCATION 'hdfs://HDFSNS/Data/d1/hive/warehouse/tmp.db/test002';
ALTER TABLE tmp.test002 RENAME TO tmp.test0002;
show create table tmp.test0002;
ALTER TABLE tmp.test0002 SET TBLPROPERTIES('EXTERNAL' = 'TRUE');
ALTER TABLE tmp.test0002 RENAME TO tmp.test002;
show create table tmp.test002;
CREATE TABLE tmp.test003 ( id INT ) STORED AS TEXTFILE;
得出结论:内表改表名Location和文件名自动发生相应变化,由此也可以知道内表不用给location,外表改表名只是单纯改了表名而已
拓展:hue没办法把表名改成大写,因为在hue中不管你敲大小写,底层都识别为小写,只能通过脚本的方式去改不然一直报错视图找不到
7.关于oozie任务数量多报错
oozie任务有数量限制,任务数量超过限制会挂掉,需要在oozie-site.xml中增加参数
参考链接:https://github.com/Shopify/pyoozie/issues/33
8.关于union all数量过多
当union all数量过多时,程序会直接在分配容器时立马报错结束,而不是在运行到某个exctor的stage结束,也就是说两次失败执行的时间都很短,这就说明是driver端出现了问题
问题原因:在Spark程序中,SparkContext,DAGScheduler都是运行在Driver端的。对应rdd的Stage切分也是在Driver端运行,如果用户自己写的程序有过多的步骤,切分出过多的Stage,这部分信息消耗的是Driver的内存,这个时候就需要调大Driver的内存
解决方式:将driver端内存从3g增加到5g,excutor不用动,之后就可以正常运行