0

大数据通识:HIVE

 2 years ago
source link: https://www.biaodianfu.com/hive.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Hive简介

Hive 由 Facebook 实现并开源,是基于 Hadoop 的一个数据仓库工具。可以将结构化的数据映射为一张数据库表并提供 HQL(Hive SQL)查询功能。底层数据是存储在 HDFS 上,Hive的本质是将 SQL 语句转换为 MapReduce 任务运行,使不熟悉 MapReduce 的用户很方便地利用 HQL 处理和计算 HDFS 上的结构化的数据,适用于离线的批量数据计算。

Hive与普通关系型数据库的区别:

  • 查询语言。Hive提供了类 SQL 的查询语言 HQL,熟悉SQL的开发者可直接使用。
  • 数据存储位置。Hive 是建立在 Hadoop 之上的,所有 Hive 的数据都是存储在 HDFS 中的。而数据库则可以将数据保存在块设备或者本地文件系统中。
  • 数据格式。Hive 中没有定义专门的数据格式,数据格式可以由用户指定,用户定义数据格式需要指定三个属性:列分隔符(通常为空格、\t、\x001)、行分隔符(\n)以及读取文件数据的方法(Hive 中默认有三个文件格式 TextFile,SequenceFile 以及 RCFile)。由于在加载数据的过程中,不需要从用户数据格式到 Hive 定义的数据格式的转换,因此,Hive 在加载的过程中不会对数据本身进行任何修改,而只是将数据内容复制或者移动到相应的 HDFS 目录中。而在数据库中,不同的数据库有不同的存储引擎,定义了自己的数据格式。所有数据都会按照一定的组织存储,因此,数据库加载数据的过程会比较耗时。
  • 数据更新。由于 Hive 是针对数据仓库应用设计的,而数据仓库的内容是读多写少的。因此,Hive 中不支持对数据的改写和添加,所有的数据都是在加载的时候中确定好的。而数据库中的数据通常是需要经常进行修改的,因此可以使用INSERT INTO … VALUES 添加数据,使用UPDATE … SET修改数据。
  • 索引。Hive 在加载数据的过程中不会对数据进行任何处理,甚至不会对数据进行扫描,因此也没有对数据中的某些 Key 建立索引。Hive 要访问数据中满足条件的特定值时,需要暴力扫描整个数据,因此访问延迟较高。由于 MapReduce 的引入, Hive 可以并行访问数据,因此即使没有索引,对于大数据量的访问,Hive 仍然可以体现出优势。数据库中,通常会针对一个或者几个列建立索引,因此对于少量的特定条件的数据的访问,数据库可以有很高的效率,较低的延迟。由于数据的访问延迟较高,决定了 Hive 不适合在线数据查询。
  • 执行。Hive 中大多数查询的执行是通过 Hadoop 提供的 MapReduce 来实现的。而数据库通常有自己的执行引擎。
  • 执行延迟。Hive 在查询数据的时候,由于没有索引,需要扫描整个表,因此延迟较高。另外一个导致 Hive 执行延迟高的因素是 MapReduce 框架。由于 MapReduce 本身具有较高的延迟,因此在利用 MapReduce 执行 Hive 查询时,也会有较高的延迟。相对的,数据库的执行延迟较低。当然,这个低是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive 的并行计算显然能体现出优势。
  • 可扩展性。由于 Hive 是建立在 Hadoop 之上的,因此 Hive 的可扩展性是和 Hadoop 的可扩展性是一致的。而数据库由于 ACID 语义的严格限制,扩展行非常有限。目前最先进的并行数据库 Oracle 在理论上的扩展能力也只有 100 台左右。
  • 数据规模。由于 Hive 建立在集群上并可以利用 MapReduce 进行并行计算,因此可以支持很大规模的数据;对应的数据库可以支持的数据规模较小。

