【Hive】Hive 的基本认识

2020-07-21 11:21:29 浏览数 (1)

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 语句;
代码语言:javascript复制
hive -e "select * from teacher;"
  • "-f" 表示执行 SQL 语句的脚本(方便用 crontab 进行定时调度);
代码语言:javascript复制
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 可以制定分区;
代码语言:javascript复制
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

但是一般不这么玩,会显示很多信息。

通常配置文件有三种方式:

  1. 配置文件方式: 默认配置文件:hive-default.xml 用户自定义配置文件:hive-site.xml 注意:用户自定义配置会覆盖默认配置。另外,Hive 也会读入 Hadoop 的配置,因为 Hive 是作为 Hadoop 的客户端启动的,Hive 的配置会覆盖 Hadoop 的配置。配置文件的设定对本机启动的所有 Hive 进程都有效。
  2. 命令行参数方式: 启动 Hive 时,可以在命令行添加 -hiveconf param=value 来设定参数。比如 # 设置 reduce 个数 > hive -hiveconf mapred.reduce.tasks=10; 这样设置是仅对本次 hive 启动有效。
  3. 参数声明方式 可以在 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 类似,它封装了一个命名字段集合,复杂数据类型允许任意层次的嵌套。

案例实操:

  1. 假设某表有如下一行,我们用 JSON 格式来表示其数据结构。在 Hive 下访问的格式为:
代码语言:javascript复制
{
  "name": "songsong",
  "friends": ["bingbing" , "lili"] , //列表 Array, 
  "children": {   //键值 Map,
    "xiao song": 18 ,
    "xiaoxiao song": 19 
  },
  "address": {  //结构 Struct,
    "street": "hui long guan" ,
    "city": "beijing" 
  }
}
  1. 基于上述数据结构,我们在 Hive 里创建对应的表,并导入数据。创建本地测试文件 text.txt:
代码语言:javascript复制
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 里的元素间关系都可以用同一个字符表示,这里用“_”。

  1. Hive 上创建测试表 test:
代码语言:javascript复制
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':行分隔符。
  1. 导入文本数据到测试表中:
代码语言:javascript复制
hive (default)> load data local inpath '/Users/chenze/Desktop/test.txt' into table test;
  1. 访问三种集合列里的数据:

先查看下数据:

代码语言: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 操作。

  1. 隐式类型转换规则如下
    1. 任何整数类型都可以隐式地转换为一个范围更广的类型,如 TINYINT 可以转换 成 INT,INT 可以转换成 BIGINT;
    2. 所有整数类型、FLOAT 和 STRING 类型都可以隐式地转换成 DOUBLE;
    3. TINYINT、SMALLINT、INT 都可以转换为 FLOAT;
    4. BOOLEAN 类型不可以转换为任何其它的类型。
  2. 可以使用 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

  1. 尚硅谷Hive教程(新版hive框架详解)
  2. Hive学习之路 (一)Hive初识
  3. Hive内部表与外部表的区别

0 人点赞