# Hive 数据类型操作
# Hive 内部表操作
# 针对基本类型建表
首先,在 master 机器的 /export/data
目录下创建 hivedata 目录,在改文件夹下创建 user.txt
,并添加如下数据内容:
1 | 1,allen,18 |
具体操作如下:
1 | [root@master ~]# mkdir -p /export/data/hivedata |
启动 hadoop 和 hive, 具体如下操作
1 | [root@master data]# sh /usr/local/hadoop-2.6.5/sbin/start-all.sh |
在 hive 界面输入 set hive.cli.print.current.db=true
设置显示当前使用数据库,然后创建 zjdf
数据库,创建数据库后可以使用 show databases
命令进行查看,如果已经存在,可以使用 drop database database_name cascade
命令强制性删除,如果有表格,一定要添加 cascade
参数,用 use
切换到这个新建的数据库,并创建内部表 t_user
, 具体操作如下:
1 | hive> set hive.cli.print.current.db=true; |
针对 hivedata 目录准备的结构化文件 user.txt 先创建一个内部表 t_user,具体示例如下:
1 | hive (zjdf)> create table t_user(id int,name string,age int) ROW FORMAT DELIMITED FIELDS TERMINATED BY ','; |
创建成功后,通过 WEB UI 界面打开 Hive 内部表所在 HDFS 路径(默认 /user/hive/warehouse/zidj.db/t_user)进行查看,如下图所示。
复制 master 终端,然后点击选项卡排列,可以达到左右分屏的效果,如下所示:
将准备好的 user.txt 移动到 hadoop 对应的 t_user 表的路径中,具体操作如下:
1 | [root@master hivedata]# hadoop fs -put user.txt /user/hive/warehouse/zjdf.db/t_user |
刷新 Hadoop 文件系统,如下图所示:
在 hive 界面中查询 t_user 表数据,发现表中已经存在数据,具体命令如下:
1 | hive (zjdf)> select * from t_user; |
# 针对复杂类型建表
例如,现有结构化数据文件 student.txt,文件内容如下所示。
1 | 1,zhangsan,唱歌:非常喜欢-跳舞:喜欢-游泳:一般般 |
通过对 student.txt 文件内容分析得出,可以设计为 3 列字段,即编号、姓名、兴趣,其中编号可以为 int 类型,姓名可以为 string 类型,而兴趣列还需要进一步分隔为 Map 类型,因此在创建 student.txt 文件对应的内部表语句如下所示:
1 | hive (zjdf)> create table t_student(id int,name string,hobby map<string,string>) row format delimited fields terminated by ',' collection items terminated by '-' map keys terminated by ':'; |
上述建表语句中,通过对 student.txt 文件结构化文件的分析,先通过逗号 “,” 对多个字段 fields 进行分隔;接着,针对 hobby 字段列,通过横线 “-” 进行集合列分隔;最后,再针对每一个爱好,通过冒号 “:” 进行分隔为最终的 “key:value” 形式。执行上述建表语句后,就会在默认的 /user/hive/warehouse/zjdf.db 文件夹下生成一个 t_student 文件夹。如下图所示:
此时,还必须将前面的结构化文件 student.txt 上传到该文件夹下进行映射,才能生成对应的内部表数据,上传完成后再次查询生成的 t_student 表信息。
1 | [root@master hivedata]# vi student.txt |
再次在 hive 中查询 t_student 数据表格,如下图所示:
1 | hive (zjdf)> select * from t_student; |
# Hive 外部表操作
内部表,即不添加关键字 External,内部表与结构化数据文件要想产生关系映射,那么数据文件就必须在指定的内部表文件夹下,而当遇到大文件的情况时,移动数据文件非常耗时,这就需要我们来创建外部表,因为它不需要移动结构化数据文件。下面我们通过一个小案例来,对外部表进行讲解。
现有结构化数据文件 student.txt,且数据内容如文件所示。
1 | 95001,李勇,男,20,CS |
首先,我们将 student.txt 文件上传至 HDFS 上的 /stu 路径下,用来模拟生产环境下的数据文件,具体命令如下所示:
1 | [root@master ~]# vi student.txt |
在 hive 中创建外部表
1 | hive (zjdf)> create external table student_ext(Sno int,Sname string,Sex string,Sage int,Sdept string) row format delimited fields terminated by ',' location '/stu'; |
从结果发现,student_ext 外部表已经创建成功,最后,HQL(HiveQL)对数据表的内容的查看、增加、删除以及修改的语句均与 SQL 语句一致。下面以查看数据表内容为例进行演示,具体语法如下所示:
1 | hive (zjdf)> select * from student_ext; |
结果如下
小提示:Hive 创建内部表时,会将数据移动到数据库指向的路径;创建外部表时,仅记录数据所在的路径,不会对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据
# Hive 分区表操作
分区表是按照属性在文件夹层面给文件更好的管理,实际上就是对应一个 HDFS 文件系统上的独立文件夹,该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询指定的分区,这样的查询效率会提高很多。Hive 分区表一共有两种,分别为普通分区和动态分区,我们下面就要对其分别进行介绍。
# Hive 普通分区
创建分区表分为两种,一种是单分区,也就是说在表文件夹目录下只有一级文件夹目录。另外一种是多分区,表文件夹下出现多文件夹嵌套模式,现在我们只针对单分区进行详解,若想学习多分区可以参考官网的官方文档。
现有结构化数据文件 user_p.txt,文件中的数据内容如文件所示。
1 | 1,allen |
1 | [root@master ~]# mkdir -p /export/data/hivedata/ |
首先,创建分区表。语法格式如下所示:
1 | hive (zjdf)> create table t_user_p(id int, name string) partitioned by (country string) row format delimited fields terminated by ','; |
其次,加载数据是将数据文件移动到与 Hive 表对应的位置,从本地(Linux)复制或移动到 HDFS 文件系统的操作。由于分区表在映射数据时不能使用 Hadoop 命令移动文件,需要使用 Load 命令,其语法格式如下所示:
1 | LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] |
Load Data 是 HQL 固定的数据装载语句,下面针对部分关键字进行讲解。
- Filepath:它可以引用一个文件(在这种情况下,Hive 将文件移动到表所对应的目录中),或者它可以是一个目录(在这种情况下,Hive 将把该目录中的所有文件移动到表所对应的目录中)。它可以是相对路径、绝对路径以及完整的 URI。
- Local:如果指定了 Local 键字,Load 命令将在本地文件系统(Hive 服务启动方)中查找文件路径,将其复制到对应的 HDFS 路径下;如果没有指定 Local 关键字,它将会从 HDFS 中移动数据文件至对应的表路径下。
- Overwrite:如果使用了 Overwrite 关键字,当加载数据时目标表或分区中的内容会被删除,然后再将 filepath 指向的文件或目录中的内容添加到表或分区中。简单的说就是覆盖表中已有数据;若不添加该关键字,则表示追加数据内容。
加载数据操作的语法格式如下所示:
1 | hive (zjdf)> load data local inpath '/export/data/hivedata/user_p.txt' into table t_user_p partition(country='USA'); |
从上述语句看出,Load Data 表示装载数据,Inpath 表示数据文件所在的本地系统路径,partition(country=‘USA’)为指定的分区,它需要与建表时设置的分区字段保持一致。执行完上述命令后,查看表内容的数据,效果如图所示:
1 | hive (zjdf)> select * from t_user_p; |
# Hive 动态分区
上面介绍了 Hive 普通分区的创建和 Load 命令加载数据的操作。在默认情况下,我们加载数据时,需要手动的设置分区字段,并且针对一个分区就要写一个插入语句。如果源数据量很大时(例如,现有许多日志文件,要求按照日期作为分区字段,在插入数据的时候无法手动的添加分区),就可以利用 Hive 提供的动态分区,可以简化插入数据时的繁琐操作,若想实现动态分区,则需要开启动态分区功能,具体命令如下所示:
1 | hive> set hive.exec.dynamic.partition=true; |
Hive 默认是不支持动态分区的,因此 hive.exec.dynamic.partition 默认值为 false,需要启动动态分区功能,可以将该参数设置为 true;其中 hive.exec.dynamic.partition.mode 的默认值是 strict,表示必须指定至少一个分区为静态分区,将此参数修改为 nonstrict,表示允许所有的分区字段都可以使用动态分区。
在 Hive 中 insert 语句是用于动态插入数据的,不同的是它主要是结合 select 查询语句使用,且非常适用于动态分区插入数据,语法格式如下所示:
1 | hive> insert overwrite table table_name |
现有原始表的结构化数据文件 dynamic_partition_table.txt,内容数据如文件所示。
1 | 2018-05-10,ip1 |
1 | [root@master hivedata]# vi dynamic_partition_table.txt |
现在我们通过一个案例进行演示动态分区的数据插入操作。将 dynamic_partition_table 中的数据按照时间(day),插入到目标表 d_p_t 的相应分区中。
首先,创建原始表。语法格式如下所示:
1 | hive (zjdf)> create table dynamic_partition_table(day string,ip string) row format delimited fields terminated by ","; |
其次,加载数据文件至原始表,语法格式如下所示:
1 | hive (zjdf)> load data local inpath '/export/data/hivedata/dynamic_partition_table.txt' into table dynamic_partition_table; |
再次,创建目标表,语法格式如下所示:
1 | hive (zjdf)> create table d_p_t(ip string) partitioned by (month string,day string); |
依次,动态插入,语法格式如下所示:
1 | hive (zjdf)> insert overwrite table d_p_t partition (month,day) select ip,substr(day,1,7) as month,day from dynamic_partition_table; |
最后,查看目标表中的分区数据,语法格式如下所示:
1 | hive (zjdf)> show partitions d_p_t; |
# Hive 桶表操作
为了将表进行更细粒度的范围划分,我们可以创建桶表。桶表,是根据某个属性字段把数据分成几个桶(我们这里设置为 4,默认值是 - 1,可自定义),也就是在文件的层面上把数据分开。下面通过一个案例进行桶表相关操作的演示。
首先,我们先开启分桶功能,命令如下所示。
1 | hive> set hive.enforce.bucketing = true; |
其次,创建桶表,语法格式如下所示:
1 | hive> create table stu_buck(Sno int,Sname string,Sex string,Sage int,Sdept string) clustered by(Sno) into 4 buckets row format delimited fields terminated by ','; |
执行上述语句后,桶表 stu_buck 创建完成,并且以学生编号(Sno)分为 4 个桶,以 “,” 为分隔符的桶表。
再次,在 HDFS 的 /stu/ 目录下已有结构化数据文件 student.txt,我们需要将 student.txt 文件的复制到 /hivedata 目录下。然后,加载数据到桶表中,由于分桶表加载数据时,不能使用 Load Data 方式导入数据(原因在于该 Load Data 本质上是对数据文件进行复制或移动到 Hive 表所对应的地址中),因此在分桶表导入数据时需要创建临时的 student 表,该表与 stu_buck 表的字段必须一致,语法格式如下所示:
1 | hive> create table student_tmp(Sno int,Sname string,Sex string,Sage int,Sdept string) |
依次,加载数据至 student 表,语法格式如下所示
1 | hive> load data local inpath '/hivedata/student.txt' into table student_tmp; |
最后,将数据导入 stu_buck 表,语法格式如下所示:
1 | hive> insert overwrite table stu_buck select * from student_tmp cluster by(Sno); |
总体来说,分桶表是把表所映射的结构化数据分得更细致,且分桶规则与 MapReduce 分区规则一致,Hive 采用对目标列值进行哈希运算,得到哈希值再与桶个数取模的方式决定数据的归并,从而看出 Hive 与 MapReduce 存在紧密联系。使用分桶可以提高查询效率,例如执行 Join 操作时,两个表有相同的列字段,如果对这两张表都采取了分桶操作,那么就可以减少 Join 操作时的数据量,从而提高查询效率。它还能够在处理大规模数据集时,选择小部分数据集进行抽样运算,从而减少资源浪费。