需明确的是,Hive 作为数仓应用工具,对比 RDBMS(关系型数据库) 有3个“不能”:

  • 不能像 RDBMS 一般实时响应,Hive 查询延时大
  • 不能像 RDBMS 做事务型查询,Hive 没有事务机制
  • 不能像 RDBMS 做行级别的变更操作(包括插入、更新、删除)

另外,Hive 相比 RDBMS 是一个更“宽松”的世界,比如:

  • Hive 没有定长的 varchar 这种类型,字符串都是 string
  • Hive 是读时模式,它在保存表数据时不会对数据进行校验,而是在读数据时校验不符合格式的数据设置为NULL

Hive的架构

由上图可知,hadoop和mapreduce是hive架构的根基。Hive架构包括如下组件:CLI(command line interface)、JDBC/ODBC、Thrift Server、WEB GUI、metastore和Driver(Complier、Optimizer和Executor),这些组件可以分为两大类:

  • 服务端组件:
    • Driver组件:该组件包括Complier、Optimizer和Executor,它的作用是将我们写的HiveQL(类SQL)语句进行解析、编译优化,生成执行计划,然后调用底层的mapreduce计算框架。
    • Metastore组件:元数据服务组件,这个组件存储hive的元数据,hive的元数据存储在关系数据库里,hive支持的关系数据库有derby、mysql。元数据对于hive十分重要,因此hive支持把metastore服务独立出来,安装到远程的服务器集群里,从而解耦hive服务和metastore服务,保证hive运行的健壮性,这个方面的知识,我会在后面的metastore小节里做详细的讲解。
    • Thrift服务:thrift是facebook开发的一个软件框架,它用来进行可扩展且跨语言的服务的开发,hive集成了该服务,能让不同的编程语言调用hive的接口。
  • 客户端组件:
    • CLI:command line interface,命令行接口。
    • Thrift客户端:上面的架构图里没有写上Thrift客户端,但是hive架构的许多客户端接口是建立在thrift客户端之上,包括JDBC和ODBC接口。
    • WEB GUI:hive客户端提供了一种通过网页的方式访问hive所提供的服务。这个接口对应hive的hwi组件(hive web interface),使用前要启动hwi服务。

Hive数据存储

Hive 的存储结构包括数据库、表、视图、分区和表数据等。数据库,表,分区等等都对 应 HDFS 上的一个目录。表数据对应 HDFS 对应目录下的文件。Hive 中所有的数据都存储在 HDFS 中,没有专门的数据存储格式,因为 Hive 是读模式 (Schema On Read),可支持 TextFile,SequenceFile,RCFile 或者自定义格式等。

Hive 没有专门的数据存储格式,也没有为数据建立索引,用户可以非常自由的组织 Hive 中的表,只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符,Hive 就可以解析数据。其次,Hive 中所有的数据都存储在 HDFS 中,Hive 中包含以下数据模型:Table,External Table,Partition,Bucket。

  • Hive 中的 Table 和数据库中的 Table 在概念上是类似的,每一个 Table 在 Hive 中都有一个相应的目录存储数据。例如,一个表 pvs,它在 HDFS 中的路径为:/wh/pvs,其中,wh 是在 hive-site.xml 中由 ${hive.metastore.warehouse.dir} 指定的数据仓库的目录,所有的 Table 数据(不包括 External Table)都保存在这个目录中。
  • Partition 对应于数据库中的 Partition 列的密集索引,但是 Hive 中 Partition 的组织方式和数据库中的很不相同。在 Hive 中,表中的一个 Partition 对应于表下的一个目录,所有的 Partition 的数据都存储在对应的目录中。例如:pvs 表中包含 ds 和 city 两个 Partition,则对应于 ds = 20090801, ctry = US 的 HDFS 子目录为:/wh/pvs/ds=20090801/ctry=US;对应于 ds = 20090801, ctry = CA 的 HDFS 子目录为;/wh/pvs/ds=20090801/ctry=CA
  • Buckets 对指定列计算 hash,根据 hash 值切分数据,目的是为了并行,每一个 Bucket 对应一个文件。将 user 列分散至 32 个 bucket,首先对 user 列的值计算 hash,对应 hash 值为 0 的 HDFS 目录为:/wh/pvs/ds=20090801/ctry=US/part-00000;hash 值为 20 的 HDFS 目录为:/wh/pvs/ds=20090801/ctry=US/part-00020
  • External Table 指向已经在 HDFS中存在的数据,可以创建 Partition。它和 Table在元数据的组织上是相同的,而实际数据的存储则有较大的差异。
    • Table 的创建过程和数据加载过程(这两个过程可以在同一个语句中完成),在加载数据的过程中,实际数据会被移动到数据仓库目录中,之后对数据对访问将会直接在数据仓库目录中完成。删除表时,表中的数据和元数据将会被同时删除。
    • External Table 只有一个过程,加载数据和创建表同时完成(CREATE EXTERNAL TABLE ……LOCATION),实际数据是存储在 LOCATION 后面指定的 HDFS 路径中,并不会移动到数据仓库目录中。当删除一个 External Table 时,仅删除。

