搭建Hive数据仓库爬过的坑-数据仓库设计要点

开篇

基于大数据的时代背景,分布式计算框架已经是无可替代的计算工具。那么数据仓库的运行环境就不只是拘泥于关系型数据库了,在数据量比较大的前提下,分布式计算将会比关系型数据库更胜一筹。

那么数据仓库环境从关系型数据到分布式计算框架的迁移过程中要考虑哪些问题或者需要解决哪些问题,下面我们具体详细讲解。

数据仓库环境: Hadoop + HDFS + Hive

数据仓库整体架构图

在这里插入图片描述

问题清单(本文会依次讲解下面所有问题)

  • 1 HDFS是基于文件存储的,那么列的分隔符应该怎么设定?
  • 2 在hive中表的类型怎样选择?分区又该怎样设置?
  • 3 hive直接操作文件,那么它的增删改怎样实现?
  • 4 ETL过程中数据抽取和转化应该怎样实现?
  • 5 元数据怎样管理?
  • 6 数据分层应该怎样设计?
  • 7 缓慢变化维(SCD)的处理方法
  • 8 计算效率怎样提升?hive计算逻辑都做了哪些优化
  • 9 遇到过的哪些问题汇总?

问题回答:

1 HDFS是基于文件存储的,那么列的分隔符应该怎么设定?

整个数据仓库的数据都是存储于HDFS上,HDFS是分布式文件系统,所以底层的数据是以文件的形式存在,那么文件要和HIVE中的表字段对应上就必须按照一定的分隔符分割,所以分隔符的选取是十分重要的。如果选取的分隔符在某个字段的内容中出现,就会导致表字段错位,导致查表时数据异常。
我们在数据仓库中指定的分隔符如下:

row format delimited fields terminated by '\u0007'

控制字符“^G” 也就是“\u0007” 是个不可见字符。在linux环境下cat 和 more命令下是看不见此分隔符的。只有在vim命令下才可以看见“^G”的分隔符。这样就能避免在字段内容里出现分隔符而导致的数据异常问题。

2 在hive中表的类型怎样选择?分区又该怎样设置?

hive的表有两种类型,一种是管理表,另一种是外部表。其中最大的区别就是外部表可以在drop table后HDFS上的数据依然不丢失。而管理表在执行drop语句后表结构和表数据都会删除。考虑到我们在ETL操作过程过程中可能会使用删除操作,并且为了保证数据不轻易丢失,我们决定使用外部表。另外,我们的集群的存储量是绝对够用的。
hive的分区设置是非常重要的。因为选择分区后可以缩减查询范围,大幅度的提高查询效率。一般的数据仓库设计中基本上都会是以时间作为分区标准,因为在数据仓库整个时间周期的前提下,一般都是T+1完成统计计算。所以数据的抽取和计算都是以固定的时间周期为基准。例如:设计表类型有日表,周表,月表,年表等。所以分区一般都是时间。大部分为日分区表,还有周分区、月分区、年分区。

partitioned by(etl_date string) 
partitioned by(etl_month string) 
partitioned by(etl_year string)

3 hive直接操作文件,那么它的增删改怎样实现?

新增记录记录是没有什么特殊操作的,对于HIVE而言,新增就是在HDFS逻辑地址下添加文件。
删除记录:无法进行固定行删除,HIVE中删除的策略就是先读取全部数据,在sql脚本或者mapreduce逻辑中添加过滤条件,筛除调待删除的记录后再重新复写回源表
修改操作:无法进行固定行修改。修改方案可以有两种:一种就是使用mapreduce程序,通过判断进行修改再写回表中;另一种就是先新增再删除,新增一条修改后的记录put到HDFS,通过sql或者mapreduce过滤掉逻辑主键相同更新时间老的那一条记录,保留新增的那一条记录即可完成

