大数据项目前三层--学习笔记

ODS层搭建

原始数据层,ODS层是原始数据的完整备份,不做任何修改。为了方便DWD层使用,一般会在ODS层增加抽取数据的日期字段。

1,列式存储和行式存储

行存储的特点: 查询满足条件的一整行(所有列)数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点: 因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
1,TEXTFILE
默认格式,行式存储。可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。并且反序列化过程中,必须逐个字符判断是不是分隔符和行结束符,性能较差。
2, ORCFILE
使用ORC文件格式可以提高hive读、写和处理数据的能力。ORCFile是RCFile的升级版。
在ORC格式的hive表中,数据按行分块,每块按列存储。结合了行存储和列存储的优点。记录首先会被横向的切分为多个stripes,然后在每一个stripe内数据以列为单位进行存储,所有列的内容都保存在同一个文件中。
每个stripe的默认大小为256MB,相对于RCFile每个4MB的stripe而言,更大的stripe使ORC可以支持索引,数据读取更加高效。
3,zlib压缩
优点:压缩率比较高;hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样。
缺点:压缩性能一般。
4, snappy压缩
优点:高速压缩速度和合理的压缩率。
缺点:压缩率比zlib要低;hadoop本身不支持,需要安装(CDH版本已自动支持,可忽略)。
5,系统采用的格式
因为ORCFILE的压缩快、存取快,而且拥有特有的查询优化机制,所以系统采用ORCFILE存储格式(RCFILE升级版),压缩算法采用orc支持的ZLIB和SNAPPY。
在ODS数据源层,因为数据量较大,可以采用orcfile+ZLIB的方式,以节省磁盘空间;
而在计算的过程中(DWD、DWM、DWS、APP),为了不影响执行的速度,采用orcfile+SNAPPY的方式,提升hive和presto的执行速度。
存储空间足够的情况下,推荐采用SNAPPY压缩。

2, 数据同步方式

1, 全量覆盖
不需要分区,每次同步都是先删后写,直接覆盖。
适用于数据不会有任何新增和变化的情况。比如地区、时间、性别等维度数据,不会变更或很少会有变更,可以只保留最新值。
2, 仅新增同步
每天新增一个日期分区,同步并存储当天的新增数据。比如登录记录表、访问日志表、交易记录表、商品评价表等。
3,新增及更新同步
每天新增一个日期分区,同步并存储当天的新增和更新数据。
适用于既有新增又有更新的数据,比如用户表、订单表、商品表等。
4,全量同步
每天新增一个日期分区,同步所有数据,历史数据定期删除。
适用于数据会有新增和更新,但是数据量较少,且历史快照不用保存很久的情况。

DWD层搭建

DWD明细层,数据粒度和ODS层一致,主要对数据进行清洗转换等操作。

1,维度建模流程

掌握了数仓的相关概念和分层后,便可着手开始实际建设数仓。由于ODS层的数据是保持原貌从业务数据库中直接拉取,因此重点考虑DW层和APP层的事实表建设。
Kimball对于维度模型设计提出了四个步骤:选择业务过程、声明粒度、确定维度和确定事实,但是由于目前业务和技术的发展,不可避免地需要把一些维度冗余进事实表中,因此在建设事实表的过程中还需要考虑把哪些通用的维度冗余进去。
1、选择业务过程
与其说选择业务过程,不如说理解清楚需求(事实表)。当从零开始构建数仓时,需要在熟悉业务流程的基础上选择最重要、易实现、对下游数据影响大的业务过程。而当数仓的具备一些最基础的中间表后,无论是业务方从业务需要的角度,还是数仓同学基于整体规划的角度,或者是在建表的过程中发现了某些坑需要去填,其本质都是一个个对数仓的需求(事实表),需要在理清需要的事实表后不断完善和丰富数仓的内容。
2、声明粒度
即每行是什么含义。在DWD层,其粒度往往是最小粒度,如表中每一行是不可再细分的订单维度、用户的每次操作记录等。而在DWS层,由于会进行一定程度的聚合,因此其粒度要较DWD层大,如每个品类、每个店铺、每一天的相关聚合信息。
3、确定维度
即表中所需要包括的环境信息,如类型、地区、日期、时间等维度。
4、确定事实
表中需要度量的属性(字段),如订单事实表中的金额、数量等,事实与维度共同构成了表中的所有列。一些不可加事实,如比率,记得要拆成可加事实,方便下游了解计算过程以及进行二次加工。
5、冗余维度
完全把事实表和维度表拆开,这是不现实也不符合效率的,因此建设过程中需要把一些通用高频的维度冗余进事实表中,以空间换时间的方法提升效率。