Hive元数据存储

Hive的metastore组件是hive元数据集中存放地。Metastore组件包括两个部分:metastore服务和后台数据的存储。后台数据存储的介质就是关系数据库,例如hive默认的嵌入式磁盘数据库derby,还有mysql数据库。Metastore服务是建立在后台数据存储介质之上,并且可以和hive服务进行交互的服务组件,默认情况下,metastore服务和hive服务是安装在一起的,运行在同一个进程当中。我也可以把metastore服务从hive服务里剥离出来,metastore独立安装在一个集群里,hive远程调用metastore服务,这样我们可以把元数据这一层放到防火墙之后,客户端访问hive服务,就可以连接到元数据这一层,从而提供了更好的管理性和安全保障。使用远程的metastore服务,可以让metastore服务和hive服务运行在不同的进程里,这样也保证了hive的稳定性,提升了hive服务的效率。

Hive 将元数据存储在 RDBMS 中,有三种模式可以连接到数据库:

  • Single User Mode: 此模式连接到一个 In-memory 的数据库 Derby,一般用于 Unit Test。
  • Multi User Mode:通过网络连接到一个数据库中,是最经常使用到的模式。
  • Remote Server Mode:用于非 Java 客户端访问元数据库,在服务器端启动一个 MetaStoreServer,客户端利用 Thrift 协议通过 MetaStoreServer 访问元数据库。

Hive字段类型

分类 类型 描述 字面量示例

原始类型 BOOLEAN true/false TRUE

TINYINT 1字节的有符号整数 -128~127 1Y

SMALLINT 2个字节的有符号整数,-32768~32767 1S

INT 4个字节的带符号整数 1

BIGINT 8个字节的带符号整数 1L

FLOAT 4字节单精度浮点数 1.0

DOUBLE 8字节单精度浮点数 1.0

DEICIMAL 任意精度的带符号小数 1.0

STRING 字符串,变长 abc

VARCHAR 变长字符串 abc

CHAR 固定长度字符串 abc

BINARY 字节数组 无法表示

TIMESTAMP 时间戳,毫秒精度 1642123232761

DATE 日期 2022-01-14

INTERVAL 时间频率间隔

复杂类型 ARRAY 有序的的同类型的集合 array(1,2)

MAP key-value,key必须为原始类型,value可以任意类型 map(‘a’,1,’b’,2)

STRUCT 字段集合,类型可以不同 struct(‘1’,1,1.0), named_stract(‘col1’,’1’,’col2’,1,’clo3’,1.0)

UNION 在有限取值范围内的一个值 create_union(1,’a’,63)

