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 会出现:
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 表中
首先需要创建一张表:
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 必须在其它分隔设置之后,否则会报错。
然后,我们需要准备一个文件:
#?stu.txt
1?Xiao_ming
2?xiao_hong
3?xiao_hao
需要注意,每行内的字段需要用 ‘\t‘ 进行分割。
接着需要使用 load 语法加载本地文件,load 语法为:
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;
最后查看下数据:
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 文件系统和本地文件系统:
dfs?-ls?/;??#?查看?hdfs?文件系统
!?ls?./;??#?查看本地文件系统
用户根目录下有一个隐藏文件记录着 hive 输入的所有历史命令:
cat?./hivehistory
注意:hive 语句不区分大小写。
3.Hive 常见属性配置3.1 数据仓库位置
Default 的数据仓库原始位置是在 hdfs 上的:/user/hive/warehoues 路径下。如果某张表属于 Default 数据库,那么会直接在数据仓库目录创建一个文件夹。
我们以刚刚创建的表为例,来查询其所在集群位置:
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:
<property>?
??<name>hive.metastore.warehouse.dir</name>?
??<value>/user/hive/warehouse</value>?
??<description>location?of?default?database?for?the?warehouse</description>?
</property>
同时也需要给修改的路径配置相应的权限:
hdfs?dfs?-chmod?g+w?/user/hive/warehouse
3.2 查询信息显示配置
我们可以在 hive-site.xml 中配置如下信息,便可以实现显示当前数据库以及查询表的头信息:
<property>?
??<name>hive.cli.print.header</name>?
??<value>true</value>?
</property>
<property>?
??<name>hive.cli.print.current.db</name>?
??<value>true</value>?
</property>
当然我们也可以通过 set 命令来设置:
set?hive.cli.print.header=true;??#?显示表头
set?hive.cli.print.current.db=true;??#?显示当前数据库
看下前后的对比:
#?前
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 查看当前所有参数配置信息:
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;
- 访问三种集合列里的数据:
先查看下数据:
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 的访问方式:
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 散列形成的多个文件,所以数据的准确性也高很多。
- 尚硅谷Hive教程(新版hive框架详解)
- Hive学习之路 (一)Hive初识
- Hive内部表与外部表的区别
?