拉链表的应用

一,,应用场景
当数据存在有变更的情况时,可以采用渐变维(SCD)模型来解决。这里推荐使用SCD2拉链表。拉链表既可以反应数据的历史状态,又能在最大程度上节省存储。
拉链表的实现需要在原始字段基础上增加两个新字段:
start_date(表示该条记录的生命周期开始时间——周期快照时的状态)
end_date(该条记录的生命周期结束时间)

ODS层的数据同步方式用到了【全量覆盖】、【仅新增同步】、【新增及更新同步】,其中【新增及更新同步】的方式涉及到了更新数据,为了便于使用,在DWD层采用SCD2拉链表存储。
二,拉链表的数据抽取
1.ODS抽取新增/变更数据;
2.初始化拉链表数据(仅首次运行);
3.重建临时表tmp;
4.合并昨日增量数据(ods表)与历史数据(拉链表)到临时表
(1)新数据(员工)end_date设为’9999-99-99’,也就是当前有效;
(2)如果增量数据有重复id的旧数据(未离职老员工),将旧数据end_date(退休日期)更新为前天(昨日-1),也就是从昨天开始不再生效;
(3)合并后的数据写入tmp表;
5.将临时表的数据,覆盖到拉链表中;
6.第二天再次循环。

DWB层实现

DWB基础数据层,对经常使用的明细数据进行冗余形成宽表,一般用作中间层。将当前有效的基础数据关联查询出来,覆盖保存。为DWS和DM的聚合统计作准备。
选取字段时,如有相同的字段不再重复录入,没有统计价值的关联字段(比如logo图片url)也不需要录入。
DWB层只需要保留一段周期内(1年)的数据,满足后续计算的要求即可。

注:宽表从字面意义上讲就是字段比较多的数据库表。通常是指业务主题相关的指标、维度、属性关联在一起的一张数据库表。由于把不同的内容都放在同一张表存储,宽表已经不符合三范式的模型设计规范,随之带来的主要坏处就是数据的大量冗余,与之相对应的好处就是查询性能的提高与便捷。宽表通过把相关字段放在同一张表中,可以大大提高数据计算时的效率问题。

3, 分桶和分区

分桶是将数据集分解成更容易管理的若干部分的一个技术,是比分区更为细粒度的数据范围划分。
1,获得更高的查询处理效率
在分区数量过于庞大以至于可能导致文件系统崩溃时,或数据集找不到合理的分区字段时,我们就需要使用分桶来解决问题了。
2,分区中的数据可以被进一步拆分成桶,不同于分区对列直接进行拆分,桶往往使用列的哈希值对数据打散,并分发到各个不同的桶中从而完成数据的分桶过程。
注意,hive使用对分桶所用的值进行hash,并用hash结果除以桶的个数做取余运算的方式来分桶,保证了每个桶中都有数据,但每个桶中的数据条数不一定相等。
如果另外一个表也按照同样的规则分成了一个个小文件。两个表join的时候,就不必要扫描整个表,只需要匹配相同分桶的数据即可,从而提升效率。
在数据量足够大的情况下,分桶比分区有更高的查询效率。

4,数据采样

在真实的大数据分析过程中,由于数据量较大,开发和自测的过程比较慢,严重影响系统的开发进度。此时就可以使用分桶来进行数据采样。采样使用的是一个具有代表性的查询结果而不是全部结果,通过对采样数据的分析,来达到快速开发和自测的目的,节省大量的研发成本。