decimal用法:用法:decimal(11,2) 代表最多有11位数字,其中后2位是小数,整数部分是9位;如果整数部分超过9位,则这个字段就会变成null;如果小数部分不足2位,则后面用0补齐两位,如果小数部分超过两位,则超出部分四舍五入。也可直接写 decimal,后面不指定位数,默认是 decimal(10,0)  整数10位,没有小数

Hive SQL简介

Hive 查询语句

Hive Select 常规语法与 Mysql 等 RDBMS SQL 几乎无异,下面附注语法格式,具体不做详细讲解。

SELECT 语法及语序

SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY order_condition]
[DISTRIBUTE BY distribute_condition [SORT BY sort_condition] ]
[LIMIT number]
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY order_condition]
[DISTRIBUTE BY distribute_condition [SORT BY sort_condition] ]
[LIMIT number]

多维度聚合分析 grouping sets/cube/roolup

不使用多维聚合方法:

SELECT NULL, NULL, NULL, COUNT(*)
FROM requests
UNION ALL
SELECT os, device, NULL, COUNT(*)
FROM requests GROUP BY os, device
UNION ALL
SELECT null, null, city, COUNT(*)
FROM requests GROUP BY city;
SELECT NULL, NULL, NULL, COUNT(*)
FROM requests
UNION ALL
SELECT os, device, NULL, COUNT(*)
FROM requests GROUP BY os, device
UNION ALL
SELECT null, null, city, COUNT(*)
FROM requests GROUP BY city;

使用 grouping sets:

SELECT os, device, city ,COUNT(*)
FROM requests
GROUP BY os, device, city GROUPING SETS((os, device), (city), ());
SELECT os, device, city ,COUNT(*)
FROM requests
GROUP BY os, device, city GROUPING SETS((os, device), (city), ());

cube 会枚举指定列的所有可能组合作为 grouping sets,而 roolup 会以按层级聚合的方式产生 grouping sets。如:

GROUP BY CUBE(a, b, c)
--等价于以下语句。
GROUPING SETS((a,b,c),(a,b),(a,c),(b,c),(a),(b),(c),())
GROUP BY ROLLUP(a, b, c)
--等价于以下语句。
GROUPING SETS((a,b,c),(a,b),(a), ())
GROUP BY CUBE(a, b, c)  
--等价于以下语句。  
GROUPING SETS((a,b,c),(a,b),(a,c),(b,c),(a),(b),(c),())

GROUP BY ROLLUP(a, b, c)
--等价于以下语句。  
GROUPING SETS((a,b,c),(a,b),(a), ())

正则方法指定 SELECT 字段列

说是指定,其实是排除,如:`(num|uid)?+.+` 排除 num 和 uid 字段列。另外,where 使用正则可以如此:where A Rlike B、where A Regexp B。

Lateral View(一行变多行)

Lateral View 和表生成函数(例如Split、Explode等函数)结合使用,它能够将一行数据拆成多行数据,并对拆分后的数据进行聚合。

假设您有一张表pageAds,它有两列数据,第一列是pageid string,第二列是adid_list,即用逗号分隔的广告ID集合。现需要统计所有广告在所有页面的出现次数,则先用 Lateral View + explode 做处理,即可正常分组聚合统计:

SELECT pageid, adid FROM pageAds LATERAL VIEW explode(adid_list) adTable AS adid;
SELECT pageid, adid FROM pageAds LATERAL VIEW explode(adid_list) adTable AS adid;

窗口函数

Hive 的窗口函数非常丰富, 其中最常用的窗口函数当属 row_number() over(partition by col order col_2),它可以实现按指定字段的分组排序。

  • COUNT 计算计数值。
  • AVG 计算平均值。
  • MAX 计算最大值。
  • MIN 计算最小值。
  • MEDIAN 计算中位数。
  • STDDEV 计算总体标准差。
  • STDDEV_SAMP 计算样本标准差。
  • SUM 计算汇总值。
  • DENSE_RANK 计算连续排名。
  • RANK 计算跳跃排名。
  • LAG 按偏移量取当前行之前第几行的值。
  • LEAD 按偏移量取当前行之后第几行的值。
  • PERCENT_RANK 计算一组数据中某行的相对排名。
  • ROW_NUMBER 计算行号。
  • CLUSTER_SAMPLE 用于分组抽样。
  • CUME_DIST 计算累计分布。
  • NTILE 将分组数据按照顺序切片,并返回切片值。

