1.Hive 简介
1.1 简介
我们知道大数据主要解决海量数据的三大问题:「传输问题、存储问题、计算问题」。
而 Hive 主要解决「存储和计算问题」。
Hive 是由 Facebook 开源的基于 Hadoop 的数据仓库工具,用于解决海量「结构化日志」的数据统计。
Hive 存储的数据是在 hdfs 上的,但它可以将结构化的数据文件映射为一张表,并提供类 SQL 的查询功能。(我们称之为 Hive-SQL,简称 HQL)
简单来说,Hive 是在 Hadoop 上「封装了一层 HQL 的接口」,这样开发人员和数据分析人员就可以使用 HQL 来进行数据的分析,而无需关注底层的 MapReduce 的编程开发。
所以 Hive 的本质是「将 HQL 转换成 MapReduce 程序」。
1.2 优缺点
1.2.1 优点
- Hive 封装了一层接口,并提供类 SQL 的查询功能,避免去写 MapReduce,减少了开发人员的学习成本;
- Hive 支持用户自定义函数,可以根据自己的需求来实现自己的函数;
- 适合处理大数据:;
- 可扩展性强:可以自由扩展集群的规模,不需要重启服务而进行横向扩展;
- 容错性强:可以保障即使有节点出现问题,SQL 语句也可以完成执行;
1.2.2 缺点
- Hive 不支持记录级别的增删改操作,但是可以通过查询创建新表来将结果导入到文件中;(hive 2.3.2 版本支持记录级别的插入操作)
- Hive 延迟较高,不适用于实时分析;
- Hive 不支持事物,因为没有增删改,所以主要用来做 OLAP(联机分析处理),而不是 OLTP(联机事物处理);
- Hive 自动生成的 MapReduce 作业,通常情况下不够智能。
1.3 架构原理
放上一张很经典的 Hive 架构图:
如上图所示:
- Hive 提供了 CLI(hive shell)、JDBC/ODBC(Java 访问 hive)、WeibGUI 接口(浏览器访问 hive);
- Hive 中有一个元数据存储(Metastore),通常是存储在关系数据库中如 MySQL、Derby 等。元数据包括表名、表所在数据库、表的列名、分区及属性、表的属性、表的数据所在的目录等;
- Thrift Server 为 Facebook 开发的一个软件框架,可以用来进行可扩展且跨语言的服务开发,Hive 通过集成了该服务能够让不同编程语言调用 Hive 的接口;
- Hadoop 使用 HDFS 进行存储,并使用 MapReduce 进行计算;
- Diver 中包含解释器(Interpreter)、编译器(Compiler)、优化器(Optimizer)和执行器(Executor):
- 「解释器」:利用第三方工具将 HQL 查询语句转换成抽象语法树 AST,并对 AST 进行语法分析,比如说表是否存在、字段是否存在、SQL 语义是否有误;
- 「编译器」:将 AST 编译生成逻辑执行计划;
- 「优化器」:多逻辑执行单元进行优化;
- 「执行器」:把逻辑执行单元转换成可以运行的物理计划,如 MapReduce、Spark。
所以 Hive 查询的大致流程为:通过用户交互接口接收到 HQL 的指令后,经过 Driver 结合元数据进行类型检测和语法分析,并生成一个逻辑方法,通过进行优化后生成 MapReduce,并提交到 Hadoop 中执行,并把执行的结果返回给用户交互接口。
1.4 与 RDBMS 的比较
Hive 采用类 SQL 的查询语句,所以很容易将 Hive 与关系型数据库(RDBMS)进行对比。但其实 Hive 除了拥有类似 SQL 的查询语句外,再无类似之处。我们需要明白的是:数据库可以用做 online 应用;而 Hive 是为数据仓库设计的。
Hive | RDBMS | |
---|---|---|
查询语言 | HQL | SQL |
数据存储 | HDFS | 本地文件系统中 |
数据更新 | 读多写少(不建议改写) | 增删改查 |
数据操作 | 覆盖追加 | 行级别更新删除 |
索引 | 0.8 版本后引入 bitmap 索引 | 建立索引 |
执行 | MapReduce | Executor |
执行延迟 | 延迟较高 | 延迟较低 |
可扩展性 | 可扩展性高 | 可扩展性低 |
数据规模 | 很大 | 较小 |
分区 | 支持 | 支持 |
总的来说,Hive 只具备 SQL 的外表,但应用场景完全不同。Hive 只适合用来做海量离线数据统计分析,也就是数据仓库。清楚这一点,有助于从应用角度理解 Hive 的特性。
2.Hive 基本操作
2.1 Hive 常用命令
在终端输入 hive -help 会出现:
代码语言:javascript复制usage: hive
-d,--define <key=value> Variable substitution to apply to Hive
commands. e.g. -d A=B or --define A=B
--database <databasename> Specify the database to use
-e <quoted-query-string> SQL from command line
-f <filename> SQL from files
-H,--help Print help information
--hiveconf <property=value> Use value for given property
--hivevar <key=value> Variable substitution to apply to Hive
commands. e.g. --hivevar A=B
-i <filename> Initialization SQL file
-S,--silent Silent mode in interactive shell
-v,--verbose Verbose mode (echo executed SQL to the
console)
常用的两个命令是 "-e" 和 "-f":
- "-e" 表示不进入 hive cli 直接执行 SQL 语句;
hive -e "select * from teacher;"
- "-f" 表示执行 SQL 语句的脚本(方便用 crontab 进行定时调度);
hive -f /opt/module/datas/hivef.sql
2.2 本地文件导入 Hive 表中
首先需要创建一张表:
代码语言:javascript复制create table student(
id int,
name string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY 't';
简单介绍下字段:
- ROW FORMAT DELIMITED:分隔符的设置的开始语句;
- FIELDS TERMINATED BY:设置每一行字段与字段之间的分隔符,我们这是用 't' 进行划分;
除此之外,还有其他的分割符设定:
- COLLECTION ITEMS TERMINATED BY:设置一个复杂类型(array/struct)字段的各个 item 之间的分隔符;
- MAP KEYS TERMINATED BY:设置一个复杂类型(Map)字段的 key value 之间的分隔符;
- LINES TERMINATED BY:设置行与行之间的分隔符;
这里需要注意的是 ROW FORMAT DELIMITED 必须在其它分隔设置之前;LINES TERMINATED BY 必须在其它分隔设置之后,否则会报错。
然后,我们需要准备一个文件:
代码语言:javascript复制# stu.txt
1 Xiao_ming
2 xiao_hong
3 xiao_hao
需要注意,每行内的字段需要用 't' 进行分割。
接着需要使用 load 语法加载本地文件,load 语法为:
代码语言:javascript复制load data [local] inpath 'filepath' [overwrite] into table tablename [partition (partcol1=val1,partcol2=val2...)]
- local 用来控制选择本地文件还是 hdfs 文件;
- overwrite 可以选择是否覆盖原来数据;
- partition 可以制定分区;
hive> load data local inpath '/Users/***/Desktop/stu1.txt' into table student;
最后查看下数据:
代码语言:javascript复制hive> select * from student;
OK
1 Xiao_ming
2 xiao_hong
3 xiao_hao
Time taken: 1.373 seconds, Fetched: 3 row(s)
2.3 Hive 其他操作
- quit:不提交数据退出;
- exit:先隐性提交数据,再退出。
不过这种区别只是在旧版本中有,两者在新版本已经没有区别了。
在 hive cli 中可以用以下命令查看 hdfs 文件系统和本地文件系统:
代码语言:javascript复制dfs -ls /; # 查看 hdfs 文件系统
! ls ./; # 查看本地文件系统
用户根目录下有一个隐藏文件记录着 hive 输入的所有历史命令:
代码语言:javascript复制cat ./hivehistory
注意:hive 语句不区分大小写。
3.Hive 常见属性配置
3.1 数据仓库位置
Default 的数据仓库原始位置是在 hdfs 上的:/user/hive/warehoues 路径下。如果某张表属于 Default 数据库,那么会直接在数据仓库目录创建一个文件夹。
我们以刚刚创建的表为例,来查询其所在集群位置:
代码语言:javascript复制hive> desc formatted student;
OK
# col_name data_type comment
id int
name string
# Detailed Table Information
Database: default
OwnerType: USER
Owner: **
CreateTime: Fri Jul 17 08:59:14 CST 2020
LastAccessTime: UNKNOWN
Retention: 0
Location: hdfs://localhost:9000/user/hive/warehouse/student
Table Type: MANAGED_TABLE
Table Parameters:
bucketing_version 2
numFiles 1
numRows 0
rawDataSize 0
totalSize 34
transient_lastDdlTime 1594948899
# Storage Information
SerDe Library: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
InputFormat: org.apache.hadoop.mapred.TextInputFormat
OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Compressed: No
Num Buckets: -1
Bucket Columns: []
Sort Columns: []
Storage Desc Params:
field.delim t
serialization.format t
Time taken: 0.099 seconds, Fetched: 32 row(s)
可以看到,Table Information 里面有一个 Location,表示当前表所在的位置,因为 student 是 Default 数据仓库的,所以会在 '/user/hive/warehouse/' 路径下。
如果我们想要修改 Default 数据仓库的原始位置,需要在 hive-site.xml(可以来自 hive-default.xml.template)文件下加入如下配置信息,并修改其 value:
代码语言:javascript复制<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
<description>location of default database for the warehouse</description>
</property>
同时也需要给修改的路径配置相应的权限:
代码语言:javascript复制hdfs dfs -chmod g w /user/hive/warehouse
3.2 查询信息显示配置
我们可以在 hive-site.xml 中配置如下信息,便可以实现显示当前数据库以及查询表的头信息:
代码语言:javascript复制<property>
<name>hive.cli.print.header</name>
<value>true</value>
</property>
<property>
<name>hive.cli.print.current.db</name>
<value>true</value>
</property>
当然我们也可以通过 set 命令来设置:
代码语言:javascript复制set hive.cli.print.header=true; # 显示表头
set hive.cli.print.current.db=true; # 显示当前数据库
看下前后的对比:
代码语言:javascript复制# 前
hive> select * from studenT;
OK
1 Xiao_ming
2 xiao_hong
3 xiao_hao
Time taken: 0.231 seconds, Fetched: 3 row(s)
# 后
hive (default)> select * from student;
OK
student.id student.name
1 Xiao_ming
2 xiao_hong
3 xiao_hao
Time taken: 0.202 seconds, Fetched: 3 row(s)
3.3 参数配置方式
可以用 set 查看当前所有参数配置信息:
代码语言:javascript复制hive> set
但是一般不这么玩,会显示很多信息。
通常配置文件有三种方式:
- 配置文件方式: 默认配置文件:hive-default.xml 用户自定义配置文件:hive-site.xml 注意:用户自定义配置会覆盖默认配置。另外,Hive 也会读入 Hadoop 的配置,因为 Hive 是作为 Hadoop 的客户端启动的,Hive 的配置会覆盖 Hadoop 的配置。配置文件的设定对本机启动的所有 Hive 进程都有效。
- 命令行参数方式: 启动 Hive 时,可以在命令行添加 -hiveconf param=value 来设定参数。比如 # 设置 reduce 个数 > hive -hiveconf mapred.reduce.tasks=10; 这样设置是仅对本次 hive 启动有效。
- 参数声明方式 可以在 hive cli 中通过 set 关键字设定参数: hive (default)> set mapred.reduce.tasks=100; 这样设置也是仅对本次 hive 启动有效。
上述三种设定方式的优先级依次递增。即配置文件<命令行参数<参数声明。注意某些系统级的参数,例如 log4j 相关的设定,必须用前两种方式设定,因为那些参数的读取在会话建立以前已经完成了。
4.Hive 数据类型
4.1 基本数据类型
Hive 数据类型 | Java 数据类型 | 长度 |
---|---|---|
TINYINT | byte | 1 byte 有符号整数 |
SMALINT | short | 2byte 有符号整数 |
INT | int | 4byte 有符号整数 |
BIGINT | long | 8byte 有符号整数 |
BOOLEAN | boolean | 布尔类型,true 或者 false |
FLOAT | float | 单精度浮点数 |
DOUBLE | double | 双精度浮点数 |
STRING | string | 字符系列。可以指定字符集。可以使用单引号或者双引号。 |
TIMESTAMP | 时间类型 | |
BINARY | 字节数组 |
Hive 的 String 类型相当于数据库的 varchar 类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数。
4.2 集合数据类型
数据类型 | 描述 | 语法示例 |
---|---|---|
STRUCT | 和 c 语言中的 struct 类似,都可以通过“点”符号访问元素内容。例如,如果某个列的数据类型是 STRUCT{first STRING, last STRING},那么第 1 个元素可以通过字段.first 来引用。 | struct() |
MAP | MAP 是一组键-值对元组集合,使用数组表示法可以访问数据。例如,如果某个列的数据类型是 MAP,其中键->值对 是 ’first’->’John’ 和 ’last’->’Doe’,那么可以通过字段名 [‘last’] 获取最后一个元素。 | map() |
ARRAY | 数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为 [‘John’, ‘Doe’], 那么第 2 个元素可以通过数组名 [1] 进行引用。 | Array() |
Hive 有三种复杂数据类型 ARRAY、MAP、STRUCT。ARRAY 和 MAP 与 Java 中的 Array 和 Map 类似,而 STRUCT 与 C 语言中的 Struct 类似,它封装了一个命名字段集合,复杂数据类型允许任意层次的嵌套。
案例实操:
- 假设某表有如下一行,我们用 JSON 格式来表示其数据结构。在 Hive 下访问的格式为:
{
"name": "songsong",
"friends": ["bingbing" , "lili"] , //列表 Array,
"children": { //键值 Map,
"xiao song": 18 ,
"xiaoxiao song": 19
},
"address": { //结构 Struct,
"street": "hui long guan" ,
"city": "beijing"
}
}
- 基于上述数据结构,我们在 Hive 里创建对应的表,并导入数据。创建本地测试文件 text.txt:
songsong,bingbing_lili,xiao song:18_xiaoxiao song:19,hui long guan_beijing yangyang,caicai_susu,xiao yang:18_xiaoxiao yang:19,chao yang_beijing
注意:MAP,STRUCT 和 ARRAY 里的元素间关系都可以用同一个字符表示,这里用“_”。
- Hive 上创建测试表 test:
create table test(
name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>
)
row format delimited
fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by 'n';
字段解释:
- row format delimited fields terminated by ',':列分隔符;
- collection items terminated by '_':MAP STRUCT 和 ARRAY 的分隔符(数据分割符号);
- map keys terminated by ':':MAP 中的 key 与 value 的分隔符;
- lines terminated by 'n':行分隔符。
- 导入文本数据到测试表中:
hive (default)> load data local inpath '/Users/chenze/Desktop/test.txt' into table test;
- 访问三种集合列里的数据:
先查看下数据:
代码语言:javascript复制hive (default)> select * from test;
OK
test.name test.friends test.children test.address
songsong ["bingbing","lili"] {"xiao song":18,"xiaoxiao song":19} {"street":"hui long guan","city":"beijing yangyang"}
Time taken: 0.113 seconds, Fetched: 1 row(s)
查看 ARRAY,MAP,STRUCT 的访问方式:
代码语言:javascript复制hive (default)> select friends[1],children['xiao song'],address.city from test where name="songsong";
OK
_c0 _c1 city
lili 18 beijing yangyang
Time taken: 0.527 seconds, Fetched: 1 row(s)
4.3 数据类型转化
Hive 的原子数据类型是可以进行隐式转换的,类似于 Java 的类型转换,例如某表达式使用 INT 类型,TINYINT 会自动转换为 INT 类型,但是 Hive 不会进行反向转化,例如,某表达式使用 TINYINT 类型,INT 不会自动转换为 TINYINT 类型,它会返回错误,除非使用 CAST 操作。
- 隐式类型转换规则如下
- 任何整数类型都可以隐式地转换为一个范围更广的类型,如 TINYINT 可以转换 成 INT,INT 可以转换成 BIGINT;
- 所有整数类型、FLOAT 和 STRING 类型都可以隐式地转换成 DOUBLE;
- TINYINT、SMALLINT、INT 都可以转换为 FLOAT;
- BOOLEAN 类型不可以转换为任何其它的类型。
- 可以使用 CAST 操作显示进行数据类型转换
例如 CAST('1' AS INT) 将把字符串 '1' 转换成整数 1;如果强制类型转换失败,如执行 CAST('X' AS INT),表达式返回空值 NULL。
5.数据组织
1、Hive 的存储结构包括「数据库、表、视图、分区和表数据」等。数据库,表,分区等等都对 应 HDFS 上的一个目录。表数据对应 HDFS 对应目录下的文件。
2、Hive 中所有的数据都存储在 HDFS 中,没有专门的数据存储格式,因为 「Hive 是读模式」 (Schema On Read),可支持 TextFile,SequenceFile,RCFile 或者自定义格式等。
- 「TextFile」:默认格式,存储方式为行存储。数据不做压缩,磁盘开销大,数据解析开销大;
- 「SequenceFile」:Hadoop API 提供的一种二进制文件支持,其具有使用方便、可分割、可压缩的特点。SequenceFile 支持三种压缩选择:NONE, RECORD, BLOCK。Record 压缩率低,一般建议使用 BLOCK 压缩;
- 「RCFile」:一种行列存储相结合的存储方式;
- 「ORCFile」:数据按照行分块,每个块按照列存储,其中每个块都存储有一个索引。Hive 给出的新格式,属于 RCFILE 的升级版,性能有大幅度提升,而且数据可以压缩存储,压缩快,且可以快速列存取;
- 「Parquet」:一种行式存储,同时具有很好的压缩性能;同时可以减少大量的表扫描和反序列化的时间。
3、 只需要在创建表的时候告诉 Hive 数据中的「列分隔符和行分隔符」,Hive 就可以解析数据
- Hive 的默认列分隔符:控制符 「Ctrl A,x01 Hive」 的;
- Hive 的默认行分隔符:换行符 「n」。
4、Hive 中包含以下数据模型:
- 「database」:在 HDFS 中表现为${hive.metastore.warehouse.dir}目录下一个文件夹;
- 「table」:在 HDFS 中表现所属 database 目录下一个文件夹;
- 「external table」:与 table 类似,不过其数据存放位置可以指定任意 HDFS 目录路径;
- 「partition」:在 HDFS 中表现为 table 目录下的子目录;
- 「bucket」:在 HDFS 中表现为同一个表目录或者分区目录下根据某个字段的值进行 hash 散列之后的多个文件;
- 「view」:与传统数据库类似,只读,基于基本表创建
5、Hive 的元数据存储在 RDBMS 中,除元数据外的其它所有数据都基于 HDFS 存储。默认情 况下,Hive 元数据保存在内嵌的 Derby 数据库中,只能允许一个会话连接,只适合简单的 测试。实际生产环境中不适用,为了支持多用户会话,则需要一个独立的元数据库,使用 MySQL 作为元数据库,Hive 内部对 MySQL 提供了很好的支持。
6、Hive 中的表分为内部表、外部表、分区表和 Bucket 表
- 「内部表和外部表的区别:」
- 创建内部表时,会将数据移动到数据仓库指向的路径;创建外部表时,仅记录数据所在路径,不对数据的位置做出改变;
- 删除内部表时,删除表元数据和数据**;**删除外部表时,删除元数据,不删除数据。所以外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据;
- 内部表数据由 Hive 自身管理,外部表数据由 HDFS 管理;
- 未被 external 修饰的是内部表,被 external 修饰的为外部表;
- 对内部表的修改会直接同步到元数据,而对外部表的表结构和分区进行修改,则需要修改 'MSCK REPAIR TABLE [table_name]'。
- 「内部表和外部表的使用选择:」
- 大多数情况,他们的区别不明显,如果数据的所有处理都在 Hive 中进行,那么倾向于选择内部表;但是如果 Hive 和其他工具要针对相同的数据集进行处理,外部表更合适;
- 使用外部表访问存储在 HDFS 上的初始数据,然后通过 Hive 转换数据并存到内部表中;
- 使用外部表的场景是针对一个数据集有多个不同的 Schema;
- 通过外部表和内部表的区别和使用选择的对比可以看出来,hive 其实仅仅只是对存储在 HDFS 上的数据提供了一种新的抽象,而不是管理存储在 HDFS 上的数据。所以不管创建内部表还是外部表,都可以对 hive 表的数据存储目录中的数据进行增删操作。
使用外部表的场景是针对一个数据集有多个不同的 Schema
通过外部表和内部表的区别和使用选择的对比可以看出来,hive 其实仅仅只是对存储在 HDFS 上的数据提供了一种新的抽象。而不是管理存储在 HDFS 上的数据。所以不管创建内部 表还是外部表,都可以对 hive 表的数据存储目录中的数据进行增删操作。
- 「分区表和分桶表的区别:」
- Hive 数据表可以根据某些字段进行分区操作,细化数据管理,可以让部分查询更快。同时表和分区也可以进一步被划分为 Buckets,分桶表的原理和 MapReduce 编程中的 HashPartitioner 的原理类似;
- 分区和分桶都是细化数据管理,但是分区表是手动添加区分,由于 Hive 是读模式,所以对添加进分区的数据不做模式校验,分桶表中的数据是按照某些分桶字段进行 hash 散列形成的多个文件,所以数据的准确性也高很多。
6.Reference
- 尚硅谷Hive教程(新版hive框架详解)
- Hive学习之路 (一)Hive初识
- Hive内部表与外部表的区别