建模理论
范式建模/维度建模的区别,应用场景的区别,优缺点
范式建模
范式建模是数仓之父 Inmon 所倡导的,“数据仓库”这个词就是这位大师所定义的,这种建模方式在范式理论上符合 3NF,这里的 3NF 与 OLTP 中的 3NF 还是有点区别的:关系数据库中的 3NF 是针对具体的业务流程的实体对象关系抽象,而数据仓库的 3NF 是站在企业角度面向主题的抽象。
Inmon 模型从流程上看是自上而下的,自上而下指的是数据的流向,“上”即数据的上游,“下”即数据的下游,即从分散异构的数据源 -> 数据仓库 -> 数据集市。以数据源头为导向,然后一步步探索获取尽量符合预期的数据,因为数据源往往是异构的,所以会更加强调数据的清洗工作,将数据抽取为实体-关系模型,并不强调事实表和维度表的概念。
范式建模就是将源表抽取为实体表,关系表,所以范式建模即是实体关系(ER)模型。数据没有冗余,符合三范式设计规范
维度建模
Kimball 模型从流程上看是自下而上的,即从数据集市-> 数据仓库 -> 分散异构的数据源。Kimball 是以最终任务为导向,将数据按照目标拆分出不同的表需求,数据会抽取为事实-维度模型,数据源经 ETL 转化为事实表和维度表导入数据集市,以星型模型或雪花模型等方式构建维度数据仓库,架构体系中,数据集市与数据仓库是紧密结合的,数据集市是数据仓库中一个逻辑上的主题域。
维度建模中,不需要单独维护数据关系表,因为关系已经冗余在事实表和维度表中。
应用场景的区别
维度建模适用于周期短,需求变化快,迭代频繁的场景
范式建模适用于相反场景
优缺点:
范式建模
优点:能够结合业务系统的数据模型,较方便的实现数据仓库的模型;同一份数据只存放在一个地方,没有数据冗余,保证了数据一致性;数据解耦,方便维护。
缺点:开发周期长,且难度较大,表的数量多;查询时关联表较多使得查询性能降低
维度建模
优点:模型结构简单,面向分析,为了提高查询性能可以增加数据冗余,反规范化的设计,开发周期短,能够快速迭代。
缺点:数据会大量冗余,预处理阶段开销大,后期维护麻烦;还有一个问题就是不能保证数据口径一致性
数据分层情况/原因,解决的什么问题
ODS(Operation Data Store)层
DW(Data warehouse)层
DWD(Data warehouse detail)层 - 结构与粒度原始表保持一致,对ODS层数据进行清洗(去除空值、脏数据、超过极限范围的数据)
DWS(Data warehouse service)层 - 以DWD为基础,进行轻度汇总
DWT(Data warehouse topic)层 - 以DWS为基础,进行累计汇总
ADS(Application Data Store)层
原因
- 空间换时间:通过建设多层次的数据模型供用户使用,避免用户直接使用操作型数据,可以更高效的访问数据
- 把复杂问题简单化:一个复杂的任务分解成多个步骤来完成,每一层只处理单一的步骤,比较简单和容易理解。而且便于维护数据的准确性,当数据出现问题之后,可以不用修复所有的数据,只需要从有问题的步骤开始修复
便于处理业务的变化:随着业务的变化,只需要调整底层的数据,对应用层对业务的调整零感知
解决问题:
数据源混乱
- 数据关系混乱,层级,复杂性高等
数据在DW层中某一层运行时出问题如何解决
搭建完善的元数据管理系统,在某一层出问题时,尽快评估数据优先级优先抢救,例如手动补偿等方式。数据仓库中的表的基本类型,以及为了保证引用完整性该以什么样的顺序对它们进行加载
数据仓库中的表的基本类型有维度表、事实表、子维度表、桥接表等几类。其中子维度表即雪花模型由支架维度技术处理,桥接表用来处理多值维度或层级结构。
数据仓库中需要加载的各类表之间有相互依赖的关系,所以加载时需要以一定的顺序进行加载。下面是一些加载的基本原则:
子维度表加载成功后,再加载维度表。
维度表加载成功后,再加载桥接表。
子维度表、维度表和桥接表都加载成功后,再加载事实表。
这个加载顺序可以通过主外键的关系来确定。(注意,此回答为总线架构的数据仓库的表的加载顺序。)
[
](https://blog.csdn.net/lurrass/article/details/105852048)
事实表和维度表的概念及类型
事实表的概念
- 每个数据仓库都包含一个或者多个事实数据表。
- 事实数据表可能包含业务销售数据,如现金登记事务所产生的数据,事实数据表通常包含大量的行
一般事实表中只存放数字或者一些Flag用来统计(Count),如收益、数量、支出等
事实表的类型
粒度事实表(Additive Fact)
- 周期快照事实表(Semi-Additive Fact)
- 聚合快照事实表(Non-Additive Fact)
-
粒度事实表(AdditiveFact):
表示的是在特定时间、空间点上的一次瞬间的测量。与粒度同层次的事实表,可以直接将事实字段进行Sum,Count等聚合操作
- 在事实表的设计时,一定要注意一个事实表只能有一个粒度,不能将不同粒度的事实建立在同一张事实表中。
交易粒度事实表的来源伴随交易事件成生的数据,例如销售单。在ETL过程中,以原子粒度直接进行迁移。
周期快照事实表(Semi-AdditiveFact)
周期快照事实表表现的是一个时间段,或者规律性的重复。这类表非常适合跟踪长期的过程,例如银行账户和其他形式的财务报表。最常用的财务上的周期快照事实表通常有一个月粒度。在周期快照事实表中的数据必须符合该粒度(就是说,他们必须量测的是同一个时间段中的活动)。对于一个好的周期快照事实表来说就是在粒度上有更多的事实。
-
聚合快照事实表(Non-AdditiveFact):
聚合快照事实表用于描述那些有明确开始和结束的过程,例如合同履行,保单受理以及常见的工作流。聚合快照不适合长期连续的处理,如跟踪银行账户或者描述连续的生产制造过程,如造纸。
- 聚合快照事实表的粒度是一个实体从其创建到当前状态的完整的历史。
- 在ETL过程中,随着业务处理过程的步骤逐步完善该表中的记录。
非事实事实表(FactlessFact Table):
每个事实表的粒度是一个事件量测。用来描述数据或事件。事件可以发生,但是没有具体的测量值
维度表
- 维度表可以看作是用户来分析数据的窗口,
- 维度表中包含事实数据表中事实记录的特性,有些特性提供描述性信息,有些特性指定如何汇总事实数据表数据,以便为分析者提供有用的信息,
-
维度表的类型
缓慢变化维(Slowly Changing Dimension)
- 快速变化维(Rapidly Changing Dimension)
- 大维(Huge Dimension)和迷你维(Mini-Dimension)
- 退化维(Degenerate Dimension)
缓慢变化维(SCD)
- 大多数的维度的内容都会有不同程度的改变。比如:
- 雇员的升职
- 客户更改了他的名称或地址
我们如何去处理这些维度中的变化呢?
下面提供了三个处理缓慢变化维的方式
- 直接更新到原先记录中
- 标记记录有效时间的开始日期和结束日期,加入版本控制
- 在记录中添加一个字段来记录历史
快速变化维(FCD):
当某个维度的变化是非常快的时候,我们认定他为快速变化维(具体要看实际的变化频率),比如:
- 产品的价格,地产的价格等
- 例如在一个分析用户如何使用搜索引擎的DW项目中,将用户搜索的关键字作为一个维度。由于用户使用的关键字会快速变化,因此关键字维度中的数据量会迅速增加。
-
通常RCD的处理可以分为3类:
Rapidly Changing Small Dimensions – 即维度表字段并不多,表的数据量也不大的情况。这种情形应用SCD中的Type2就可以了。(即:新增一行,旧行置过期)
- Rapidly Changing Large Dimensions – 即维度表字段较多,表的数据量较大的情况。这种情形Type2会增加过多的行并导致效率降低,因此通常采用Type3.(新增列:仅保存上一个值previous_value,current_value)
- Rapidly Changing Monster Dimensions – 最糟糕的情况,即维度表字段较多,表的数据量很大,且维度表中的一部分字段频繁变化的情况。此时应将相对稳定的字段和频繁变化的字段分割开,频繁变化的字段独立出来形成新的维度表与事实表相连或形成新的雪花关系。(维表分离)
大维度(HugeDimension):
数据仓库中最有意思的维度是一些非常大的维度,比如客户,产品等等。一个大的企业客户维度往往有上百万记录,每条记录又有上百个字段。而大的个人客户维度则会超过千万条记录,这些个人客户维度有时也会有十多个字段,但大多数时候比较少见的维度也只有不多的几个属性。
大维度需要特殊的处理。由于数据量大,很多涉及大维度数据仓库功能可能会很慢,效率很低。你需要采用高效率的设计方法、选择正确的索引、或者采用其它优化技术来处理以下问题,包括:
- 向大维度表填充数据
- 非限制维度的浏览性能,尤其是那些属性较少的维度
- 多限制的维度属性值的浏览时间
- 涉及大维度表的对事实表查询的低效率问题
-
迷你维(MiniDimension):
将常用的大维度中的少数字段提取出来,形成一个字段少的维度,在查询的时候便可以使用迷你维中的字段
这样的设计明显提高查询效率普通维:
普通维是基于一个维表的维度,由维表中的不同列来表示维度中的不同级别。
雪花维:
雪花维是基于多个维表的维度,各个维表间以外键关联,分别存储在同一维度中不同级别的成员列值。
父子维:
父子维是基于两个维表列的维度,由维表中的两列来共同定义各个成员的隶属关系,一列称为成员键列,标识每个成员,另一列称为父键列,标识每个成员的父代。
父子维度通俗的话来讲,这个表是自反 的,即外键本身就是引用的主键;类似这样的关系,如公司组织结构,分公司是总公司的一部分,部门是分公司的一部分,当然如果定义得好的话员工是部门的一部 分;通常公司的组织架构并非处在等层次上的,例如总公司下面的部门看起来就和分公司是一样的层次。因此父子维的层次通常不固定的。
因为父子维的复杂的自引用关系,如果按照缓慢维度的全历史记录方式来处理,必然导致逻辑关系混乱,处理起来比较棘手;任何一个组织的变动 (修改名称,更改引用,新增等等操作 )将会引起其下属节点相应的变动;任何一个意外都会导致整个结构的变化,同时发生意外后所带来的逻辑关系很难理顺。而 SQLServer2000中 Analysis Service对于这种急剧的变化处理并不稳定。
因此建议按照缓慢变化维的覆盖方式解决,即只根据主键这个唯一标志进行判断是否是新增还是修改。索引:
与在其他关系数据库中一样,索引对数据仓库的性能具有重要作用。每个维度表都必须在主键上建立索引。在其他列如标识层次结构级别的列上,索引对于某些专用查询的性能也很游泳。事实数据表必须在由维度表外键构成的组件主键上建立索引。
粒度(Grain) 层次(Hierarchy):
粒度是指数据仓库的数据单位中保存数据的细化或综合程度的级别。细化程度越高,粒度级就越小;相反,细化程度越低,粒度级就越大。设计粒度是设计数据仓库中的一个重要的前提
- 层次指描述明细数据的层次
一些影响维度建模的因素:
- 数据或展现的安全性
-
拉链表的使用场景、实现与优化
使用场景
拉链表使用场景:需要查看历史某一时间节点的状态,同时考虑到存储空间
实现方式
首先是拉链表dw_order_his的设置,有start_date和end_date两个字段;
其次在ods层创建一个ods_order_update表,储存当变化数据(包括insert和update的数据)
源表(order)
ods_order_update表和dw_order_his表的交集进行封链操作,end_date=current_date
ods_oder_update数据插入到his表中,对于记录的end_date=9999-12-31,start_date=current_date实际场景
在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:
有一些表的数据量很大,比如一张用户表,大约10亿条记录,50个字段,这种表,即使使用ORC压缩,单张表的存储也会超过100G,在HDFS使用双备份或者三备份的话就更大一些。
- 表中的部分字段会被update更新操作,如用户联系方式,产品的描述信息,订单的状态等等。
- 需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态。
- 表中的记录变化的比例和频率不是很大,比如,总共有10亿的用户,每天新增和发生变化的有200万左右,变化的比例占的很小。
全量主要数据表加载的策略为每次加载时需要根据主键将目标表的数据与源表数据进行比对,删除目标表中在源数据表中的相关记录,然后将源表数据全部插入目标表。表现在脚本上为先delete相关记录,然后insert所有记录。主表加载策略主要用于大部分主表的加载,比如客户信息等主要数据表。
增量拉链是指每次加载时,将源表数据视为增量抽取后的结果,加载到目标表时需要考虑数据历史情况。一般数据发生变化时关闭旧数据链,然后开新数据链。增量拉链针对的是历史表情况,由于数据仓库中记录了大部分数据历史表变化情况,因此增量拉链加载策略在数据仓库中是使用比较广泛的一种加载策略。通常这种历史表都含有start_date和end_date字段,首先全字段对比源数据和目标表得出真正的增量数据,这里的全字段不包含start_date和end_date字段,然后根据主键对目标表进行关旧链操作,然后对新增数据开新链,这种拉链策略同样可以处理全量数据
优化
拉链表当然也会遇到查询性能的问题,比如说我们存放了5年的拉链数据,那么这张表势必会比较大,当查询的时候性能就比较低了,个人认为两个思路来解决:
在一些查询引擎中,我们对start_date和end_date做索引,这样能提高不少性能。
保留部分历史数据,比如说我们一张表里面存放全量的拉链表数据,然后再对外暴露一张只提供近3个月数据的拉链表。
星型模型和雪花模型区别
星形模型(Star Schema):
- 事实被维度所包围,且维度没有被新的表连接
星形模型是一个比较折中的的建模方式(BIAPPS中都是用的是星形的建模方式)
雪花模型(Snowflake Schema):
事实表被多个维表或一个或多个层次所包围
- 雪花模型一般在处理大的且相对静态的层次的时候使用
根据事实表和维度表的关系,又可将常见的模型分为星型模型和雪花型模型。
星形模型:当所有维度表连接到事实表上的时候,整个图就像一个星星,故称之为星型模型。星型架构是一种非正规化的结构,多维数据集的每一个维度都直接与事实表相连,不存在渐变维度,所以数据有一定冗余。因为有冗余,所以很多统计不需要做外部的关联查询,因此一般情况下效率比雪花模型高。
雪花模型:当有多个维度表没有直接连接到事实表上,而是通过其他维度表连接到事实表上时,其图形就像雪花,故称雪花模型。雪花模型的优点是减少了数据冗余,所以一般情况下查询需要关联其他表。在冗余可接受的前提下使用星型模型。
星型模型和雪花模型的区别在于:维度表是直接连接到事实表还是其他维度表。
元数据是怎样管理的?元数据中都包括了那些数据
元数据是描述数据仓库内数据的结构和建立方法的数据,可将其按用途的不同分为两类:技术元数据(Technical Metadata)和业务元数据(Business Metadata)
如何管理
通过搭建完备的元数据管理系统进行管理。
元数据管理主要有两种方法:
- 对于相对简单的环境,按照通用的元数据管理标准建立一个集中式的元数据知识库
对于比较复杂的环境,分别建立各部分的元数据管理系统,形成分布式元数据知识库,然后,通过建立标准的元数据交换格式,实现元数据的集成管理
包括了哪些数据
技术元数据主要包括以下信息:
数据仓库结构的描述,包括仓库模式、视图、维、层次结构和导出数据的定义,以及数据集市的位置和内容
- 业务系统、数据仓库和数据集市的体系结构和模式
- 汇总用的算法,包括度量和维定义算法,数据粒度、主题领域、聚集、汇总、预定义的查询与报告
- 由操作环境到数据仓库环境的映射,包括源数据和它们的内容、数据分割、数据提取、清理、转换规则和数据刷新规则、安全(用户授权和存取控制)
业务元数据主要包括以下信息:
- 企业概念模型:这是业务元数据所应提供的重要的信息,它表示企业数据模型的高层信息、整个企业的业务概念和相互关系。以这个企业模型为基础,不懂数据库技术和SQL语句的业务人员对数据仓库中的数据也能做到心中有数
- 多维数据模型:这是企业概念模型的重要组成部分,它告诉业务分析人员在数据集市当中有哪些维、维的类别、数据立方体以及数据集市中的聚合规则。这里的数据立方体表示某主题领域业务事实表和维表的多维组织形式
- 业务概念模型和物理数据之间的依赖:以上提到的业务元数据只是表示出了数据的业务视图,这些业务视图与实际的数据仓库或数据库、多维数据库中的表、字段、维、层次等之间的对应关系也应该在元数据知识库中有所体现
应用
数据抽取和同步的方法
抽取
根据业务需求以及建模方法,将数据进行聚合,多表查询等方式进行抽取
同步
分全量,增量,新增及变化,特殊状况几个策略,离线和实时同步
例如按每日根据ODS层的表分别进行全量,增量等同步
join的表中筛选和where中筛选有什么区别
join on 和 where 在筛选条件的时候,on 会显示所有满足 | 不满足条件的数据而 where 只显示满足条件的数据
- 如果是left join 在on上写主表a的条件不会生效,全表扫描。
- 如果是left join 在on上写副表b的条件会生效,但是语义与写到where 条件不同。
- 如果是inner join 在on上写主表a、副表b的条件都会生效。
- 如果是full join 全外连接, 是在等值连接的基础上将左表和右表的未匹配数据都加上
怎样保证数据质量
由于数据质量管理是贯穿数据整个生命周期的,所以根据数据的各环节进行分点描述:
- 数据产生—-控制外部数据源
(1)非开放式输入,避免用户自己输入,尽量提供用户选择项。设定字典表,例如性别不允许输入(男、女、未知)以外的内容
(2)开放式输入,增加提示或者校验。例如设定临界值,例如年龄填了-1或者200,不允许输入。 - 数据接入—-保持多点录入一致
建立统一的数据体系,例如指标(度量)、口径(维度)。 - 数据存储—-保持数据结构统一
建立标准的数据结构,例如字段格式,系统提前定义好一种时间默认格式为yyyy-mm-dd。 - 数据处理—-保持处理流程一致,该点包括数据稽核和数据清洗
按照标准的处理流程,例如统一的清洗规则等。 - 数据输出—-保持数据结构统一
对数据处理后的结果进行保存时,按照数据存储的要求,进行标准化的统一管理。 - 数据展示—-持续监测分析数据
设立监测规则不断发现问题,通过解决问题不断改进规则。
Hadoop
HDFS读流程
- client向namenode发送RPC请求。请求文件block的位置
- namenode收到请求之后会检查用户权限以及是否有这个文件,如果都符合,则会视情况返回部分或全部的block列表,对于每个block,NameNode 都会返回含有该 block 副本的 DataNode 地址; 这些返回的 DN 地址,会按照集群拓扑结构得出 DataNode 与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的 DN 状态为 STALE,这样的排靠后
- Client 选取排序靠前的 DataNode 来读取 block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性)
- 底层上本质是建立 Socket Stream(FSDataInputStream),重复的调用父类 DataInputStream 的 read 方法,直到这个块上的数据读取完毕
- 当读完列表的 block 后,若文件读取还没有结束,客户端会继续向NameNode 获取下一批的 block 列表
- 读取完一个 block 都会进行 checksum 验证,如果读取 DataNode 时出现错误,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的DataNode 继续读
- read 方法是并行的读取 block 信息,不是一块一块的读取;NameNode 只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据
- 最终读取来所有的 block 会合并成一个完整的最终文件
HDFS写流程
- client客户端发送上传请求,通过RPC与namenode建立通信,namenode检查该用户是否有上传权限,以及上传的文件是否在hdfs对应的目录下重名,如果这两者有任意一个不满足,则直接报错,如果两者都满足,则返回给客户端一个可以上传的信息
- client根据文件的大小进行切分,默认128M一块,切分完成之后给namenode发送请求第一个block块上传到哪些服务器上
- namenode收到请求之后,根据网络拓扑和机架感知以及副本机制进行文件分配,返回可用的DataNode的地址
- 注:Hadoop 在设计时考虑到数据的安全与高效, 数据文件默认在 HDFS 上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份
- 客户端收到地址之后与服务器地址列表中的一个节点如A进行通信,本质上就是RPC调用,建立pipeline,A收到请求后会继续调用B,B在调用C,将整个pipeline建立完成,逐级返回client
- client开始向A上发送第一个block(先从磁盘读取数据然后放到本地内存缓存),以packet(数据包,64kb)为单位,A收到一个packet就会发送给B,然后B发送给C,A每传完一个packet就会放入一个应答队列等待应答
- 数据被分割成一个个的packet数据包在pipeline上依次传输,在pipeline反向传输中,逐个发送ack(命令正确应答),最终由pipeline 中第一个 DataNode 节点 A 将 pipelineack 发送给 Client
- 当一个 block 传输完成之后, Client 再次请求 NameNode 上传第二个 block ,namenode重新选择三台DataNode给client
HDFS在读取文件的时候,如果其中一个块突然损坏了怎么办
客户端读取完DataNode上的块之后会进行checksum 验证,也就是把客户端读取到本地的块与HDFS上的原始块进行校验,如果发现校验结果不一致,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的DataNode 继续读
HDFS在上传文件的时候,如果其中一个DataNode突然挂掉了怎么办
客户端上传文件时与DataNode建立pipeline管道,管道正向是客户端向DataNode发送的数据包,管道反向是DataNode向客户端发送ack确认,也就是正确接收到数据包之后发送一个已确认接收到的应答,当DataNode突然挂掉了,客户端接收不到这个DataNode发送的ack确认,客户端会通知 NameNode,NameNode检查该块的副本与规定的不符,NameNode会通知DataNode去复制副本,并将挂掉的DataNode作下线处理,不再让它参与文件上传与下载
MR中Map Task的工作机制
inputFile通过split被切割为多个split文件,通过Record按行读取内容给map(自己写的处理逻辑的方法)
,数据被map处理完之后交给OutputCollect收集器,对其结果key进行分区(默认使用的hashPartitioner),然后写入buffer,每个map task 都有一个内存缓冲区(环形缓冲区),存放着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式溢写到磁盘,当整个map task 结束后再对磁盘中这个maptask产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task的拉取
详细步骤:
1) 读取数据组件 InputFormat (默认 TextInputFormat) 会通过 getSplits 方法对输入目录中的文件进行逻辑切片规划得到 block, 有多少个 block就对应启动多少个 MapTask.
2) 将输入文件切分为 block 之后, 由 RecordReader 对象 (默认是LineRecordReader) 进行读取, 以 \n 作为分隔符, 读取一行数据, 返回
3) 读取 block 返回
4) Mapper 逻辑结束之后, 将 Mapper 的每条结果通过 context.write 进行collect数据收集. 在 collect 中, 会先对其进行分区处理,默认使用 HashPartitioner
5) 接下来, 会将数据写入内存, 内存中这片区域叫做环形缓冲区(默认100M), 缓冲区的作用是 批量收集 Mapper 结果, 减少磁盘 IO 的影响. 我们的 Key/Value 对以及 Partition 的结果都会被写入缓冲区. 当然, 写入之前,Key 与 Value 值都会被序列化成字节数组
6) 当环形缓冲区的数据达到溢写比列(默认0.8),也就是80M时,溢写线程启动, 需要对这 80MB 空间内的 Key 做排序 (Sort). 排序是 MapReduce 模型默认的行为, 这里的排序也是对序列化的字节做的排序
7) 合并溢写文件, 每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有 Combiner), 如果 Mapper 的输出结果真的很大, 有多次这样的溢写发生, 磁盘上相应的就会有多个临时文件存在. 当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并, 因为最终的文件只有一个, 写入磁盘, 并且为这个文件提供了一个索引文件, 以记录每个reduce对应数据的偏移量
MR中Reduce Task的工作机制
简单描述:
Reduce 大致分为 copy、sort、reduce 三个阶段,重点在前两个阶段。copy 阶段包含一个 eventFetcher 来获取已完成的 map 列表,由 Fetcher 线程去 copy 数据,在此过程中会启动两个 merge 线程,分别为 inMemoryMerger 和 onDiskMerger,分别将内存中的数据 merge 到磁盘和将磁盘中的数据进行 merge。待数据 copy 完成之后,copy 阶段就完成了,开始进行 sort 阶段,sort 阶段主要是执行 finalMerge 操作,纯粹的 sort 阶段,完成之后就是 reduce 阶段,调用用户定义的 reduce 函数进行处理
详细步骤:
1) Copy阶段:简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求maptask获取属于自己的文件(map task 的分区会标识每个map task属于哪个reduce task ,默认reduce task的标识从0开始)。
2) Merge阶段:这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活。merge有三种形式:内存到内存;内存到磁盘;磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的文件。
3) 合并排序:把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。
4) 对排序后的键值对调用reduce方法,键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到HDFS文件中
YARN作业提交流程
作业提交
作业提交的过程和经典的MapReduce很像, 新的作业ID(应用ID)由资源管理器分配(第2步). 作业的客户端核实作业的输出, 计算输入的split, 将作业的资源(包括Jar包, 配置文件, split信息)拷贝给HDFS(第3步). 最后, 通过调用资源管理器的submitApplication()来提交作业(第4步).
作业初始化
当资源管理器收到submitApplciation()的请求时, 就将该请求发给调度器(scheduler), 调度器分配第一个container, 然后资源管理器在该container内启动应用管理器进程, 由节点管理器监控(第5a和5b步).
MapReduce作业的应用管理器是一个主类为MRAppMaster的Java应用. 其通过创造一些bookkeeping对象来监控作业的进度, 得到任务的进度和完成报告(第6步). 然后其通过分布式文件系统得到由客户端计算好的输入split(第7步). 然后为每个输入split创建一个map任务, 根据mapreduce.job.reduces创建reduce任务对象.
然后应用管理器决定如何运行构成整个作业的任务. 如果作业很小, 应用管理器会选择在其自己的JVM中运行任务, 这种作业称作是被unerized, 或者是以uber task的方式运行. 在任务运行之前, 作业的setup方法被调用来创建输出路径. 与MapRuduce 1中该方法由tasktracker运行的一个任务调用不同, 在YARN中是由应用管理器调用的.
任务分配
如果不是小作业, 那么应用管理器向资源管理器请求container来运行所有的map和reduce任务(第8步). (注:每个任务对应一个container,且只能在该container上运行)这些请求是通过心跳来传输的, 包括每个map任务的数据位置, 比如存放输入split的主机名和机架(rack). 调度器利用这些信息来调度任务, 尽量将任务分配给存储数据的节点, 或者退而分配给和存放输入split的节点相同机架的节点.
请求也包括了任务的内存需求, 默认情况下map和reduce任务的内存需求都是1024MB. 可以通过mapreduce.map.memory.mb和mapreduce.reduce.memory.mb来配置.
分配内存的方式和MapReduce 1中不一样, MapReduce 1中每个tasktracker有固定数量的slot, slot是在集群配置是设置的, 每个任务运行在一个slot中, 每个slot都有最大内存限制, 这也是整个集群固定的. 这种方式很不灵活.
在YARN中, 资源划分的粒度更细. 应用的内存需求可以介于最小内存和最大内存之间, 并且必须是最小内存的倍数.
任务运行
当一个任务由资源管理器的调度器分配给一个container后, 应用管理器通过练习节点管理器来启动container(第9a步和9b步). 任务有一个主类为YarnChild的Java应用执行. 在运行任务之前首先本地化任务需要的资源, 比如作业配置, JAR文件, 以及分布式缓存的所有文件(第10步). 最后, 运行map或reduce任务(第11步).
YarnChild运行在一个专用的JVM中, 但是YARN不支持JVM重用.
进度和状态更新
YARN中的任务将其进度和状态(包括counter)返回给应用管理器, 后者通过每3秒的脐带接口有整个作业的视图(view). 这和MapRduce 1不太一样, 后者的进度流从tasktracker到jobtracker。
客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应用管理器请求进度更新, 展示给用户。
在MapReduce 1中, jobtracker的UI有运行的任务列表及其对应的进度. 在YARN中, 资源管理器的UI展示了所有的应用以及各自的应用管理器的UI。
作业完成
除了向应用管理器请求作业进度外, 客户端每5分钟都会通过调用waitForCompletion()来检查作业是否完成. 时间间隔可以通过mapreduce.client.completion.pollinterval来设置。
作业完成之后, 应用管理器和container会清理工作状态, OutputCommiter的作业清理方法也会被调用. 作业的信息会被作业历史服务器存储以备之后用户核
HIVE
HIVE工作原理
Hive的工作原理简单的说就是一个查询引擎,接收到一个SQL,而后面做的事情包括:
- 词法分析/语法分析
- 使用antlr将SQL语句解析成抽象语法树(AST)
- 语义分析
- 从Megastore获取模式信息,验证SQL语句中队表名,列名,以及数据类型的检查和隐式转换,以及Hive提供的函数和用户自定义的函数(UDF/UAF)
- 逻辑计划生成
- 生成逻辑计划—算子树
- 逻辑计划优化
- 对算子树进行优化,包括列剪枝,分区剪枝,谓词下推等
- 物理计划生成
- 将逻辑计划生成包含由MapReduce任务组成的DAG的物理计划
- 物理计划执行
- 将DAG发送到Hadoop集群进行执行
- 最后把查询结果返回
Hive 和MySQL的区别
从结构上来看,Hive 和数据库除了拥有类似的查询语言,再无类似之处
Hive 的 join 有几种方式,怎么实现 join 的
有3 种 join 方式:
- 在 reduce 端进行 join,最常用的 join 方式。Map端的主要工作:为来自不同表(文件)的 key/value 对打标签以区别不同来源的记录。然后用连接字段作为 key,其余部分和新加的标志作为 value,最后进行输出。reduce 端的主要工作:在 reduce 端以连接字段作为 key 的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录 (在 map 阶段已经打标志)分开,最后进行笛卡尔。
- 在 map 端进行 join,使用场景:一张表十分小、一张表很大。在提交作业的时候先将小表文件放到该作业的 DistributedCache 中,然后从 DistributeCache 中取出该小表进行 join key / value 解释分割放到内存中(可以放大 Hash Map 等等容器中)。然后扫描大表,看大表中的每条记录的 join key /value 值是否能够在内存中找到相同 join key 的记录,如果有则直接输出结果。
- SemiJoin,semijoin 就是左边连接是 reducejoin 的一种变种,在 map 端过滤掉一些数据,在网络传输过程中,只传输参与连接的数据,减少了 shuffle的网络传输量,其他和 reduce的思想是一样的。实现:将小表中参与 join 的 key 单独抽取出来通过 DistributeCache 分发到相关节点,在 map 阶段扫描连接表,将 join key 不在内存 hashset 的纪录过滤掉,让参与 join 的纪录通过 shuffle 传输到 reduce 端进行 join,其他和 reduce join 一样
hive 内部表和外部表的区别
内部表:建表时会在 hdfs 创建一个表的存储目录,增加分区的时候,会将数据复制到此location下,删除数据的时候,将表的数据和元数据一起删除。
外部表:一般会建立分区,增加分区的时候不会将数据移到此表的 location下,删除数据的时候,只删除了表的元数据信息,表的数据不会删除
hive 是如何实现分区的
建表语句:create table tablename (id) partitioned by (dt string)
增加分区:alter table tablenname add partition (dt = ‘2016-03-06’)
删除分区:alter table tablename drop partition (dt = ‘2016-03-06’)
Hive 有哪些方式保存元数据,各有哪些优缺点
- 存储于 derby 数据库,此方法只能开启一个hive客户端,不推荐使用
- 存储于mysql数据库中,可以多客户端连接,推荐使用
hive 如何优化
- join 优化,尽量将小表放在 join 的左边,如果一个表很小可以采用 mapjoin。
- 排序优化,order by 一个 reduce 效率低,distirbute by +sort by 也可以实现全局排序。
- 使用分区,查询时可减少数据的检索,从而节省时间
- 数据存储及压缩。
针对hive中表的存储格式通常有orc和parquet,压缩格式一般使用snappy。相比与textfile格式表,orc占有更少的存储。因为hive底层使用MR计算架构,数据流是hdfs到磁盘再到hdfs,而且会有很多次,所以使用orc数据格式和snappy压缩策略可以降低IO读写,还能降低网络传输量,这样在一定程度上可以节省存储,还能提升hql任务执行效率; - 通过调参优化。
并行执行,调节parallel参数;
调节jvm参数,重用jvm;
设置map、reduce的参数;开启strict mode模式;
关闭推测执行设置。 - 有效地减小数据集将大表拆分成子表;结合使用外部表和分区表。
- SQL优化
大表对大表:尽量减少数据集,可以通过分区表,避免扫描全表或者全字段;
大表对小表:设置自动识别小表,将小表放入内存中去执行
hive 中的压缩格式 RCFile、 TextFile、 SequenceFile 各有什么区别
- TextFile:默认格式,数据不做压缩,磁盘开销大,数据解析开销大
- SequenceFile:Hadoop API提供的一种二进制文件支持,使用方便,可分割,可压缩,支持三种压缩NONE,RECORD,BLOCK。
- RCFILE 是一种行列存储相结合的方式。首先,将数据按行分块,保证同一个 record 在同一个块上,避免读一个记录读取多个block。其次,块数据列式存储,有利于数据压缩和快速的列存取。数据加载的时候性能消耗大,但具有较好的压缩比和查询响应
hive 相对于Oracle来说有那些优点
- 存储,hive 存储在 hdfs 上,oracle 存储在本地文件系统。
- 扩展性,hive 可以扩展到数千节点,oracle 理论上只可扩展到 100 台左右。3)单表存储,数据量大 hive 可以分区分桶,oracle 数据量大只能分表
Hive 的 sort by 和 order by 的区别
- order by 会对输入数据做全局排序,只有一个 reduce,数据量较大时,很慢。
- sort by 不是全局排序,只能保证每个 reduce 有序,不能保证全局有序,需设置mapred.reduce.tasks>1
Hive数据倾斜排查及优化
hive数据倾斜通常表现为一个或者几个reduce节点运行很慢,延长了整个任务完成的时间
1,小表与大表Join时容易发生数据倾斜,表现为小表的数据量比较少但key却比较集中,导致分发到某一个或几个reduce上的数据比其他reduce多很多,造成数据倾斜
优化:使用Map Join将小表装入内存,在map端完成join操作,这样就避免了reduce操作。有两种方法可以执行Map Join:
- 通过hint指定小表做MapJoin
- 通过配置参数自动做MapJoin
2,大表与大表Join,当其中一张表的NULL值(或其他值)比较多时,容易导致这些相同值在reduce阶段集中在某一个或几个reduce上,发生数据倾斜问题
优化:
- 将NULL值提取出来最后合并,这一部分只有map操作;非NULL值的数据分散到不同reduce上,不会出现某个reduce任务数据加工时间过长的情况,整体效率提升明显。这种方法由于有两次Table Scan会导致map增多
- 在Join时直接把NULL值打散成随机值来作为reduce的key值,不会出现某个reduce任务数据加工时间过长的情况,整体效率提升明显。这种方法解释计划只有一次map,效率一般优于第一种方法
