大数据数仓的性能优化主要围绕以下四个方面:
- 调度优化
- 模型优化
- 同步任务优化
- 计算任务优化
下面将对着四方面的优化方法进行具体阐述。
调度优化
在数据仓库建设的过程中,我们不可避免的要执行数据任务,那么这些任务如何进行配置才会是最优的?如果任务调度配置存在问题,将会导致出现瓶颈任务,或者无法及时提供业务所需的数据,这时我们就需要首先从调度方面来考虑,是不是有些任务的调度时间设置不合理?或者是不是有的任务的优先级设置不合理?
调度优化方式
- 耗时长的任务定时时间提前
- 业务重要程度高的任务定时时间提前,并设置高优先级,以便任务执行时有更多资源
- 重要性低的任务避开调度高峰时间,以免发生抢资源,导致资源不足的情况
- 减少任务的依赖层级
模型优化
对于数仓的建模而言,其实可以分为3NF建模和维度建模,推荐使用维度建模方式,可以按照星型模型或者雪花模型架构的方式去建模。3NF建模方式或者实体建模方式的应用性会差一点,在很多时候其性能也会差一点,但3NF会避免数据的冗余,其扩展性会好一些。而维度建模会有一定的数据冗余,并且冗余程度会很高,但是对于上层使用者而言,其易用性要好很多,并且其查询的性能也会好很多,虽然牺牲了一定的可扩展性,但是仍然在可接受的范围之内。之所以在大数据的框架下推荐使用维度建模,是因为建模产生的数据冗余对于大数据离线数仓来说,存储的成本并不高,因为其都属于SATA盘的存储,这样的存储成本是很低的。
总之,在大数据框架下推荐大家使用维度建模,使用星型模型或者雪花模型建模的方式,这样无论对于后续的运维还是后续的数据使用而言,都是比较便利的,并且性能会好一些。星型模型其实就是中间一个事实表,周边围绕着一堆维度表,其结构会简单一些,使用比较方便,性能也比较好;对于雪花模型而言,维度表可能还会继续关联其他的维度表,这种方式就是雪花模型,它会略微比星型模型复杂一些。其实星型模型也可以理解为较为简单的雪花模型。这里推荐大家使用星型模型,当然如果业务非常复杂,必须要使用雪花型也可以使用。这是因为星型模型虽然有数据冗余,但是其结构比较简单,容易理解,而且使用起来只需要A传给B就可以了,不需要再关联一个C。
除了上述两个较大的关键点之外,还有一些需要注意的小点,比如中间表的使用。我们一般将数仓分为三层,第一层做缓冲,第二层做整合,第三层做应用。但是并不是严格的只能分为三层,中间可能会有一些中间表,用于存储中间计算的结果,如果能够利用好中间表则会增强数仓的易用性和整体的性能。中间表的使用主要在数仓的第二层里面,因为需要整合数据,但整合后的数据仍是明细数据,对于这些表而言,数据量往往会比较大,而且会有见多的下游任务依赖这个表,因此可以做一些轻度的汇总,也就是做一些公共的汇总的中间表,这样应用层可以节省很多的计算量和成本。此外,虽然建议使用中间表,但也要注意中间表的数量,因为中间表数量过多,就会有太多的依赖层级。
在某些业务场景下,我们还需要对宽表进行拆表,拆表的情况一般发生在该表的字段较多,而其中几个字段的产出时间较晚,导致整个表的交付时间也会延迟,在这种情况下我们可以将这几个字段单独拆出来处理,这样就不会因为几个字段影响其余业务的使用。
与拆表相对的情况是合表,随着业务的增多,可能会有多个表中存放类似的数据指标,此时,我们可以将多个表整合到一个表中,减少数据任务的冗余。
模型优化--合理设计分区
表分区的功能一定要合理利用,这对于性能会产生很大的影响,一级分区一般都是按照天划分的,建议大家一天一个增量或者一天一个全量来做。二级分区的选择反而会多一些,首先大家要烤炉是否建立二级分区,其次大家再选择二级分区的建立方式。二级分区比较适合于在where语句中经常使用到的字段,而且这个字段应该是可枚举的,比如部门名称这样的。这里还有一个前提,就是如果这个字段的值的分布是非常不均匀的,那么就不太建议做二级分区。
同步任务优化
计算任务优化
离线数仓的计算任务基本都是通过SQL实现,这里也只讲在SQL部分如何进行优化。我们平时在进行数据处理,数据清洗,数据转换,数据加工的过程中都会使用到SQL。对于大数据体系下的SQL的优化而言,主要集中在两个大的方面进行:减少数据输入和避免数据倾斜。减少数据输入是最核心的一点,如果数据输入量太大,就会占用很多的计算资源。而数据倾斜是在离线数仓中经常会遇到的,数据倾斜分为几种,需要针对性的进行优化。
分区的合理使用
对有分区的表,合理使用分区可以过滤数据,避免全表扫描,有效的降低计算的数据输入。
一次输入,多个输出
SQL支持只读取一次源数据,然后将其写入到多个目标表,这样就保证了只做一次查询。语法如下
from (select user,seller from dwhtl.tmp_tab group by user,seller) a
insert overwrite table dwhtl.tmp_tab_1
select user,count(1)
group by user
insert overwrite table dwhtl.tmp_tab_2
select seller,count(1)
group by seller
先过滤数据再做计算
当我们在使用join,Reduce或者UDF时,先对数据进行过滤也能有效的提高任务的效率
Map倾斜
当发生数据再Map阶段倾斜的情况,第一种处理方式反馈至业务层面,看能否通过业务层面的修改让kv值均衡分布,如果业务层面无法处理,那么可以调整Map的个数,也就是加大Map的计算节点,默认情况是每256M的数据为一个计算节点,我们可以将其调小,也就是加大Map处理的节点的个数,使得数据分割的更加均匀一些。
Join倾斜
Join阶段的倾斜也是比较常见的,其解决方案需要分钟如下几种情况处理:
- 大表跟小表Join,则对小表加Map Join Hint
- 两个大表Join,key值出现倾斜,如果倾斜值为Null,则需对Null值进行随机值处理
- 两个大表Join,可以尽量先去重再Join
- 两个大表Join,可以从业务层面考虑,检查业务的必要性
Reduce倾斜
Reduce倾斜可能的情况有以下几种:
- group by某个key倾斜严重
此时首先考虑该key是否可以过滤,如果不能,则可以改变写法
select m.seller,sum(m.cnt)
from (select seller,count(1) cnt
from dwhtl.tmp_tab
group by seller,
if(seller='qaz',hash(rand())%10,0))m
group by m.seller
- distinct引起的数据倾斜
可以通过先打标记,然后再group by的方式。如下的例子中就是对于求IP段,求美国IP段、中国IP段以及总的IP段一共有多少个,左边这种图简单的写法,当出现IP Key的倾斜就会使得作业比较慢,那么就可以将其打散,先求解这条ID的记录是美国的还是中国的,在子查询里先做这一步,在外面再去求解总的Count或者Sum,从原本Map-Reduce两个阶段的处理改成了Map-Reduce-Reduce三个阶段处理,这种方案也能解决数据倾斜问题。
--可能会发生倾斜
select user,
count(distinct ip) ip_cnt,
count(distinct if(country='US',ip,null)) us_ip_cnt,
count(distinct if(country='CN',ip,null)) cn_ip_cnt
from tmp_tab
group by user
--改为如下写法
select user,
count(1) ip_cnt,
sum(is_us) us_ip_cnt,
sum(is_cn) cn_ip_cnt
from (select user,ip,
if(country='US',1,0) is_us,
if(country='CN',1,0) is_cn
from tmp_tab
group by user,ip)a
group by user
总结一下,性能调优归根结底还是资源不够了或者资源使用的不合理,或者是因为任务分配的不好,使得某些资源分配和利用不合理。