aa,bb,cc,1,2019-07-19 12:00:00 --此记录会进行更新操作,被删除掉
aa,bb,cc,3,2019-07-19 15:00:00 --若逻辑上联合主键下标为0,1,2那么此条记录会保留
aa,bb,dd,4,2019-07-19 18:00:00 --主键不重复,保留此记录

4 ETL过程中数据抽取和转化应该怎样实现?

数据抽取分为两类:
       1 直接从关系型数据库进行数据抽取
       2 直接ftp数据文件

对于第二类数据抽取就比较简单,直接从服务器上ftp文件就可以完成数据抽取。
相比之下第一类就是比较普遍也是比较复杂一点。直接从关系型数据库抽取数据可以可分为两种情况:第一是全量抽取,第二是增量抽取。
转化: 转化的过程是强依赖元数据管理。整个表的字段映射过程都是属于元数据管理。
一般将这类映射关系,配置关系等都会插入到配置表中,查询响应较快,便于管理。
转化的过程就是存在数据的规范化,将数据按照数据仓库的主题域、命名规范、转化规则等导入到数据仓库中。

转化规则类型一:硬编码转化,若出现异常则以“@”开头插入数据,便于后续查找知晓问题错误根源
CASE COLUMN WHEN 1 THEN '2' WHEN -1 THEN '4' WHEN -2 THEN '5' ELSE concat('@',trim(cast(COLUMN as char(2)))) END

转化规则类型二:字段加前缀或者后缀
concat('S01','_',TRIM(ID)) 

转化规则类型三:两个字段进行拼接                                  --  
concat(TRIM(ID),'_',TRIM(ID2))

5 元数据怎样管理?

元数据的管理是非常重要的,因为在整个数据仓库过程中都是依赖元数据。在实际工作中元数据可以分为两部分进行管理: 首先考虑的就是关系型数据库存储,元数据一般都是比较规范化的规则数据,将数据以数据库表的形式存储最好不过,可以提高查询响应效率,也可以分类入表便于管理。另外一种管理方法就是EXECL,直接明了一目了然

个人建议:元数据管理可以使用EXECL和配置数据库同时使用,EXECL可以在本地进行版本控制,将新增数据先存入EXECL生成插入表的sql语句,检查无误后可以导出sql语句在配置库中执行。相当于EXECL是一个配置库的前端显示,便于用户查看,也能快速的进行批量操作。

元数据涉及的流程有以下几个方面:
1 数据抽取时的卸数规则,包括:卸数的表名,字段名,字段转换逻辑,数据筛选逻辑(where条件),卸数的数据周期
2 数据加载: 加载的表名对应的数据文件名。
3 数据转化: 数据转化逻辑类型,全量删除后全量插入类、按照逻辑主键更新插入类、历史拉链表类。不同的转化逻辑对应的计算过程可以通过sql脚本或者mapreduce程序实现

6 数据分层应该怎样设计?

分层的好处:
        1 以空间换时间,通过大量的预处理来提升用户体验和查询效率。
        2 简化数据清洗过程,明确数据转化过程,便于处理数据异常

分层
1 近源层(或者临时存储层,或者ODS): 可提供数据清洗异常查询,可提供业务线人员的明细数据提取
2 数据仓库(DW层):数据是分主题的,集成的,便于数据分析
3 数据集市(DW层):数据集市通过存储预处理的冗余数据,进行快速的报表查询。数据提取,决策分析

7 缓慢变化维(SCD)的处理方法

缓慢变化维: 在实际情况下,维度的属性并不是静态的,它会随着时间的流失发生缓慢的变化
处理方法:

  • 1.新信息直接覆盖旧信息 C1 全删全插
  • 2.对主键进行update操作 C2 先删后插
  • 3.对属性进行历史拉链 C3 标准拉链 将变化的数据先关旧链,后开新链

8 计算效率怎样提升?hive计算逻辑都做了哪些优化?

