Hive
Hadoop在分布式数据处理中遇到的问题
MR开发调试复杂,不适合要求快速得出结果的场景。
Hadoop由Java开发,对JAVA支持最好,对其他语言的使用者不够友好。
需要对Hadoop底层具有一定的了解,并且熟悉API才能开发出优秀的MR程序。
概述
Hive是一个建立在Hadoop基础之上的数据仓库工具,以HiveQL(类SQL)的操作方式让我们能够轻松的实现分布式的海量离线数据处理。而不必去编写调试繁琐的MR程序。
优点:
避免了MR繁琐的开发调试过程,Hive自动将我们输入的HQL编译为MR运行
HQL这种类SQL语言对于任何开发语言的程序员来说都比较友好。
我们不需要对Hadoop底层有太多的理解,也不用记忆大量的API就能实现分布式数据的处理
Hive也提供了自定义函数的方式来补充自身函数库可能存在的不足。即编写JAVA代码来实现复杂的逻辑并封装为UDF(自定义函数)来供我们重复使用。
数据仓库
数据仓库是一个面向主题的,稳定的,集成的,反应历史数据的数据存储工具,他主要支持管理者的决策分析。
安装
Hive安装非常简单,解压之后即可直接运行,不需要太多配置,前提是要配置JAVA_HOME和HADOOP_HOME。并且Hadoop要全量启动(五个进程)
初始化元数据库
schematool -dbType derby -initSchema
启动
在/home/app/apache-hive-2.3.6-bin/bin目录下执行
./hive
基础操作
创建数据库
create database jtdb;
结论1:Hive中的数据库,其实就是HDFS中/user/hive/warehouse目录下的以.db结尾的文件夹。
创建表
create table tb_user(id int,name string);
结论2:Hive中的表其实就是HDFS中对应数据库文件夹下的一级文件夹。
插入数据
insert into table tb_user values(1,"dongcc");
结论3:Hive在必要的时候会将HQL编译为MR来执行,上图展示了变异后的MR执行的过程。
结论4:hive中的数据在HDFS中以文件的形式存储在其所在表的文件夹中。
结论5:默认数据库default没有自己单独的文件夹,其下的表直接存放在/user/hive/warehouse下。
加载数据
Load data local inpath '/home/data/book1.txt' into table table_name;
其中,local关键字为标识从本地文件系统中读取文件,如果不加,默认从HDFS中读取指定文件。
可能遇到的问题
Hive启动不了
检查JAVA_HOME和HADOOP_HOME是否配置成功。如果没有问题并报错:Cannot find hadoop installation: $HADOOP_HOME or $HADOOP_PREFIX must be set or hadoop must be in the path
解决办法:
指定HADOOP_HOME路径
cd /home/app/apache-hive-1.2.0-bin/conf
cp hive-env.sh.template hive-env.sh
vim hive-env.sh
增加HADOOP_HOME
HADOOP_HOME=/home/app/hadoop-2.7.1
Hive启动报错Safe mode
Hadoop在启动时有个安全模式,其在启动时有些工作要做,元数据的处理,DataNode的等待等过程。需要一段时间,遇到时需要等一段时间,耐心稍微等一会。过会自动就会好。
如果长时间还报错,还在安全模式。可以手工设置退出安全模式。
[root@hadoop01 bin]# pwd
/home/app/hadoop-2.7.1/bin
[root@hadoop01 bin]# ./hadoop dfsadmin -safemode leave
DEPRECATED: Use of this script to execute hdfs command is deprecated.
Instead use the hdfs command for it.
Safe mode is OFF
[root@hadoop01 bin]#
参数value的说明如下:
enter - 进入安全模式
leave - 强制NameNode离开安全模式
get - 返回安全模式是否开启的信息
wait - 等待,一直到安全模式结束
元数据库的替换
Hive没有将描述数据库、表、数据之间关系的元数据直接存放在HDFS中,而是存放在了传统的关系型数据库中,这样保证了元数据可以快速的进行增删改查。
Hive原生的将元数据保存在了内置的Derby数据库中。
Derby存在的问题:过于轻量级,性能较低,安全性不高,不适合生产。
这种情况我们是无法忍受的,实际开发中不会使用Derby来做Hive的元数据库。所以我们要将他替换掉。以mysql为例。
修改配置文件hive-site.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hive.default.fileformat</name>
<value>TextFile</value>
</property>
<property>
<!--端口改为你自己的端口,这里是连接数据库中hive数据库-->
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://hadoop01:3306/hive?createDatabaseIfNotExist=true</value>
<description>JDBC connect string for a JDBC metastore</description>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
<description>Driver class name for a JDBC metastore</description>
</property>
<property>
<!--连接MySQL的用户名-->
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
<description>username to use against metastore database</description>
</property>
<property>
<!--连接MySQL的密码-->
<name>javax.jdo.option.ConnectionPassword</name>
<value>root</value>
<description>password to use against metastore database</description>
</property>
</configuration>
导入驱动包
直接将mysql驱动包上传到hive的lib目录中即可
/home/app/apache-hive-2.3.6-bin/lib
开放mysql权限
进入mysql客户端(命令行)执行
grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;
如果上面修改后还提示权限错误,修改指定机器
grant all privileges on *.* to 'root'@'hadoop01' identified by 'root' with grant option;
flush privileges;
初始化
schematool -dbType mysql -initSchema
元数据信息
DBS:数据库信息
TBLS:表信息
SDS:表详细信息
COLUMNS_V2:列信息
表的分类
内部表和外部表
内部表(托管表):MANAGED_TABLE
Hive中先创建表后插入或上传数据的表称之为内部表。
创建语句:
create table table_name (id int,name string) row format delimited fields terminated by '\t';
加载数据:
load data local inpath '文件路径' into table table_name;
删除表:
drop table table_name;
删除表时,连同数据一起被删除。
再次加载数据一样可以被管理到。
外部表:EXTERNAL_TABLE
Hive中先有数据后创建表来管理数据的表叫做外部表。
创建表:在创建之前先要有数据。
create external table table_name (id int ,name string) row format delimited fields terminated by '\t' location 'HDFS中文件所在
路径’;
删除表:
drop table table_name;
外部表在删除时,只会将元数据信息删掉,而不会删除数据本身。
再次加载数据一样可以被管理。
内外部表的区别
内部表在被drop的时候,元数据信息会被删除,表数据也会被删除。
外部表在被drop的时候,元数据信息会被删除,表数据不会被删除
分区表
在hive中数据的面向主题存储就是由表的分区来实现的。
单级分区
创建分区表:
create table table_name (id int,name string) partitioned by (country string) row format delimited fields terminated by '\t';
与普通表相比只是多了partitioned by (country string)
为分区表中载入数据:
load data local inpath '文件位置' overwrite into table table_name partition (country='CHN');
经过查看HDFS中存储发现:
分区在表所在目录中以文件夹形式存在,数据落在分区目录中,并且分区文件夹以分区类型=分区名定义如上图country=CHN;
另外在select * from tb_par;时发现:
分区信息查询结果中展示多出了一列,这并不是给数据增加了一个字段。而是单独的分区信息绑定,当然你可以把它当做一个字段来使用,比如作为条件查询。
再次加载数据不能直接被管理
在分区表中仍然支持手动直接上传文件到HDFS中,但是需要我们按照对应的目录标准创建分区目录,并且需要
alter table table_name add partition (country='JPA')location '自定义的分区目录';
这样元数据信息中才能有该分区的信息,hive才能读取到这些我们自己上传的数据。
多级分区
创建多级分区表:
create table table_name (id int,name string) partitioned by (country string, gender string) row format delimited fields terminated by '\t';
加载数据:
load data local inpath '/home/data/JPA_female.txt' overwrite into table tb_par2 partition (country='JPA',gender='female');
查看元数据:发现分区就是多了几级目录。
查询多级分区目录:
select * from tb_par2 where gender = 'female' and country = 'JPA';
注意:在实际的生产中分区不是随意设置的,也不是越多越好,而是将经常查询的维度设置为分区即可!
分桶表
在测试过程中,每次都将全表数据加载进来会花费非常多的时间,而随意截取部分数据进行测试又不具有代表性,这时我们可以使用分桶的方法,来减少测试数据量,并使结果具有较高的代表性。
创建测试数据表:
create table tb_data (id int,name string) row format delimited fields terminated by ',';
加载数据:
load data local inpath '/home/data/teachers.txt' into table tb_data;
创建分桶表:
create table table_name (id int,name string) clustered by (id) into 4 buckets row format delimited fields terminated by ',';
开启分桶模式:
Hive默认未开启多个reduce,分桶需要多个reduce同时工作,所以这里要开启分桶模式:
set hive.enforce.bucketing=true;
然后进行导入:这个过程时间较长,桶越多时间越长。
insert into table table_name select * from tb_data;
数据顺序发生了改变,查看HDFS存储发现整个文件被拆分成四部分,自动命名0~3;
数据取样
select * from table_name tablesample (bucket x out of y on id);
其中x为第几个桶,不能大于总桶数, y为选取数据的步长(几个桶取一次数据)。Y应为桶数的因数或倍数,当y大于桶数时,选取结果非整桶抽取,而是抽取每桶中的y除以桶数分之一,即:1/(y/桶数)
按百分比取样:整表数据额百分之三十
select * from table_name tablesample(30 percent);
进阶:hivejdbc
在Hadoop配置文件中加入以下内容
hdfs-site.xml
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
core-site.xml
<property>
<name>hadoop.proxyuser.root.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
启动hiveserver2
在hive安装目录下的bin目录中执行
./hiveserver2
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>flume</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>2.3.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
</project>
代码
package cn.tedu.flume;
import java.sql.*;
public class HiveJDBC {
private static String driverName = "org.apache.hive.jdbc.HiveDriver";
/**
* @param args
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.exit(1);
}
Connection con = DriverManager.getConnection("jdbc:hive2://hadoop01:10000/default", "root", "");
Statement stmt = con.createStatement();
String dbName = "test";
String tableName = dbName+".tb_test";
System.out.println("insert into table "+ tableName + " values(1, 'dongcc')");
stmt.execute("drop database if exists " + dbName);
stmt.execute("create database " + dbName);
stmt.execute("drop table if exists " + tableName);
stmt.execute("create table " + tableName + " (key int, value string)");
stmt.execute("insert into table " + tableName + " values(1, 'dongcc')");
// show tables
String sql = "select * from "+tableName;
System.out.println("Running: " + sql);
ResultSet res = stmt.executeQuery(sql);
if (res.next()) {
System.out.println(res.getString(1));
System.out.println(res.getString(2));
}
}
}
补充
数据仓库和关系型数据库的区别:
数据库 | 数据仓库 |
---|---|
面向事务 | 面向主题 |
实时数据 | 历史数据 |
避免冗余 | 有意冗余 |
捕获数据 | 分析数据 |
为用户提供服务 | 为管理者提供服务 |
所有的离线数据处理场景都适用hive吗?
并不是所有场景都适合,逻辑简单又要求快速出结果的场景Hive优势更大。但是在业务逻辑非常复杂的情况下还是需要开发MapReduce程序更加直接有效。
Hive能作为业务系统的数据库使用吗?
不能。传统数据库要求能够为系统提供实时的增删改查,而Hive不支持行级别的增删改,查询的速度也不比传统关系型数据库,而是胜在吞吐量高,所以不能作为关系型数据库来使用。
Hive与传统MR方式处理数据相比能够提高运行效率吗?能够提高工作效率吗?
Hive的使用中需要将HQL编译为MR来运行,所以在执行效率上要低于直接运行MR程序。但是对于我们来说,由于只需要编写调试HQL,而不用开发调试复杂的MR程序,所以工作效率能够大大提高。
Hive为什么不支持行级别的增删改?
Hive不支持行级别的增删改的根本原因在于他的底层HDFS本身不支持。在HDFS中如果对整个文件的某一段或某一行内容进行增删改,势必会影响整个文件在集群中的存放布局。需要对整个集群中的数据进行汇总,重新切块,重新发送数据到每个节点,并备份,这样的情况是得不偿失的。所以HDFS的设计模式使他天生不适合做这个事