5,分桶和分区的区别

1.分桶对数据的处理比分区更加细粒度化:分区针对的是数据的存储路径;分桶针对的是数据文件;
2.分桶是按照列的哈希函数进行分割的,相对比较平均;而分区是按照列的值来进行分割的,容易造成数据倾斜;
3.分桶和分区两者不干扰,可以把分区表进一步分桶。
4,CLUSTERED BY来指定划分桶所用列;
SORTED BY对桶中的一个或多个列进行排序;
into 6 buckets指定划分桶的个数。
分桶规则:HIVE对key的hash值除bucket个数取余数,保证数据均匀随机分布在所有bucket里。
5,插入数据
insert into table test_buck select id, name from temp_buck;
hive.enforce.bucketing:启用桶表,数据分桶是否被强制执行,默认false,如果开启,则写入table数据时会启动分桶。
6,文本数据处理
注意:对于分桶表,不能使用load data的方式进行数据插入操作,因为load data导入的数据不会有分桶结构。
如何避免针对桶表使用load data插入数据的误操作呢?
–限制对桶表进行load操作
set hive.strict.checks.bucketing = true;
(1.先创建临时表,通过load data将txt文本导入临时表。

--创建临时表
create table temp_buck(id int, name string)
row format delimited fields terminated by ',';
--导入数据
load data local inpath '/tools/test_buck.txt' into table temp_buck;

(2.使用insert select语句间接的把数据从临时表导入到分桶表。

--启用桶表
set hive.enforce.bucketing=true;
--限制对桶表进行load操作
set hive.strict.checks.bucketing = true;
--insert select
insert into table test_buck select id, name from temp_buck;
--分桶成功

6, 数据采样

对表分桶一般有两个目的,提高数据查询效率、抽样调查。通过前面的讲解,我们已经可以对分桶表进行正常的创建并导入数据了。一般在实际生产中,对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结果,比如在开发自测的时候。这个时候Hive就可以通过对表进行抽样来满足这个需求。

select * from table tablesample(bucket x out of y on column)

hive根据y的大小,决定抽样的比例。y必须是table总bucket数的倍数或者因子。
例如,table总共分了10份bucket,当y=2时,抽取(10/2=)5个bucket的数据,当y=10时,抽取(10/10=)1个bucket的数据。

x表示从哪个bucket开始抽取,如果需要取多个分区,以后的分区号为当前分区号加上y。
例如,table总bucket数为6,tablesample(bucket 1 out of 2),表示总共抽取(6/2=)3个bucket的数据,从第1个bucket开始,抽取第1(x)个和第3(x+y)个和第5(x+y)个bucket的数据。
注意:x的值必须小于等于y的值。否则会抛出异常:FAILED: SemanticException [Error 10061]: Numerator should not be bigger than denominator in sample clause for table stu_buck。

7, Map Join

MapJoin顾名思义,就是在Map阶段进行表之间的连接。而不需要进入到Reduce阶段才进行连接。这样就节省了在Shuffle阶段时要进行的大量数据传输。从而起到了优化作业的作用。
要使MapJoin能够顺利进行,那就必须满足这样的条件:除了一份表的数据分布在不同的Map中外,其他连接的表的数据必须在每个Map中有完整的拷贝。
所以并不是所有的场景都适合用MapJoin。它通常会用在如下的一些情景:在二个要连接的表中,有一个很大,有一个很小,这个小表可以存放在内存中而不影响性能。
这样我们就把小表文件复制到每一个Map任务的本地,再让Map把文件读到内存中待用。
在Hive v0.7之前,需要使用hint提示 /*+ mapjoin(table) */才会执行MapJoin。Hive v0.7之后的版本已经不需要给出MapJoin的指示就进行优化。现在可以通过如下配置参数来进行控制:

set hive.auto.convert.join=true;

Hive还提供另外一个参数–表文件的大小作为开启和关闭MapJoin的阈值:

--旧版本为hive.mapjoin.smalltable.filesize
set hive.auto.convert.join.noconditionaltask.size=20971520

注意,如果hive.auto.convert.join是关闭的,则本参数不起作用。否则,如果参与连接的N个表(或分区)中的N-1个 的总大小小于512MB,则直接将连接转为Map连接。默认值为20MB。
MapJoin的使用场景:
关联操作中有一张表非常小

8,大小表关联

select f.a,f.b from A t join B f  on ( f.a=t.a and f.ftime=20110802)  

该语句中B表有30亿行记录,A表只有100行记录,而且B表中数据倾斜特别严重,有一个key上有15亿行记录,在运行过程中特别的慢,而且在reduece的过程中遇到执行时间过长或者内存不够的问题。
MAPJION会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map时进行了join操作,省去了reduce运行的效率会高很多。
这样就不会由于数据倾斜导致某个reduce上落数据太多而失败。于是原来的sql可以通过使用hint的方式指定join时使用mapjoin。

select /*+ mapjoin(A)*/ f.a,f.b from A t join B f  on ( f.a=t.a and f.ftime=20110802) 

在实际使用中,只要根据业务调整小表的阈值即可,hive会自动帮我们完成mapjoin,提高执行的效率。

9,不等连接

mapjoin还有一个很大的好处是能够进行不等连接的join操作,如果将不等条件写在where中,那么mapreduce过程中会进行笛卡尔积,运行效率特别低,如果使用mapjoin操作,在map的过程中就完成了不等值的join操作,效率会高很多。

select A.a ,A.b from A join B where A.a>B.a

10, Bucket-MapJoin

1, 作用
两个表join的时候,小表不足以放到内存中,但是又想用map side join这个时候就要用到bucket Map join。其方法是两个join表在join key上都做hash bucket,并且把你打算复制的那个(相对)小表的bucket数设置为大表的倍数。这样数据就会按照key join,做hash bucket。小表依然复制到所有节点,Map join的时候,小表的每一组bucket加载成hashtable,与对应的一个大表bucket做局部join,这样每次只需要加载部分hashtable就可以了。
2,条件
1) set hive.optimize.bucketmapjoin = true;
2) 一个表的bucket数是另一个表bucket数的整数倍
3) bucket列 == join列
4) 必须是应用在map join的场景中

