Hive 是Facebook开源用于解决海量结构化日志的数据统计工具。Hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类 SQL 查询功能。Hive的本质如下,所以Hive比较适合处理大数据,小数据没有优势,时效性也是比较差的,所以Hive适合离线数据分析处理的场景,并且Hive自动生成MapReduce程序,因为效率比较低,同时调优也是相对比较困难。
(1)Hive存储的数据存储在HDFS上
(2)Hive分析数据底层的逻辑实现是MapReduce
(3)执行程序运行在yarn上面
Hive的系统架构可以看成是客户端-驱动器-作业执行器(MapReduce/Spark)-文件存储器(HDFS)构成。
Hive架构:
(1)用户接口,用户访问Hive的方式,主要有以下三种
(2)元数据(Metastore)
元数据包括:表名,数据库的库名(默认是default),表的拥有者,列/分区字段、字段类型、表类型、表所在的目录等表的和数据库的信息等。元数据信息默认存储在自带的derby数据库中,但是这个数据库不支持多人同时访问,因此一般需要配置使用MySql数据库在来存储元数据(Metastore)
(3)驱动器
驱动器包含解析器(SQL Parser),编译器(Physical Plan),优化器(Query Optimizer)和执行器(Execution)
Hive 通过给用户提供的一系列交互接口,接收到用户的指令(SQL),使用自己的 Driver,结合元数据(MetaStore),将这些指令翻译成 MapReduce作业,提交到Hadoop中执行,最后,将执行返回的结果输出到用户交互接口。
注意:部分数据在元数据的信息里直接查询,不会经过MapReduce程序。
Hive下载地址,安装前需要先下载,请注意对应的版本号:http://archive.apache.org /dist/hive/
Hive安装流程如下:
(1)把 apache-hive-3.1.2-bin.tar.gz 上传到 linux 的/opt/software 目录下
(2)解压 apache-hive-3.1.2-bin.tar.gz 到/opt/module/目录下面
[mrlin@hadoop102 software]$ tar -zxvf /opt/software/apache-hive-3.1.2- bin.tar.gz -C /opt/module/
(3)将 apache-hive-3.1.2-bin的名称修改为 hive,方便后续操作
[mrlin@hadoop102 software]$ mv /opt/module/apache-hive-3.1.2-bin/ /opt/module/hive
(4)修改/etc/profile.d/my_env.sh,添加环境变量
[mrlin@hadoop102 software]$ sudo vim /etc/profile.d/my_env.sh
(5)环境变量后面添加以下内容,添加Hive的环境变量
#HIVE_HOME
export HIVE_HOME=/opt/module/hive
export PATH=$PATH:$HIVE_HOME/bin
(6)解决日志 Jar 包的冲突,修改包名
mv /opt/module/hive/lib/log4j-slf4j-impl-2.10.0.jar /opt/module/hive/lib/log4j-slf4j-impl-2.10.0.bak
(7)初始化元数据库,第一次需要初始化
[mrlin@hadoop102 hive]$ bin/schematool -dbType derby -initSchema
(8)启动hive,启动之前必须先启动HDFS集群
[mrlin@hadoop102 hive]$ bin/hive
因为Hive默认数据库是derby,该数据库仅支持单用户操作,实际工作中都是多用户操作,因此需要修改为多用户操作的mysql。
(1)检查当前系统是否安装过 MySQL,有安装需要先卸载
[mrlin@hadoop102 ~]$ rpm -qa|grep mariadb
mariadb-libs-5.5.56-2.el7.x86_64
# 如果存在通过如下命令卸载
[mrlin@hadoop102 ~]$ sudo rpm -e --nodeps mariadb-libs
(2)将 MySQL 安装包拷贝到/opt/software 目录下
[mrlin @hadoop102 software]# ll
# 总用量 528384
-rw-r--r--. 1 root root 609556480 3 月 21 15:41 mysql-5.7.28- 1.el7.x86_64.rpm-bundle.tar
(3)解压 MySQL 安装包
tar -xf mysql-5.7.28-1.el7.x86_64.rpm- bundle.tar
(4)在安装目录下执行 rpm 安装,安装必须按照顺序安装,因为有依赖关系
[mrlin @hadoop102 software]$
sudo rpm -ivh mysql-community-common-5.7.28-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-libs-5.7.28-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-libs-compat-5.7.28-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-client-5.7.28-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm
(5)删除/etc/my.cnf 文件中 datadir 指向的目录下的所有内容,如果有内容的情况下先查看 datadir 的值:
cat /etc/my.cnf
[mysqld] datadir=/var/lib/mysql
# 删除/var/lib/mysql 目录下的所有内容:
[mrlin @hadoop102 mysql] cd /var/lib/mysql
[mrlin @hadoop102 mysql] sudo rm -rf ./* # 该命令是删除目录下所有文件,注意执行命令的位置,否则会导致误删
(6)初始化mysql数据库
[mrlin @hadoop102 opt]$ sudo mysqld --initialize --user=mysql
(7)查看临时生成的 root 用户的密码
[mrlin @hadoop102 opt]$ sudo cat /var/log/mysqld.log
在最后面的root@localhost:XXXXXXXX,XXX部分即为密码,先拷贝复制
(8)启动 MySQL 服务,首次安装完需要手动启动,之后系统会添加进开机自启动,以后不用再次启动
[mrlin @hadoop102 opt]$ sudo systemctl start mysqld
(9)登录 MySQL 数据库
[mrlin @hadoop102 opt]$ mysql -uroot -p
Enter password: # 输入临时生成的密码,粘贴上面拷贝复制的密码
# 登录成功.
(10)必须先修改 root 用户的密码,否则执行其他的操作会报错,且此处新密码必须与先设置的密码保持一致
mysql > set password = password("新密码");
(11)修改 mysql 库下的 user 表中的 root 用户允许任意 ip 连接,否则的话只能本机连接,实际生产使用具体用户修改,不使用root账户,因为root账户具有最高权限,不安全
# 查看用户表的信息
mysql > select host,user from mysql.user;
# 更新host为%,%为通配符,表示所有都可以
mysql > update mysql.user set host='%' where user='root';
# 刷新配置
mysql > flush privileges;
到这里只是安装了mysql数据库,并且配置了部分信息,现在需要将mysql跟Hive的元数据作连接,才能将Hive连接到MySQL中来。
Hive中元数据的概念,元数据其实就是数据库、数据表和字段信息等,HDFS里面才真正存储数据,它们是分开的,有时候删除了元数据里面的表,但是真正的数据还是存在的。
(1)拷贝驱动,将 MySQL 的 JDBC 驱动拷贝到 Hive 的 lib 目录下
[mrlin@hadoop102 software]$ cp /opt/software/mysql-connector-java- 5.1.37.jar $HIVE_HOME/lib
(2)配置元数据(Metastore)到 MySQL
[mrlin@hadoop102 software]$ vim /opt/module/hive/conf/hive-site.xml
<configuration>
<property>
<name>javax.jdo.option.ConnectionURLname>
<value>jdbc:mysql://hadoop102:3306/metastore?useSSL=falsevalue>
property>
<property>
<name>javax.jdo.option.ConnectionDriverNamename>
<value>com.mysql.jdbc.Drivervalue>
property>
<property>
<name>javax.jdo.option.ConnectionUserNamename>
<value>rootvalue>
property>
<property>
<name>javax.jdo.option.ConnectionPasswordname>
<value>000000value>
property>
<property>
<name>hive.metastore.schema.verificationname>
<value>falsevalue>
property>
<property>
<name>hive.metastore.event.db.notification.api.authname>
<value>falsevalue>
property>
<property>
<name>hive.metastore.warehouse.dirname>
<value>/user/hive/warehousevalue>
property>
configuration>
(2)登陆 MySQL,这里会报密码明文显示的警告,需要去除,只需将密码在下一行输入即可(推荐也是密文比较好)
[mrlin@hadoop102 software]$ mysql -uroot -p000000
(3)新建 Hive 元数据库
mysql> create database metastore;
mysql> quit;
(4) 初始化 Hive 元数据库,schematool命令在/opt/module/hive/bin/目录下的schematool
[mrlin@hadoop102 software]$ schematool -initSchema -dbType mysql -verbose
初始化完成后,此时就可以多用户多窗口访问Hive了。
(1)在 hive-site.xml 文件中添加如下配置信息
<property>
<name>hive.metastore.urisname>
<value>thrift://hadoop102:9083value>
property>
(2)启动 metastore
[mrlin@hadoop202 hive]$ hive --service metastore
2020-04-24 16:58:08: Starting Hive Metastore Server
注意: 启动后窗口不能再操作,需打开一个新的 shell 窗口做别的操作
(3)启动 hive
[mrlin@hadoop202 hive]$ bin/hive
(1)在 hive-site.xml 文件中添加如下配置信息
<property>
<name>hive.server2.thrift.bind.hostname>
<value>hadoop102value>
property>
<property>
<name>hive.server2.thrift.portname>
<value>10000value>
property>
(2)启动 hiveserver2
[mrlin@hadoop102 hive]$ bin/hive --service hiveserver2
(3)启动 beeline 客户端(需要多等待一会)
[mrlin@hadoop102 hive]$ bin/beeline -u jdbc:hive2://hadoop102:10000 -n mrlin
(4)看到如下界面,即为连接成功
Connecting to jdbc:hive2://hadoop102:10000
Connected to: Apache Hive (version 3.1.2)
Driver: Hive JDBC (version 3.1.2)
Transaction isolation: TRANSACTION_REPEATABLE_READ
Beeline version 3.1.2 by Apache Hive
0: jdbc:hive2://hadoop102:10000>
简单使用Hive
(1)启动Hive
[mrlin@hadoop102 hive]$ bin/hive
(2)使用Hive
hive> show databases;
hive> show tables;
hive> create table student (id int);
hive> insert into student values(1);
hive> select * from student ;
(3)另外开启另一个窗口开启 Hive
hive> show databases;
hive> show tables;
hive> select * from aa;
(4)编写hive服务启动脚本
在上面的启动Hive中,我们总是需要在前台开启多个shell窗口,其实这很不方便。最好的方法就是将Hive启动到后台运行。
nohup: 放在命令开头,表示不挂起,也就是关闭终端进程也继续保持运行状态
/dev/null:是 Linux 文件系统中的一个文件,被称为黑洞,所有写入改文件的内容 都会被自动丢弃
2>&1 : 表示将错误重定向到标准输出上
&: 放在命令结尾,表示后台运行
一般会组合使用: nohup [xxx 命令操作]> file 2>&1 &,表示将 xxx 命令运行的结 果输出到 file 中,并保持命令启动的进程在后台运行,如下:
[mrlin@hadoop202 hive]$ nohup hive --service metastore 2>&1 &
[mrlin@hadoop202 hive]$ nohup hive --service hiveserver2 2>&1 &
为了方便使用,也可以通过直接编写脚本的方式来管理服务的启动和开启:
[mrlin@hadoop102 hive]$ vim $HIVE_HOME/bin/hiveservices.sh
# 里面的内容如下:
#!/bin/bash HIVE_LOG_DIR=$HIVE_HOME/logs if [ ! -d $HIVE_LOG_DIR ] then
mkdir -p $HIVE_LOG_DIR
fi
#检查进程是否运行正常,参数 1 为进程名,参数 2 为进程端口
function check_process()
{
pid=$(ps -ef 2>/dev/null | grep -v grep | grep -i $1 | awk '{print$2}')
ppid=$(netstat -nltp 2>/dev/null | grep $2 | awk '{print $7}' | cut - d '/' -f 1)
echo $pid
[[ "$pid" =~ "$ppid" ]] && [ "$ppid" ] && return 0 || return 1
}
function hive_start()
{
metapid=$(check_process HiveMetastore 9083)
cmd="nohup hive --service metastore >$HIVE_LOG_DIR/metastore.log 2>&1 &"
[ -z "$metapid" ] && eval $cmd || echo "Metastroe 服务已启动" server2pid=$(check_process HiveServer2 10000)
cmd="nohup hiveserver2 >$HIVE_LOG_DIR/hiveServer2.log 2>&1 &"
[ -z "$server2pid" ] && eval $cmd || echo "HiveServer2 服务已启动"
}
function hive_stop()
{
metapid=$(check_process HiveMetastore 9083)
[ "$metapid" ] && kill $metapid || echo "Metastore 服务未启动" server2pid=$(check_process HiveServer2 10000)
[ "$server2pid" ] && kill $server2pid || echo "HiveServer2 服务未启动"
}
case $1 in "start")
hive_start
;;
"stop")
hive_stop
;;
"restart")
hive_stop sleep 2 hive_start
;;
"status")
check_process HiveMetastore 9083 >/dev/null && echo "Metastore 服务运行 正常" || echo "Metastore 服务运行异常"
check_process HiveServer2 10000 >/dev/null && echo "HiveServer2 服务运 行正常" || echo "HiveServer2 服务运行异常"
;;
*)
echo Invalid Args!
echo 'Usage: '$(basename $0)' start|stop|restart|status'
;;
esac
再添加执行权限,并在后台启动服务进程:
chmod +x $HIVE_HOME/bin/hiveservices.sh
hiveservices.sh start
[mrliin@hadoop102 hive]$ bin/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)
(1)“-e”不进入 hive 的交互窗口执行 sql 语句
[mrlin@hadoop102 hive]$ bin/hive -e "select id from student;"
(2)“-f”执行脚本中的 sql 语句
[mrlin@hadoop102 datas]$ touch hivef.sql
select *from student;
[mrlin@hadoop102 hive]$ bin/hive -f /opt/module/hive/datas/hivef.sql
[mrlin@hadoop102 hive]$ bin/hive -f /opt/module/hive/datas/hivef.sql > /opt/module/datas/hive_result.txt
(3)退出命令,可以输入以下命令,或者直接ctrl+c
hive(default)>exit;
hive(default)>quit;
(4)在 hive cli 命令窗口中查看 hdfs 文件系统
hive(default)>dfs -ls /;
(5)查看在 hive 中输入的所有历史命令
(1)修改Hive的log默认存放路径
Hive 的 log 默认存放在/tmp/mrlin/hive.log 目录下(当前用户名下),将其修改存放日志到/opt/module/hive/logs目录下
先修改/opt/module/hive/conf/hive-log4j2.properties.template 文件名称为hive-log4j2.properties:
[mrlin@hadoop102 conf]$ pwd
/opt/module/hive/conf
[mrlin@hadoop102 conf]$ mv hive-log4j2.properties.template hive- log4j2.properties
再在 hive-log4j2.properties 文件中修改 log 存放位置
vim /opt/module/hive/conf/hive-log4j2.properties
hive.log.dir=/opt/module/hive/logs
(2)设置打印当前库和表头,默认Hive是不打印数据库的名称和表名称的,在多库多用户的情况下,为了避免混淆,将数据库和表的信息一同打印出来,在 hive-site.xml 中加入如下两个配置:
<property>
<name>hive.cli.print.headername>
<value>truevalue>
property>
<property>
<name>hive.cli.print.current.dbname>
<value>truevalue>
property>
查看当前配置信息命令如下:
hive>set;
参数的配置有三种方式,分别如下:
(1)根据配置文件配置,默认配置文件:hive-default.xml,用户自定义配置文件:hive-site.xml
用户自定义配置会覆盖默认配置。另外,Hive 也会读入 Hadoop 的配置,因为 Hive 是作为 Hadoop 的客户端启动的,Hive 的配置会覆盖 Hadoop 的配置。配置文件的设定对本机启动的所有 Hive 进程都有效。配置文件的读取的顺序依次为hadoop配置 ==> 默认配置hive-default.xml ==> 用户自定义配置hive-site.xml,因为后读取的会覆盖前面的配置所以越往后,优先级越高。
(2)命令行参数方式,启动 Hive 时,可以在命令行添加-hiveconf param=value 来设定参数
例如:
[mrlin@hadoop103 hive]$ bin/hive -hiveconf mapred.reduce.tasks=10;
注意:仅对本次 hive 启动有效 查看参数设置:
hive (default)> set mapred.reduce.tasks;
(3)参数声明方式,可以在 HQL 中使用 SET 关键字设定参数
例如:
hive (default)> set mapred.reduce.tasks=100;
注意:仅对本次 hive 启动有效。 查看参数设置
hive (default)> set mapred.reduce.tasks;
上述三种设定方式的优先级依次递增。即配置文件<命令行参数<参数声明。注意某些系 统级的参数,例如 log4j 相关的设定,必须用前两种方式设定,因为那些参数的读取在会话 建立以前已经完成了。
基本常用类型包括:TINYINT(1byte),SMALINT(2byte),INT,BIGINT,BOOLEAN,FLOAT,DOUBLE,STRING,TIMESTAMP(时间类型),BINARY(字节数组),其中string类型,相当于数据库的varchar类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数。
Hive 有三种复杂数据类型 ARRAY、MAP 和 STRUCT。ARRAY 和 MAP 与 Java 中的 Array 和 Map 类似,而 STRUCT 与 C 语言中的 Struct 类似,它封装了一个命名字段集合,复杂数据 类型允许任意层次的嵌套。
结构体Struct
和 c 语言中的 struct 类似,都可以通过“点”符号访 问元素内容。例如,如果某个列的数据类型是 STRUCT{first STRING, last STRING},那么第 1 个元素可以通过字段.first 来 引用。
键值对Map
MAP 是一组键-值对元组集合,使用数组表示法可以 访问数据。例如,如果某个列的数据类型是 MAP,其中键
->值对是’first’->’John’和’last’->’Doe’,那么可以 通过字段名[‘last’]获取最后一个元素
数组Array
数组是一组具有相同类型和名称的变量的集合。这些 变量称为数组的元素,每个数组元素都有一个编号,编号从 零开始。例如,数组值为[‘John’, ‘Doe’],那么第 2 个元素可以通过数组名[1]进行访问。
简单演示:
(1)基于上述数据结构,我们在 Hive 里创建对应的表,并导入数据。
# 创建本地测试文件 test.txt
[mrliin@hadoop102 hive]$ mkdir datas
[mrliin@hadoop102 hive]$ cd datas
[mrliin@hadoop102 datas]$ vim test.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 里的元素间关系都可以用同一个字符表示,这里用“_”。
(2)在Hive上创建测试表 test
[mrliin@hadoop102 hive]$ bin/hive
hive (default)> 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';
字段解释:
hive (default)> load data local inpath '/opt/module/hive/datas/test.txt' into table test;
(4)访问三种集合列里的数据,以下分别是 ARRAY,MAP,STRUCT 的访问方式
hive (default)> select friends[1],children['xiao song'],address.city from test where name="songsong";
Hive 的原子数据类型是可以进行隐式转换的,类似于 Java 的类型转换,例如某表达式 使用 INT 类型,TINYINT 会自动转换为 INT 类型,但是 Hive 不会进行反向转化,例如,某表 达式使用 TINYINT 类型,INT 不会自动转换为 TINYINT 类型,它会返回错误,除非使用 CAST 操作。
(1)隐式类型转换规则如下:
(2)可以使用CAST进行强制类型转换:
例如 CAST(‘1’ AS INT)将把字符串’1’ 转换成整数 1;如果强制类型转换失败,如执行 CAST(‘X’ AS INT),表达式返回空值 NULL。
数据定义语言,一般用的比较少,用时可以理解,查表即可。
基本语法:
CREATE DATABASE [IF NOT EXISTS] database_name
[COMMENT 'database_comment']
[LOCATION 'hdfs_path']
[WITH DBPROPERTIES ('property_name'='property_value', ...)];
简单说明:
[] 里面为可选项,最简单的创建数据库语句为: create database abc; – 创建名为abc的数据库
[IF NOT EXISTS] 当数据库名称已经存在时,不加 if not exists 系统会报错;
COMMENT 后面接的是数据库的注释
LOCATION 后面接的是数据库存储的位置
DBPROPERTIES dbpropertier接的是数据库的额外属性,可以有name,date等,说明创建数据的用户和日期等,这个属性不存储在原数据的DBS表里面,而是存储在元数据表DATABASE_PARAMS里面
简单示例:
create database if not exists abc
comment 'first test database'
location '/user/hive/warehouse/abc.db'
with dbproperties ('name'='mrlin','date'='2022-11-11');
数据库常用操作:
# 显示数据库
hive (default)> show databases;
# 显示数据库可以加模糊匹配
hive (default)> show databases like 'ab%';
# 查看数据库的描述属性
hive (default)> desc database abc;
# 通过 dbpropertier 添加的属性需要用下面的扩展命令才能查看
hive (default)> desc database extended abc;
# 切换使用的数据库
hive (default)> use abc;
# 使用完之后命令行的前面会变成:
hive (abc)>
# dbproperties 设置的值也可以通过后续进行修改
hive (abc)> alter database abc set dbproperties('name'='lin');
# 删除空数据库
hive (abc)>drop database abcd;
# 上面如果数据库不存在,这会报错
hive (abc)>drop database if exists abcd;
hive (abc)>drop database if exists abc;
# 删除非空数据库必须使用强制指令(谨慎使用**)
drop database abc cascade;
基本语法:
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [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], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]
简单说明:
其中IF NOT EXISTS,COMMENT跟创建数据库时一致,为存在时不报错和对应的注释,相同的情况不再说明。
EXTERNAL 外部表关键字,加上外部表关键字,hive认为改表数据不为hive独有,所有执行drop table删除表的操作时,只会删除数据库里面的元数据,真正的数据仍然保存在hdfs中,生产中多为外部表。不加此关键字则是,删除表drop table时会把元数据和hdfs的真实数据一起删除;
col_name 列名,后面再接列的类型data_type,和该列的注释col_comment,列可以多个,用’,'分隔开;
PARTITIONED BY 创建分区表,后面会详细解释;
CLUSTERED BY 创建分桶表,后面也会详细解释;
SORTED BY 不常用,对桶中的一个或多个列另外排序;
ROW FORMAT SerDe 是 Serialize/Deserilize 的简称, hive 使用 Serde 进行行对象的序列与反序列化;
STORED AS 指定存储文件类型 常用的存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)。如果文件数据是纯文本,可以使用 STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE。
LOCATION 指定表在HDFS上面的存储位置
AS 后跟查询语句,根据查询结果创建表;
LIKE 允许用户复制现有的表结构,但是不复制数据。
简单示例:
# 实例,创建员工表:
create external table if not exists emp( empno int,
ename string, job string, mgr int,
hiredate string, sal double,
comm double, deptno int)
row format delimited fields terminated by '\t';
# 创建员工表2,只复制结构
hive (default)> create table is not exists emp2 like tmp;
表常用操作:
# 查看有哪些表
hive (default)>show tables;
# 查看表的类型
hive (default)> desc formatted emp;
# 查看表的结构
hive (default)> desc emp;
# 修改表的类型为管理表(也就是内部表)
hive (default)> alter table student2 set tblproperties('EXTERNAL'='FALSE');
# 修改表的类型为外部表
hive (default)> alter table student2 set tblproperties('EXTERNAL'='TRUE');
# 重命名表
alter table table_name rename to new_table_name;
# 更新列语法
ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]
# 增加和替换列
ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
hive (default)> create table abc(id int);
hive (default)> desc abc;
hive (default)> alter table abc add columns(name string);
hive (default)> alter table abc replace columns(deptno string, dname string, loc string);
# 删除表
hive (default)> drop table abc;
# 清空表数据,清空只能清空内部表(管理表),无法清空外部表。
hive (abc)> truncate table student;
注:(‘EXTERNAL’=‘FALSE’)和(‘EXTERNAL’=‘TRUE’)的写法为固定写法,区分大小写,不可更改
注:ADD 是代表新增一字段,字段位置在所有列后面(partition 列前),REPLACE 则是表示替换表中所有字段。
基本语法:
hive> load data [local] inpath '数据的 path' [overwrite] into table student [partition (partcol1=val1,…)];
简单说明:
load data:表示加载数据
local:表示从本地加载数据到 hive 表;否则从 HDFS 加载数据到 hive 表
inpath:表示加载数据的路径
overwrite:表示覆盖表中已有数据,否则表示追加
into table:表示加载到哪张表
student:表示具体的表
partition:表示上传到指定分区
简单示例:
# 创建student.txt文件,并输入以下内容
[mrliin@hadoop102 hive]$ vim datas/student.txt
1001,aa
1002,bbb
1003,cc
# 进入数据库,
[mrliin@hadoop102 hive]$ bin/hive
hive (default)> use abc;
# 并创建表student
hive (abc)> create table student(id string,name string) row format delimited fields terminated by ',';
# 加载本地表的数据到abc下的student表
hive (default)> load data local inpath '/opt/module/hive/datas/student.txt' into table abc.student;
# 将数据上到HDFS上,HDFS上创建datas目录,并上传文件
hive (abc)> dfs -mkdir /user/hive/datas;
hive (abc)> dfs -put /opt/module/hive/datas/student.txt /user/hive/datas/;
# 加载数据到abc下的student表
hive (abc)> load data inpath '/user/hive/datas/student.txt' into table abc.student;
# 查看student表里面的数据
hive (abc)> select * from student;
# 再次覆盖上传
hive (abc)> load data local inpath '/opt/module/hive/datas/student.txt' overwrite into table abc.student;
注:执行load命令后,本地的文件仍然存在,但是HDFS上面的文件会删除。
# 创建临时表:
hive (abc)> create table student_tmp(id string,name string) row format delimited fields terminated by ',';
# 向临时表插入数据:
hive (abc)> insert into table student_tmp values('1004','wanggu'),('1004','wangli');
因为上面的修改都是修改元数据,或者直接数据跟表关联(load),一直都没有用到MapReduce,这里的数据插入需要用到MapReduce,所以插入很慢。加载数据一定不能使用MapReduce,使用本地加载才最好,MapReduce只能用于分析,不然,效率极低。
# 查询向表中插入数据:
insert overwrite table student_tmp select id,name from student;
# 其中insert into是追加的方式添加数据,insert overwrite是覆盖的方式添加数据。
# 通过查询一个表建表
create table if not exists student2
as select id,name from student where xxx;
# 通过在hdfs指定位置创建表,会默认将该路径的表数据导入
# 上传数据
hive (abc)> dfs -put /opt/module/hive/datas/student.txt /user/hive/datas/
# 在该位置创建表student2
hive (abc)> create external table if not exists student2(id string,name string)
> row format delimited fields terminated by ','
> location '/user/hive/datas';
# 查询表数据
hive (abc)> select * from student2;
(1)export导出和import导入,导入导出的路径都是HDFS上面的路径,如果导出的路径不存在,则会自动创建,expoer和import主要用于两个hadoop集群之前的Hive表迁移。
hive (abc)> export table student2 to '/opt/module/hive/datas/student2';
hive (abc)> import table student3 from '/opt/module/hive/datas/student2';
(2)可以用insert将数据导出到本地,不过不推荐使用这种方式,因为要用MapReduce
# 将查询结果导出到本地
hive (abc)> insert overwrite local directory '/opt/module/hive/datas/export/student'
> select * from student;
# 将查询的结果格式化再导出到本地
hive(default)>insert overwrite local directory '/opt/module/hive/data/export/student1'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
select * from student;
# 将查询的结果导出到HDFS上(没有接local关键字)
hive (default)> insert overwrite directory '/user/mrlin/student2'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
select * from student;
(3)使用hadoop方式导出到本地
hive (default)> dfs -get /user/hive/warehouse/student/student.txt
/opt/module/data/export/student3.txt;
(4)Hive shell的命令导出
[mrliin@hadoop102 hive]$ bin/hive -e 'select * from abc.student;' > /opt/module/hive/datas/export/student1.txt
基本语法:
SELECT [ALL | DISTINCT] select_expr [AS expr_name], select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY col_list] [CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list]]
[LIMIT number]
简单说明:
DISTRIBUTE BY 用于分区显示,类似于MR程序中的分区
SORT BY 用于分区内排序,因为往往不需要总的排序,只需部分有序
CLUSTER BY 当DISTRIBUTE BY和SORT BY的字段一致时,也就是分区和排序的字段一致时,那么就可以使用CLUSTER BY代替。
limit 用于限制返回的行数
语法注意:
基本的查询跟SQL语句一致,这里不再赘述。
用法示例:
基本查询、简单函数查询例如:count(),max(),min(),sum(),avg()、比较运算符如>,<,=,<>、逻辑运算符如and,or,not、分组group by having、left join,right join,full join等跟SQL基本都是一样的,这里都不再介绍。
(1)RLIKE模糊匹配
% 代表零个或多个字符(任意个字符)。
_ 代表一个字符。
RLIKE 子句是 Hive 中模糊匹配功能的一个扩展,它可以通过 Java 的正则表达式这个更强大 的功能来指定匹配条件。除非是能头部匹配,使用Like,否则都使用Rlike。
# 查找名字中带有A的学生信息
hive (abc)> select * from student where name RLIKE '[A]';
# 可以使用LIKE
hive (abc)> select * from student where name LIKE 'A%';
# 这种情况直接用上面的RLIKE,不推荐使用LIKE
hive (abc)> select * from student where name LIKE '%A%';
(2)Join连接
Join连接的写法其实跟SQL是一样的,但当对 3 个或者更多表进行 join 连接时,如果每个 on 子句都使用相同的连接字段的话,那么只会产生一个 MapReduce job,可以达到优化的效果。
(3)全局排序和局部排序
正常的order by只会产生一个Reduce进行排序,这在分布式效率极低。但在大多数情况下,并不需要全局排序,所以Sort by是为每一个reducer排序,每个reducer内部有序,但是全局来说不是有序。
# 设置 reduce 个数为 3
hive (abc)> set mapreduce.job.reduces=3;
# 查看设置 reduce 个数
hive (abc)> set mapreduce.job.reduces;
# 根据部门编号降序查看员工信息
hive (abc)> select * from emp sort by deptno desc;
# 将查询结果导入到文件中(按照部门编号降序排序)
hive (abc)> insert overwrite local directory '/opt/module/data/sortby-result'
select * from emp sort by deptno desc;
(4)分区
Distribute By:在有些情况下,我们需要控制某个特定行应该到哪个 reducer,通常是为了进行后续的聚集操作,distribute by 子句可以做这件事。distribute by 类似MR中 partition(自定义分区),进行分区,结合 sort by 使用。对于 distribute by 进行测试,一定要分配多 reduce 进行处理,否则无法看到 distribute by 的效果。
# 先按照部门编号分区,再按照员工编号降序排序。
hive (default)> set mapreduce.job.reduces=3;
hive (default)> insert overwrite local directory '/opt/module/data/distribute-result'
select * from emp distribute by deptno sort by empno desc;
注意:
Cluster By:
当 distribute by 和 sorts by 字段相同时,可以使用 cluster by 方式。cluster by 除了具有 distribute by 的功能外还兼具 sort by 的功能。但是排序只能是升序排序,不能指定排序规则为 ASC 或者 DESC。
# 以下两种写法等价
hive (default)> select * from emp cluster by deptno;
hive (default)> select * from emp distribute by deptno sort by deptno;
分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过WHERE 子句中的表达式选择查询所需要的指定的分区,这样的查询效率会提高很多。
创建分区表:
hive (abc)> create table dept_partition(deptno int,dname string)
partitioned by (day string) row format delimited fields terminated by '\t';
创建分区的日志文件:
# 在/opt/module/hive/datas/下面创建dept_20200401.log,dept_20200402.log,dept_20200403.log文件
[mrliin@hadoop102 datas]$ vim dept_20220401.log
[mrliin@hadoop102 datas]$ vim dept_20220402.log
[mrliin@hadoop102 datas]$ vim dept_20220403.log
导入分区表数据:
hive (abc)> load data local inpath '/opt/module/hive/datas/dept_20220401.log'
into table dept_partition partition(day='20220401');
hive (abc)> load data local inpath '/opt/module/hive/datas/dept_20220402.log'
into table dept_partition partition(day='20220402');
hive (abc)> load data local inpath '/opt/module/hive/datas/dept_20220403.log'
into table dept_partition partition(day='20220403');
注意:分区表加载数据时,必须指定分区。
# 数据查询
hive (abc)> select * from dept_partition where day='20220401';
增加分区
hive (abc)> alter table dept_partition add partition(day='20200404');
# 同时添加多个分区,注意这里间隔是空格,只能是空格,其他间隔报错
hive (abc)> alter table dept_partition add partition(day='20200405') partition(day='20200406');
删除分区
hive (abc)> alter table dept_partition drop partition (day='20200406');
# 同时删除多个分区,注意这里间隔是逗号,只能是逗号,其他间隔报错
hive (abc)> alter table dept_partition drop partition (day='20200404'), partition(day='20200405');
查看分区表有多少分区
hive (abc)> show partitions dept_partition;
查看分区表结构
hive (abc)> show formatted dept_partition;
二级分区
当数据量很大时,可以考虑实行二级分区,进一步降低每个分区的数据量。
# 创建二级分区表
hive (abc)> create table dept_partition2(deptno int,dname string)
partitioned by (day string,hour string);
# 加载数据
hive (default)> load data local inpath '/opt/module/hive/datas/dept_20200401.log'
into table dept_partition2 partition(day='20200401', hour='12');
# 查询数据
hive (default)> select * from dept_partition2 where day='20200401' and hour='12';
数据上传到分区目录上,让分区表和数据产生关联的三种方式。其实就是元数据里面没有该分区的目录信息,下面的方式可以将分区的信息更新到元数据上面去。
(1)上传数据后修复
# 创建分区目录
hive (abc)> dfs -mkdir -p /user/hive/warehouse/abc.db/dept_partition2/day=20220401/hour=13;
# 上传数据到分区目录
hive (abc)> dfs -put /opt/module/hive/datas/dept_20220401.log /user/hive/warehouse/abc.db/dept_partition2/day=20220401/hour=13;
# 执行修复命令,不执行此命令,下面的语句是查不到数据的,因为分区数据跟分区目录还没有关联上
hive (abc)> msck repair table dept_partition2;
# 查询数据
hive (abc)> select * from dept_partition2 where day='20220401' and hour='13';
(2)上传数据后添加分区
# 创建分区目录
hive (abc)> dfs -mkdir -p /user/hive/warehouse/abc.db/dept_partition2/day=20220401/hour=14;
# 上传数据到分区目录
hive (abc)> dfs -put /opt/module/hive/datas/dept_20220401.log /user/hive/warehouse/abc.db/dept_partition2/day=20220401/hour=14;
# 执行修复命令,不执行此命令,下面的语句是查不到数据的,因为分区数据跟分区目录还没有关联上
hive (abc)> alter table dept_partition2 add partition(day='20220401',hour='14');
# 查询数据
hive (abc)> select * from dept_partition2 where day='20220401' and hour='14';
(3)创建文件夹后加载数据到分区
# 创建分区目录
hive (abc)> dfs -mkdir -p /user/hive/warehouse/abc.db/dept_partition2/day=20220401/hour=15;
# load数据到分区
hive (abc)> load data local inpath '/opt/module/hive/datas/dept_20220401.log' into table dept_partition2 partition(day='20220401',hour='15');
# 查询数据
hive (abc)> select * from dept_partition2 where day='20220401' and hour='15';
关系型数据库中,对分区表 Insert 数据时候,数据库自动会根据分区字段的值,将数据 插入到相应的分区中,Hive 中也提供了类似的机制,即动态分区(Dynamic Partition),只不过, 使用 Hive 的动态分区,需要进行相应的配置。
开启动态分区参数设置:
(1)开启动态分区功能(默认 true,开启)
hive.exec.dynamic.partition=true
(2)设置为非严格模式(动态分区的模式,默认 strict,表示必须指定至少一个分区为 静态分区,nonstrict 模式表示允许所有的分区字段都可以使用动态分区。)
hive.exec.dynamic.partition.mode=nonstrict
(3)在所有执行 MR 的节点上,最大一共可以创建多少个动态分区。默认 1000
hive.exec.max.dynamic.partitions=1000
(4)在每个执行 MR 的节点上,最大可以创建多少个动态分区。该参数需要根据实际 的数据来设定。比如:源数据中包含了一年的数据,即 day 字段有 365 个值,那么该参数就需要设置成大于 365,如果使用默认值 100,则会报错。
hive.exec.max.dynamic.partitions.pernode=100
(5)整个 MR Job 中,最大可以创建多少个 HDFS 文件。默认 100000
hive.exec.max.created.files=100000
(6)当有空分区生成时,是否抛出异常。一般不需要设置。默认 false
hive.error.on.empty.partition=false
动态分区字段指定:
# 创建分区表,并指定deptno为分区字段
create table dept_no_par(dname string,loc string) partition by (deptno int)
row format delimited fields terminated by '\t';
(1)静态插入数据,表dept里面没有deptno=70的数据
insert into table dept_no_par partition(deptno='70') select dname,loc from dept;
(2)动态插入数据,直接写分区字段,deptno,不用加其他,在查询处的最后一个字段作为分区的数据来源
insert into table dept_no_par partition(deptno) select dname,loc,deptno from dept;
(3)动态插入数据3.0最新写法,可以不写分区的字段,直接查询即可,注意也是最后一个字段作为分区的数据来源,此时不改为非严格模式也是可以的。
insert into table dept_no_par select dname,loc,deptno from dept;
分区提供一个隔离数据和优化查询的便利方式。不过,并非所有的数据集都可形成合理的分区。对于一张表或者分区,Hive可以进一步组织成桶,也就是更为细粒度的数据范围划分。分桶是将数据集分解成更容易管理的若干部分的另一个技术。 分区针对的是数据的存储路径;分桶针对的是数据文件。
创建分桶表
create table stu_buck(id int,name string) clustered by(id) into 4 buckets
row format delimited fields terminated by '\t';
注意:分区表的字段,表里面不能有;分桶表的字段,表里面必须有
# 查看分桶表结构
desc formatted stu_buck;
# 创建数据,并导入分桶表,因为是分桶,需要经过哈希索引,所以此处一定是走mapreduce通道
load data inpath '/student.txt' into table stu_buck;
# insert 方式将数据导入分桶表
hive(default)>insert into table stu_buck select * from student_insert;
# 查询分桶的数据
select * from stu_buck;
分桶规则:
Hive 的分桶采用对分桶字段的值进行哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。比如是:1001,1002,1003,1004,1005,1006 分3桶的话,是按照余数相同分一个桶,就会1001,1004在一个桶,1002,1005在一个桶,1003,1006在一个桶。
分桶表注意事项:
(1)reduce 的个数设置为-1,让 Job 自行决定需要用多少个 reduce 或者将 reduce 的个 数设置为大于等于分桶表的桶数。设置为-1之后,reduce会自动根据桶的数量来划分reduce
(2)从 hdfs 中 load 数据到分桶表中,避免本地文件找不到问题
(3)不要使用本地模式
抽样调查:
对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结果。Hive 可以通过对表进行抽样来满足这个需求。
语法:Tablesample(bucket x out of y),其中x的值必须小于等于y的值,否则报错
# 此处4和1的意思是,分为4等分,从第1份开始抽,
hive (default)> select * from stu_buck tablesample(bucket 1 out of 4 on id);
(1)查看函数用法:
# 查看系统自带的函数
hive (abc)> show functions;
# 显示自带函数的用法
hive (abc)> desc function upper;
# 详细显示自带函数的用法
hive (abc)> desc function extended upper;
(2)行转列
列转行简单示例:
SELECT movie, category_name FROM movie_info
lateral VIEW
explode(split(category,",")) movie_info_tmp AS category_name;
(4)自定义函数
Hive提供了用户可以自定义函数的接口,可以自己编写函数,主要有以下3种。
一般编程步骤如下:
org.apache.hadoop.hive.ql.udf.generic.GenericUDF
org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
add jar linux_jar_path
create [temporary] function [dbname.]function_name AS class_name;
drop [temporary] function [if exists] [dbname.]function_name;
自定义UDF函数示例:(一进一出)
自定义一个 UDF 实现计算给定字符串的长度,例如:
hive(default)> select my_len("abcd");
(1)导入依赖
<dependency>
<groupId>org.apache.hivegroupId>
<artifactId>hive-execartifactId>
<version>3.1.2version>
dependency>
(2)创建类
package com.mrlin.udf;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
public class my_len extends GenericUDF {
//校验数据参数个数
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
if (arguments.length != 1) {
throw new UDFArgumentException("参数个数不为1");
}
return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
}
//处理数据
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
//1.取出输入数据
String input = arguments[0].get().toString();
//2.判断输入数据是否为null
if (input == null) {
return 0;
}
//3.返回输入数据的长度
return input.length();
}
@Override
public String getDisplayString(String[] children) {
return "";
}
}
(3)打成 jar 包上传到服务器/opt/module/data/myudf.jar
(4)将 jar 包添加到 hive 的 classpath
hive (default)> add jar /opt/module/data/myudf.jar;
(5)创建临时函数与开发好的 java class 关联
hive (default)> create temporary function my_len as "com.mrlin.udf.my_len";
(6)即可在 hql 中使用自定义的函数
hive (default)> select ename,my_len(ename) ename_len from emp;
自定义UDTF函数示例:
自定义一个 UDTF 实现将一个任意分割符的字符串切割成独立的单词,例如:
hive(default)> select myudtf("hello,world,hadoop,hive", ",");
hello
world
hadoop
hive
(1)创建类
package com.mrlin.udf;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
import java.util.List;
public class MyUDTF extends GenericUDTF {
//输出数据的集合
private ArrayList<String> outPutList = new ArrayList<>();
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//输出数据的默认列名,可以别别名覆盖
List<String> fieldNames = new ArrayList<>();
fieldNames.add("word");
//输出数据的类型
List<ObjectInspector> fieldOIs = new ArrayList<>();
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
//最终返回值
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
//处理输入数据:hello,atguigu,hive
@Override
public void process(Object[] args) throws HiveException {
//1.取出输入数据
String input = args[0].toString();
//2.按照","分割字符串
String[] words = input.split(",");
//3.遍历数据写出
for (String word : words) {
//清空集合
outPutList.clear();
//将数据放入集合
outPutList.add(word);
//输出数据
forward(outPutList);
}
}
//收尾方法
@Override
public void close() throws HiveException {
}
}
(2)打成 jar 包上传到服务器/opt/module/hive/data/myudtf.jar
(3)将 jar 包添加到 hive 的 classpath 下
hive (default)> add jar /opt/module/hive/data/myudtf.jar;
(4)创建临时函数与开发好的 java class 关联
hive (default)> create temporary function myudtf as "com.mrlin.udf.MyUDTF";
5)使用自定义的函数
hive (default)> select myudtf(“hello,world,hadoop,hive”,“,”);
hadoop支持的压缩包含以下:deflate,Gzip,bzip2,LZO,Snappy,其中支持切分的有bzip2和LZO,使用以下命令可以查看Hadoop支持哪些数据压缩格式。
# 可以查看支持哪些压缩
hadoop checknative
(1)压缩参数配置
要在Hadoop中启用压缩,首先需要在配置文件mapred-site.xml中配置。其中
(2)开启Map输出阶段的压缩
# 开启 hive 中间传输数据压缩功能
hive (default)>set hive.exec.compress.intermediate=true;
# 开启 mapreduce 中 map 输出压缩功能
hive (default)>set mapreduce.map.output.compress=true;
# 设置 mapreduce 中 map 输出数据的压缩方式为Snappy
hive (default)>set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;
# 执行查询语句
hive (default)> select count(ename) name from emp;
查看是否按照设置的压缩执行,可以查看历史日志
(3)开启Reduce输出阶段的压缩
# 开启 hive 最终输出数据压缩功能
hive (default)>set hive.exec.compress.output=true;
# 开启 mapreduce 最终输出数据压缩
hive (default)>set mapreduce.output.fileoutputformat.compress=true;
# 设置 mapreduce 最终数据输出压缩方式
hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
# 设置 mapreduce 最终数据输出压缩为块压缩
hive (default)> set mapreduce.output.fileoutputformat.compress.type=BLOCK;
# 默认是行压缩,可以先查看默认的压缩模式
hive (default)> set mapreduce.output.fileoutputformat.compress.type;
# 测试一下输出结果是否是压缩文件
hive (default)> insert overwrite local directory '/opt/module/data/distribute-result' select * from emp distriute by deptno sort by empno desc;
Hive支持的存储数据的格式主要有:TextFile,SequenceFile,ORC和 parquet 四种。假设有表数据如下:
a | b | c |
---|---|---|
a1 | b1 | c1 |
a2 | b2 | c2 |
a3 | b3 | c3 |
行存储的格式为:a1,b1,c1,a2,b2,c2,a3,b3,c3,每一行的数据是在一起的。
列存储的格式为:a1,a2,a3,b1,b2,b3,c1,c2,c3,每一列的数据是在一起的。
行存储的特点:
查询条件满足一整行的时候,因为都是顺序连着的所以很快,需要获取表里一整行数据的时候,比较快,TextFile,SequenceFile为行存储。
列存储的特点:
因为每个字段的数据聚集存储,在查询只要少数几个字段的时候,能减少读取的数据量;同时同类型的数据一起存储,可以设计更好的压缩算法,ORC,parquet为列存储。
TextFile文件格式:
默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合 Gzip、Bzip2 使用, 但使用 Gzip 这种方式,hive 不会对数据进行切分,从而无法对数据进行并行操作。
ORC文件格式:
ORC格式(Optimized Row Columnar),每个ORC文件由 1 个或多个stripe组成,而每个Stripe由Index Data,Row Data,Stripe Footer三部分组成。每个Stripe一般为HDFS的块大小。
(1)Index Data:一个轻量级的 index,默认是每隔 1W 行做一个索引。这里做的索引只是记录某行的各字段在 Row Data 中的 offset。
(2)Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个 Stream 来存储。
(3)Stripe Footer:存的是各个 Stream 的类型,长度等信息。
每个文件有一个File Footer,这里面存的是每个Stripe的行数,每个Column 的数据类型信息等;每个文件的尾部是一个PostScript,这里面记录了整个文件的压缩类型以及FileFooter 的长度信息等。在读取文件时,会seek到文件尾部读PostScript,从里面解析到File Footer长度再读 FileFooter从里面解析到各个 Stripe 信息,再读各个Stripe,即从后往前读。
Parquet 文件格式:
Parquet格式,最适合需要从大型表中读取某些列的查询,spark默认的文件存储格式。Parquet 文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet 格式文件是自解析的。主要由以下三部分组成:
(1)行组(Row Group):每一个行组包含一定的行数,在一个 HDFS 文件中至少存储一 个行组,类似于 orc 的 stripe 的概念。
(2)列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连 续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩。
(3)页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块 的不同页可能使用不同的编码方式。
通常情况下,在存储 Parquet 数据的时候会按照 Block 大小设置行组的大小,由于一般 情况下每一个 Mapper 任务处理数据的最小单位是一个 Block,这样可以把每一个行组由一 个 Mapper 任务处理,增大任务执行并行度。
简单存储格式示例:
# 存储格式为textfile
create table log_text (
name string,
url string
)
row format delimited fields terminated by '\t' stored as textfile;
# 存储格式为ORC
create table log_orc (
name string,
url string
)
row format delimited fields terminated by '\t'
stored as orc
tblproperties("orc.compress"="NONE"); -- 设置 orc 存储不使用压缩
# 存储格式为parquet
create table log_parquet (
name string,
url string
)
row format delimited fields terminated by '\t'
stored as parquet;
添加数据:
hive (default)> load data local inpath '/opt/module/hive/datas/log.data' into table log_text ;
hive (default)> insert into table log_orc select * from log_text;
hive (default)> insert into table log_parquet select * from log_text;
其中ORC,Parquet这两种存储数据的格式不能用load直接添加,需要使用select insert 的方式,因为load不会进入MR
压缩率 : ORC > Parquet > textFile
查询速度:ORC,Parquet,textFile 查询速度相近
简单压缩和存储示例:
# 创建一个ZLIB压缩的ORC存储方式,ORC默认的压缩方式就是 deflate 压缩算法
create table log_orc_zlib( -- 建表
track_time string,url string)
row format delimited fields terminated by '\t' -- 按\t分行
stored as orc -- orc存储
tblproperties("orc.compress"="ZLIB"); -- ZLIB压缩
# 插入数据
insert into log_orc_snappy select * from log_text;
# 查看插入后的数据
dfs -du -h /user/hive/warehouse/log_orc_zlib/;
# 创建一个Snappy压缩的ORC存储方式的表
create table log_orc_snappy(
track_time string,url string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties("orc.compress"="SNAPPY");
# 创建一个 SNAPPY 压缩的 parquet 存储方式
create table log_parquet_snappy(
track_time string,url string)
row format delimited fields terminated by '\t'
stored as parquet
tblproperties("parquet.compression"="SNAPPY");
在实际的项目开发当中,hive 表的数据存储格式一般选择:orc 或 parquet。压缩方式一般选择 snappy,lzo。snappy跟lzo读取和压缩速度快,lzo适合切片的场景。