Hive定义变量

SET aa='10';
SELECT ${hiveconf:aa};
SET hivevar:aa='10';
SELECT ${hivevar:aa};
SET hiveconf:aa='10';
SELECT ${hiveconf:aa};
SET aa='10';
SELECT ${hiveconf:aa};

SET hivevar:aa='10';
SELECT ${hivevar:aa};

SET hiveconf:aa='10';
SELECT ${hiveconf:aa};

CTE语法和定义变量

with t1 as(
select user_id
from user
where ...
@var:= select
shop_id
from shop
where ...;
select *
from user_shop
where user_id in(select * from t1)
and shop_id in(select * from @var);
with t1 as(
    select user_id
    from user
    where ...
)

@var:= select
         shop_id
       from shop
       where ...;

select *
from user_shop
where user_id in(select * from t1)
and shop_id in(select * from @var);

Hive 定义语句

Hive 建表语句格式

方法一:独立声明

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [DEFAULT value] [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name [, col_name, ...]) [SORTED BY (col_name [ASC | DESC] [, col_name [ASC | DESC] ...])] INTO number_of_buckets BUCKETS]
[STORED BY StorageHandler] -- 仅限外部表
[WITH SERDEPROPERTIES (Options)] -- 仅限外部表
[LOCATION OSSLocation]; -- 仅限外部表
[LIFECYCLE days]
[AS select_statement]
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [DEFAULT value] [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name [, col_name, ...]) [SORTED BY (col_name [ASC | DESC] [, col_name [ASC | DESC] ...])] INTO number_of_buckets BUCKETS] 
[STORED BY StorageHandler] -- 仅限外部表
[WITH SERDEPROPERTIES (Options)] -- 仅限外部表
[LOCATION OSSLocation]; -- 仅限外部表
[LIFECYCLE days]
[AS select_statement]

方法二:从已有表直接复制

CREATE TABLE [IF NOT EXISTS] table_name LIKE existing_table_name
CREATE TABLE [IF NOT EXISTS] table_name LIKE existing_table_name

下面对当中关键的声明语句做解释:

  • [EXTERNAL]:声明为外部表,往往在该表需要被多个工具共享时声明,外部表删表不会删数据,只会删元数据。
  • col_name datatype:data_type 一定要严谨定义,避免 bigint、double 等等统统用 string 的偷懒做法,否则不知某天数据就出错了。(团队内曾有同事犯过此错误)
  • [if not exists]:创建时不指定,若存在同名表则返回出错。指定此选项,若存在同名表忽略后续,不存在则创建。
  • [DEFAULT value]:指定列的默认值,当INSERT操作不指定该列时,该列写入默认值。
  • [PARTITIONED BY]:指定表的分区字段,当利用分区字段对表进行分区时,新增分区、更新分区内数据和读取分区数据均不需做全表扫描,可以提高处理效率。
  • [LIFECYCLE]:是表的生命周期,分区表则每个分区的生命周期与表生命周期相同
  • [AS select_statement]:意味着可直接跟 select 语句插入数据

简单示例:创建表sale_detail来保存销售记录,该表使用销售时间 sale_date 和销售区域 region 作为分区列。

create table if not exists sale_detail
shop_name string,
customer_id string,
total_price double
partitioned by (sale_date string, region string);
create table if not exists sale_detail
(
shop_name     string,
customer_id   string,
total_price   double
)
partitioned by (sale_date string, region string);