优化点:
  • 1 HIVE表进行分区、分桶设置,减少无关的数据参与计算。
  • 2 列裁剪。对于大数据量的表查询时要筛选列,减少参与计算的数据量。
  • 3 拆分表。对于数据量大并且日增数据量也非常大的表进行拆分。拆分的形式比较多样化,可以做全量表和精简表的拆分、分月的滚动表拆分等。
  • 4 合并小文件。在计算时可能会出现大量的小数据量文件,导致任务多,计算量小,效率却不高。合并文件后可以提高计算效率。
  • 5 尽量避免数据倾斜。
  • 6 优化count(distinct x)
  • 7 设置适当的map和reduce数量,提高计算效率
  • 8 优化sql语句,减少执行的job数量,缩短计算时间
  • 9 使用动态分区策略
  • 10 使用mapreduce程序进行数据去重
  • 11 ETL过程中添加并发操作(shell脚本添加并发处理)
  • 12 编写UDF和UDAF函数提高执行效率

9 遇到过的哪些问题汇总?

  • 1.数据为null时,UDF函数报错,没有抛出异常值。UDF代码如下:
public class ToTimestamp extends UDF {
	
    	public static Timestamp evaluate(String str , String pattern) throws Exception{ 
    		SimpleDateFormat sdf = new SimpleDateFormat(); 
    		sdf.applyPattern(pattern);
    		Date datestr = sdf.parse(str);
    		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		//System.out.println(sdf2.format(datestr));
    		return Timestamp.valueOf(sdf2.format(datestr));
    }
 
	public static void main(String[] args) throws Exception{ 
		System.out.println(evaluate("null","yyyyMMddHH"));
	}

}
Exception in thread "main" java.text.ParseException: Unparseable date: "null"
	at java.text.DateFormat.parse(DateFormat.java:337)
	at com.umpay.udfs.fun.ToTimestamp.evaluate(ToTimestamp.java:22)
	at com.umpay.udfs.fun.ToTimestamp.main(ToTimestamp.java:33)
  • 2 动态分区创建数超过系统默认设置,错误如下:
Caused by: org.apache.hadoop.hive.ql.metadata.HiveFatalException: [Error 20004]: Fatal error occurred when node tried to create too many dynamic partitions. The maximum number of dynamic partitions is controlled by hive.exec.max.dynamic.partitions and hive.exec.max.dynamic.partitions.pernode. Maximum was set to: 256
    at org.apache.hadoop.hive.ql.exec.FileSinkOperator.getDynOutPaths(FileSinkOperator.java:933)
    at org.apache.hadoop.hive.ql.exec.FileSinkOperator.process(FileSinkOperator.java:709)
    at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:837)
    at org.apache.hadoop.hive.ql.exec.SelectOperator.process(SelectOperator.java:88)
    at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:837)
    at org.apache.hadoop.hive.ql.exec.TableScanOperator.process(TableScanOperator.java:97)
    at org.apache.hadoop.hive.ql.exec.MapOperator$MapOpCtx.forward(MapOperator.java:162)
    at org.apache.hadoop.hive.ql.exec.MapOperator.process(MapOperator.java:508)

解决方法:

SET hive.exec.dynamic.partition=true;
SET hive.exec.max.dynamic.partitions=2048;
SET hive.exec.max.dynamic.partitions.pernode=2048;
  • 使用 with cube、grouping sets 函数时报错
An additional MR job is introduced since the cardinality of grouping sets is more than hive.new.job.grouping.set.cardinality. 
This functionality is not supported with distincts. 
Either set hive.new.job.grouping.set.cardinality to a high number (higher than the number of rows per input row due to grouping sets in the query), 
or rewrite the query to not use distincts. The number of rows per input row due to grouping sets is 32

解决方法:

set hive.new.job.grouping.set.cardinality = 1000; 
发布了10 篇原创文章 · 获赞 11 · 访问量 1483

猜你喜欢

转载自blog.csdn.net/resin_404/article/details/97641605
今日推荐