注意:如果表不是bucket的,则只是做普通join。

11, SMB Join

全称Sort Merge Bucket Join。
作用
大表对小表应该使用MapJoin来进行优化,但是如果是大表对大表,如果进行shuffle,那就非常可怕,第一个慢不用说,第二个容易出异常,此时就可以使用SMB Join来提高性能。SMB Join基于bucket-mapjoin的有序bucket,可实现在map端完成join操作,可以有效地减少或避免shuffle的数据量。SMB join的条件和Map join类似但又不同。
确保分桶列排序
hive并不检查两个join的表是否已经做好bucket且sorted,需要用户自己去保证join的表数据sorted,否则可能数据不正确。
有两个办法:
1)hive.enforce.sorting 设置为 true。开启强制排序时,插数据到表中会进行强制排序,默认false。
2)插入数据时通过在sql中用distributed c1 sort by c1 或者 cluster by c1
另外,表创建时必须是CLUSTERED且SORTED,

create table test_smb_2(mid string,age_id string)
CLUSTERED BY(mid) SORTED BY(mid) INTO 500 BUCKETS;

综上,涉及到分桶表操作的齐全配置为:

--写入数据强制分桶
set hive.enforce.bucketing=true;
--写入数据强制排序
set hive.enforce.sorting=true;
--开启bucketmapjoin
set hive.optimize.bucketmapjoin = true;
--开启SMB Join
set hive.auto.convert.sortmerge.join=true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;

开启MapJoin的配置(hive.auto.convert.join和hive.auto.convert.join.noconditionaltask.size),还有限制对桶表进行load操作(hive.strict.checks.bucketing)可以直接设置在hive的配置项中,无需在sql中声明。
自动尝试SMB联接(hive.optimize.bucketmapjoin.sortedmerge)也可以在设置中进行提前配置。

你可能感兴趣的:(大数据,学习,笔记)