1、数据仓库
数据仓库是一个面向主题的、集成的、随时间变化的、但信息本身相对稳定的数据集合,用于对管理决策过程的支持。
2、数据库和数据仓库
数据库:是一种逻辑概念,用来存放数据的仓库。通过数据库软件来实现。数据库由许多表组成,表是二维的,一张表里面可以有很多字段,数据库的表,在与能够用二维表现多维关系。
数据仓库:是数据库概念的升级。从逻辑上理解,数据库和数据仓库没有区别,都是通过数据库软件实现的存放数据的地方,只不过从数据量来说,数据仓库要比数据库更庞大得多。数据仓库主要用于数据挖掘和数据分析,辅助领导做决策。
数据库与数据仓库的区别实际讲的是OLTP与OLAP的区别。
对比:
操作型处理,叫联机事务处理OLTP(On-Line Transaction Processing,),也可以称面向交易的处理系统,它是针对具体业务在数据库联机的日常操作,通常对少数记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作型处理。
分析型处理,叫联机分析处理OLAP(On-Line Analytical Processing)一般针对某些主题的历史数据进行分析,支持管理决策。
Hive基于Hadoop的一个数据分析工具,而且是将结构化的数据文件映射为一张数据库表,它提供的是类SQL查询功能,HQL语句转换为MapReduce的任务,然后来进行数据访问。
Hive和数据库比较
Hive 和数据库除了拥有类似的查询语言,再无类似之处。
1)数据存储位置
Hive 存储在 HDFS 。数据库将数据保存在块设备或者本地文件系统中。
2)数据更新
Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改的,
3)执行延迟
Hive 执行延迟较高。数据库的执行延迟较低。当然,这个是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。
4)数据规模
Hive支持很大规模的数据计算;数据库可以支持的数据规模较小。
3、数据仓库和数据集市的区别
数仓是面向整个集团组织的数据,数据集市是单个部门使用的,数据集市是数据仓库的子集,是一个主题域。
4、数仓的分层架构
ODS层:贴源层,与业务库保持一致,不做任何处理
DWD层(TD中P层):明细层,对数据进行规范话化,转换清洗。
DWS层(TD中CDM层):对数据按维度进行汇总,降低业务需求
ADS层:应用层,面向业务需求开发。
分层的好处:
清晰数据结构
数据血缘追踪
减少重复开发
把复杂问题简单化
5、Hive架构
什么是hive:Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射成一张表,并提供类SQL查询功能;其本质是将HQL转化成MapReduce程序。
6、DDL DML DCL
DDL(data definition language)数据库定义语言:
其实就是我们在创建表的时候用到的一些sql,比如说:CREATE、ALTER、DROP等。DDL主要是用在定义或改变表的结构,数据类型,表之间的链接和约束等初始化工作上
DML(data manipulation language)数据操纵语言:
就是我们最经常用到的 SELECT、UPDATE、INSERT、DELETE。 主要用来对数据库的数据进行一些操作。
DCL(Data Control Language)数据库控制语言:
是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。这个比较少用到。
7、Hive数据类型
类型转化
Hive 的原子数据类型是可以进行隐式转换的,类似于 Java 的类型转换,例如某表达式使用 INT 类型,TINYINT 会自动转换为 INT 类型,但是 Hive 不会进行反向转化,例如,
某表达式使用 TINYINT 类型,INT 不会自动转换为 TINYINT 类型,它会返回错误,除非使用 CAST 操作。
1.隐式类型转换规则如下
(1)任何整数类型都可以隐式地转换为一个范围更广的类型,如 TINYINT 可以转换成 INT,INT 可以转换成 BIGINT。
(2)所有整数类型、FLOAT 和 STRING 类型都可以隐式地转换成 DOUBLE。
(3)TINYINT、SMALLINT、INT 都可以转换为 FLOAT。
(4)BOOLEAN 类型不可以转换为任何其它的类型。
2.可以使用 CAST 操作显示进行数据类型转换
8、Hive内部表和外部表
Hive创建内部表时,会将数据移动到数据仓库指向的路径。hive默认创建的是内部表。
创建外部表时,仅记录数据所在的路径,不对数据的位置做任何改变。
在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。需要注意的是传统数据库对表数据验证是写时模式,而Hive在load时是不检查数据是否符合schema的,Hive遵循的是读时模式,只有在读的时候Hive才检查解析具体的数据字段。
Load data会转移数据
下面是几条原则 :
1.在大部分场景下,两种表的应用没有太大的区别。
2.数据场景简单, 几乎都是在Hive中的流转, 可以优先选用内部表。
3.需要对数据内容和元数据进行紧凑的管理, 建议内部表。
比如负责计算过程中用到的临时表, 数据内容随用随删, 而不希望关注底层文件。
4.数据处理场景较多,复杂, 建议采用外部表。
比如需要用Spark, Mapreduce等处理复杂的数据, 然后用Hive 做后续处理的; 需要处理非结构化日志数据的情况。
5.需要对数据和元数据分开管理的场景, 对数据安全性要求更高的场景, 建议采用外部表。
9、索引、分区和分桶
Hive的索引目的是提高Hive表指定列的查询速度。
没有索引时,类似’WHERE tab1.col1 = 10’ 的查询,Hive会加载整张表或分区,然后处理所有的rows,
但是如果在字段col1上面存在索引时,那么只会加载和处理文件的一部分。
与其他传统数据库一样,增加索引在提升查询速度时,会消耗额外资源去创建索引表和需要更多的磁盘空间存储索引。
分区:
是指按照数据表的某列或某些列分为多个区,区从形式上可以理解为文件夹,
我们可以按照日期对数据表进行分区,不同日期的数据存放在不同的分区,在查询时只要指定分区字段的值就可以直接从该分区查找。
创建分区表的时候,要通过关键字 partitioned by (name string)声明该表是分区表
向分区表导入数据的时候,要通过关键字partition(name=“jack”)显示声明数据要导入到表的哪个分区,这里表示要将数据导入到分区为name=jack的分区。
分桶:
分桶是相对分区进行更细粒度的划分。分桶将整个数据内容安装某列属性值得hash值进行区分,如要安装name属性分为3个桶,就是对name属性值的hash值对3取摸,按照取模结果对数据分桶。如取模结果为0的数据记录存放到一个文件,取模为1的数据存放到一个文件,取模为2的数据存放到一个文件。
注意:第一,分桶之前要执行命令hive.enforce.bucketiong=true;
第二,要使用关键字clustered by 指定分区依据的列名,还要指定分为多少桶,这里指定分为3桶。
第三,与分区不同的是,分区依据的不是真实数据表文件中的列,而是我们指定的伪列,但是分桶是依据数据表中真实的列而不是伪列。所以在指定分区依据的列的时候要指定列的类型,因为在数据表文件中不存在这个列,相当于新建一个列。而分桶依据的是表中已经存在的列,这个列的数据类型显然是已知的,所以不需要指定列的类型。
索引和分区最大的区别就是索引不分割数据库,分区分割数据库。
索引其实就是拿额外的存储空间换查询时间,但分区已经将整个大数据库按照分区列拆分成多个小数据库了。
分区和分桶最大的区别就是分桶随机分割数据库,分区是非随机分割数据库。
因为分桶是按照列的哈希函数进行分割的,相对比较平均;而分区是按照列的值来进行分割的,容易造成数据倾斜。
其次两者的另一个区别就是分桶是对应不同的文件(细粒度),分区是对应不同的文件夹(粗粒度)。
注意:普通表(外部表、内部表)、分区表这三个都是对应HDFS上的目录,桶表对应是目录里的文件
10、Order by、Sort by、Distribute by、Cluster by
Order by 查询的结果做一次全局排序
Sort by 在每个reducer端都会做排序,也就是说保证了局部有序
Distribute by和sort by一起使用,distribute by必须要写在sort by之前。
Cluster by的功能就是distribute by和sort by相结合,cluster by指定的列只能是降序,不能指定asc和desc。
11、窗口函数
RANK() 排序相同时会重复,总数不会变
DENSE_RANK() 排序相同时会重复,总数会减少
ROW_NUMBER() 会根据顺序计算
1) OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化
2)CURRENT ROW:当前行
3)n PRECEDING:往前n行数据
4) n FOLLOWING:往后n行数据
5)UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
6) LAG(col,n):往前第n行数据
7)LEAD(col,n):往后第n行数据
8) NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。
高级聚合函数
grouping sets 根据感兴趣的维度组合进行聚合
cube 分别按照不同维度进行聚合
rollup 函数是cube的子集,以最左侧维度为主,按照顺序依次进行聚合.
12、自定义函数
Hive自定义函数包括三种UDF、UDAF、UDTF
UDF(User-Defined-Function) 一进一出
UDAF(User- Defined Aggregation Funcation) 聚集函数,多进一出。Count/max/min
UDTF(User-Defined Table-Generating Functions) 一进多出,如lateral view explore)
使用方式 :在HIVE会话中add 自定义函数的jar文件,然后创建function继而使用函数。
用UDF函数解析公共字段;用UDTF函数解析事件字段。
自定义UDF:继承UDF,重写evaluate方法
自定义UDTF:继承自GenericUDTF,重写3个方法:initialize(自定义输出的列名和类型),process(将结果返回forward(result)),close
为什么要自定义UDF/UDTF,因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试.
13、动态分区
a. 静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来进行判断。
b. 详细来说,静态分区的列实在编译时期,通过用户传递来决定的;动态分区只有在 SQL 执行时才能决定。
c. 动态分区是基于查询参数的位置去推断分区的名称,从而建立分区
14、Hive行转列函数
concat_ws(sep, collect_set(col1)) :同组不同行合并成一列,以sep分隔符分隔。collect_set在无重复的情况下也可以collect_list()代替。collect_set()去重,collect_list()不去重
lateral view explode(split(col1,‘,’)) :同组同列的数据拆分成多行,以sep分隔符区分。
collect_set和collect_list区别:
它们都是将分组中的某列转为一个数组返回,不同的是collect_list不去重而collect_set去重。
15、Hive压缩格式
常用的主要由gzip,bzip2,snappy,lzo这几种.
在压缩速度上:snappy>>lzo>gzip>bzip2
在解压速度上: snappy>>lzo>gzip>bzip2
输入压缩:io.compression.codecs.xxxCodec(在core-site.xml中配置)
开启输出压缩:mapreduce.map.output.compress=false 默认值
使用map输出压缩类型:mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.BZip2Codec
reduce输出:mapreduce.output.fileoutputformat.compress=false(默认)
reduce输出压缩类型:mapreduce.output.fileoutputformat.compress=org.apache.hadoop.io.compress.BZip2Codec
输出使用的压缩类型:
mapreduce.output.fileoutputformat.compress.type=BLOCK;
开启hive中间传输压缩功能 hive.exec.compress.intermediate=true;
开启map输出阶段压缩可减少job中map和reduce的数据传输量
开启hive最终输出压缩功能:hive.exec.compress.output=true;
16、Hive的一些存储格式
TEXTFILE(行式存储),SEQUENCEFILE(行式存储),PARQUET(列式存储),ORC(列式存储)。
行式存储:1.行查询效率高。
2.列查询效率低。
3.数据压缩效率低。(因为一行中各列的数据类型有可能不行,会不停切换压缩格式)
列式存储:1.行查询效率低。
2.列查询效率高。
3.数据压缩效率高。
TEXTFILE:默认格式,数据不做压缩,磁盘开销大,数据解析开销大。结合gzip和bzip2使用。
ORC:每个ORC文件有一个或多个stripe组成,每个stripe有250mb大小。一个stripe由IndexData,RowData,StripFooter组成。indexData:某些列的索引数据;RowData:真正的数据存储;StripFooter:Stripe的元数据信息。
Parquet:是面向分析型业务的存储格式。是二进制存储的,不能直接读取,文件中包含文件的数据和元数据。
存储文件压缩对比:ORC>PARQUET>TEXTFILE
查询文件速度对比:ORC>TEXTFILE>PARQUET
17、Hive的调优
1.当一些单表数据量不大select * 查询时候,可以采取fetch抓取,查询不走mr。
set hive.fetch.task.conversion=more;(none为不适用)
2.当查询一些小数据集时候,可以开启本地模式,在单台机器上完成处理。
set hive.exec.mode.local.auto=true;
3.如果是小表关联大表,可以使用map join使数据在map阶段就开始join操作
set hive.auto.convert.join=true;
设置小表的阈值(大概在25M)
set hive.mapjoin.smalltable.filesize=25123456;
4.默认情况,map阶段同一个key的数据会分发给一个reduce,当一个key数据过大时候就容易发生数据倾斜。可以在map段做局部的聚合。
是否在map段进行聚合
set hive.map.aggr=true;
在map端进行聚合的条目数量
set hive.groupby.mapaggr.checkinterval=1000000
当发生数据倾斜时候是否进行负载均衡
set hive.groupby.skewindata=true;
当为true时,会生成两个mr job
第一个mr job中,map输出的数据会随机分布到reduce中,每个reduce做部分聚合工作,并输出结果。这样处理的结果是相同的group by key有可能被分发到不同的reduce中,从而达到负载均衡的目的。
第二个mr job在根据预处理的数据结果按照group by key分布到reduce中(这个过程可以保证同一个group by key 被分布到同一个reduce中),最后完成最终的聚合操作。
5.count(distinct)
因为同一个字段去重如果使用多个reduce达不到效果,但是如果一个reduce处理的数据量过大就导致job很难完成。所以可以先将字段进行group by 操作然后再使用count。
6.笛卡尔积
尽量避免join下不加on条件,或者无效的on条件。Hive只能使用一个reduce来处理笛卡尔积。
7.动态分区参数设置。
开启动态分区功能
set hive.exec.dynamic.partition=true;
开启非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;
在所有MR节点上,最多允许创建分区个数
set hive.exec.max.dynamic.partitions=1000
在每一个MR节点上,最多创建最大的分区个数
set hive.exec.max.dynamic.partitions.pernode=1000
在每一个MR JOB中,最多可以创建HDFS文件的个数
set hive,.exec.max.created.files=100000
当有空分区生成时,是否产生异常
set hive.error.on.empty.partition=false
8.并行执行。HIVE查询会转化为一个或多个阶段,当这些阶段中没有依赖关系时候可以采用。
不过job中并行的多,会占用集群的资源。
set hive.exec.parallel=true;
9.严格模式。
set hive.mapred.mode=nonstrict;
set hive.mapred.mode=strict;
1.对于分区表,where的后面必须由指定的分区字段来作为过滤条件,否则不允许执行。
2.对使用了order by语句的,必须加上limit。
3.限制笛卡尔积的查询。
10.JVM重用
set hive.mapred.reuse.jvm.num.tasks=10;
18、小文件的解决方案
从源头解决
1.使用Sequencefile作为存储格式,不用textfile。
2.减少reduce的数量(可以通过参数控制)
3.少用动态分区,用时记得按distributed by分区
对于已有的小文件
1.使用hadoop archive命令把小文件进行归档。
2.重建表,建表时减少reduce数量。
3.通过参数进行调节,设置map/reduce端的相关参数
如下:
设置map输入合并小文件的相关参数
//每个Map最大输入大小(这个值决定了合并后文件的数量)
set mapred.max.split.size=256000000;
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
设置map输出和reduce输出进行合并的相关参数:
//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 25610001000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件 merge
set hive.merge.smallfiles.avgsize=16000000
19、hive数据倾斜如何解决
1)group by,hive.map.aggr=true 这个配置项代表是否在map端进行聚合
2)map和reduce优化 要增加map个数 ; 当出现小文件过多,需要合并小文件。可以通过set hive.merge.mapfiles=true来解决
3)当HiveQL中包含count(distinct)时,使用sum…group by代替
4)当遇到一个大表和一个小表进行join操作时,使用mapjoin 将小表加载到内存中
5)遇到需要进行join的但是关联字段有数据为空,给空值分配随机的key值
20、mapjoin的实现原理?
MapJoin通常用于一个很小的表和一个大表进行join的场景,具体小表有多小,由参数 hive.mapjoin.smalltable.filesize来决定,该参数表示小表的总大小,默认值为25000000字节,即25M.
把小表文件复制到每一个Map任务的本地,再让Map把文件读到内存中待用。
MapJoin顾名思义,就是在Map阶段进行表之间的连接。而不需要进入到Reduce阶段才进行连接
MapJoin的实现方法:
1)在Map-Reduce的驱动程序中使用静态方法DistributedCache.addCacheFile()增加要拷贝的小表文件,。JobTracker在作业启动之前会获取打这个URI列表,并将相应的文件拷贝到各个TaskTracker的本地磁盘上。
2)在Map类的setup方法中使用DistributedCache.getLocalCacheFiles()方法获取文件目录,并使用标准的文件读写API读取相应的文件。