创建成功的表可以通过 desc 查看定义信息:

desc <table_name>;
desc extended <table_name>; --查看外部表信息。
desc <table_name>;
desc extended <table_name>; --查看外部表信息。

如果需要不记得完整的表名,可以通过 show tables 在 db(数据库)范围内查找:

use db_name;
show tables ('tb.*'); --- tb.* 为正则表达式
use db_name;
show tables ('tb.*'); --- tb.* 为正则表达式

Hive 删表语句格式

DROP TABLE [IF EXISTS] table_name; --- 删除表
ALTER TABLE table_name DROP [IF EXISTS] PARTITION (partition_col1 = partition_col_value1, ...); --- 删除某分区
DROP TABLE [IF EXISTS] table_name;  --- 删除表
ALTER TABLE table_name DROP [IF EXISTS] PARTITION (partition_col1 = partition_col_value1,  ...); --- 删除某分区

Hive 变更表定义语句格式

ALTER TABLE table_name RENAME TO table_name_new; --- 重命名表
ALTER TABLE table_name ADD [IF NOT EXISTS] PARTITION (partition_col1 = partition_col_value1 ...); --- 增加分区
ALTER TABLE table_name ADD COLUMNS (col_name1 type1 comment 'XXX'); --- 增加列,同时定义类型与注释
ALTER TABLE table_name CHANGE COLUMN old_col_name new_col_name column_type COMMENT column_comment; --- 修改列名和注释
ALTER TABLE table_name SET lifecycle days; --- 修改生命周期
ALTER TABLE table_name RENAME TO table_name_new;  --- 重命名表
ALTER TABLE table_name ADD [IF NOT EXISTS] PARTITION (partition_col1 = partition_col_value1 ...);  --- 增加分区
ALTER TABLE table_name ADD COLUMNS (col_name1 type1 comment 'XXX');  --- 增加列,同时定义类型与注释
ALTER TABLE table_name CHANGE COLUMN old_col_name new_col_name column_type COMMENT column_comment;  --- 修改列名和注释
ALTER TABLE table_name SET lifecycle days;  --- 修改生命周期

Hive 操作语句

Hive insert语句格式:

INSERT OVERWRITE|INTO TABLE tablename [PARTITION (partcol1=val1...]
select_statement
FROM from_statement;
INSERT OVERWRITE|INTO TABLE tablename [PARTITION (partcol1=val1...]
select_statement
FROM from_statement;

下面对当中关键的声明语句做解释:

  • into|overwrite:into-直接向表或表的分区中追加数据;先清空表中的原有数据,再向表或分区中插入数据。
  • [PARTITION (partcol1=val1…]:不允许使用函数等表达式,只能是常量。

关于 PARTITION 这里展开说明指定分区插入和动态分区插入

  • 输出到指定分区:在INSERT语句中直接指定分区值,将数据插入指定的分区。
  • 输出到动态分区:在INSERT语句中不直接指定分区值,只指定分区列名。分区列的值在SELECT子句中提供,系统自动根据分区字段的值将数据插入到相应分区。

HIVE SQL优化

列裁剪

例如某表有a,b,c,d,e五个字段,但是我们只需要a和b,那么请用select a,b from table 而不是select * from table

分区裁剪

在查询的过程中减少不必要的分区,即尽量指定分区

小表放前大表放后

在编写带有join的代码语句时,应该将条目少的表/子查询放在join操作符的前面

因为在Reduce阶段,位于join操作符左边的表会先被加载到内存,载入条目较少的表可以有效的防止内存溢出(OOM)。所以对于同一个key来说,对应的value值小的放前面,大的放后面

尽量避免使用distinct

尽量避免使用distinct进行重排,特别是大表,容易产生数据倾斜(key一样在一个reduce处理)。使用group by替代:

select distinct key from a
select key from a group by key
select distinct key from a
select key from a group by key

参考链接:


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK