第四模块:Hive

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的设计模式使他天生不适合做这个事

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值