Hive原理

相对于hadoop复杂的job(MapReduce)编写过程,Hive提供了HQL(类似于SQL的)语言来高效的生成查询job,方便数据工程师在不会编程的情况下,进行并专注于数据分析。从本质上讲,Hive同Pig一样,都是一种简化批处理任务编程复杂度的工具,下面我们就来总体认识一下。

服务端组件

  • Driver组件:该组件包括:CompilerOptimizerExecutor,它可以将Hive的HQL进行编译、解析、优化后,最终转化为MapReduce任务提交给Hadoop 1中的JobTracker,或者Hadoop 2中的ResourceManager来执行实际的任务。

  • MetaStore组件:存储着Hive的元数据信息,Hive将自己的元数据存储到了关系型数据库当中,支持的数据库主要有:Mysql、Derby、支持把metastore独立出来放在远程的集群上面,使得hive更加健壮。元数据主要包括了表的名称、表的列、分区和属性、表的属性(是不是外部表等等)、表的数据所在的目录。

  • 用户接口:CLI(常用的接口:命令行模式)、Client:Hive的客户端用户连接至Hive Server, 在启动Client的时候,需要指定Hive Server所在的节点,并且在该节点上启动Hive Server、Web UI:通过浏览器的方式访问Hive。

任务执行流程

  1. 用户提交查询等任务给Driver
    1. Compiler获得该用户的任务Plan;
    2. Compiler根据用户任务去MetaStore中获取需要的Hive的元数据信息;
    3. Compiler得到元数据信息,对任务进行编译
      1. 先将HiveQL转换为抽象语法树
      2. 然后将抽象语法树转换成查询块
      3. 将查询块转化为逻辑的查询计划
      4. 重写逻辑查询计划,将逻辑计划转化为物理的计划
      5. 最后选择最佳的策略
    4. 将最终的计划提交给Driver
  2. Driver将计划Plan转交给ExecutionEngine去执行,获取元数据信息,提交给JobTracker或者ResourceManager执行该任务,任务会直接读取HDFS中文件进行相应的操作;
  3. 获取执行的结果;
  4. 取得并返回执行结果。

数据模型

  • Table
    类似于传统数据库中的Table,每一个Table在Hive中都有一个相应的目录来存储数据。例如:一个表t,它在HDFS中的路径为:/user/hive/warehouse/t内部表做删除表,就删除了目录及数据

  • External Table
    指向已存在HDFS中的数据,可创建Partition。数据是存储在Location后面指定的HDFS路径中的,并不会移动到数据仓库中。删除外部表,只是删除了元数据的信息,该外部表所指向的数据是不会被删除

  • Partition

    在Hive中,表中的一个Partition对应于表下的一个目录,所有的Partition数据都存储在对应的目录中。例如:t表中包含ds和city两个Partition,则对应于ds=2014,city=beijing的HDFS子目录为:/user/hive/warehouse/t/ds=2014/city=Beijing;需要注意的是,分区列是表的伪列,表数据文件中并不存在这个分区列的数据。一个表可以拥有一个或者多个分区,每个分区以文件夹的形式单独存在表文件夹的目录下。分区避免 Hive Select查询中扫描整个表内容,防止消耗很多时间做没必要的工作(相当于只查询了子表<子目录>)。

  • Buckets

    对指定列计算的hash,根据hash值切分数据,目的是为了便于并行,每一个Buckets对应一个文件。将user列分至32个Bucket上,首先对user列的值计算hash,比如,对应hash=0的HDFS目录为:/user/hive/warehouse/t/ds=2014/city=Beijing/part-00000;对应hash=20的目录为:/user/hive/warehouse/t/ds=2014/city=Beijing/part-00020

    buckets是对partition的一种补充,具体见以下例子:

    假设我们有一张日志表,我们需要按照日期和用户id来分区,目的是为了加快查询谁哪天干了什么。但是这里面用user_id去切分的话,就会产生很多很多的分区了,这些分区可大可小,这个数量是文件系统所不能承受的。
    在这种情况下,我们既想加快查询速度,又避免出现如此多的小分区,篮子(bucket)就出现了。

    1
    2
    3
    CREATE TABLE weblog (user_id INT, url STRING, source_ip STRING)
    > PARTITIONED BY (dt STRING)
    > CLUSTERED BY (user_id) INTO 96 BUCKETS;

    首先按照日期分区,分区结束之后再按照user_id把日志放在96个篮子,这样同一个用户的所有日志都会在同一个篮子里面,并且一个篮子里面有好多用户的日志。

操作实践

安装包

1
2
3
4
wget https://mirrors.sonic.net/apache/hive/hive-3.1.2/apache-hive-3.1.2-bin.tar.gz

tar -xzvf apache-hive-3.1.2-bin.tar.gz
mv apache-hive-3.1.2-bin hive-3.1.2

配置

  • hive-default.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    cd $HIVE_HOME/conf
    cp hive-default.xml.template hive-default.xml

    #注意修改thrift账号密码
    #写入xml
    <property>
    <name>hive.server2.thrift.client.user</name>
    <value>hive</value>
    <description>Username to use against thrift client</description>
    </property>
    <property>
    <name>hive.server2.thrift.client.password</name>
    <value>hive</value>
    <description>Password to use against thrift client</description>
    </property>
  • hive-env.sh

    1
    2
    3
    4
    5
    cd $HIVE_HOME/conf
    cp hive-env.sh.template hive-env.sh

    #追加内容
    HADOOP_HOME=/root/bigData/hadoop-3.3.0
  • hive-site.xml

    需要在$HIVE_HOME/conf/下创建hive-site.xml,并写入以下值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
    <configuration>

    <property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value>jdbc:derby:${HIVE_HOME}/metastore_db;databaseName=metastore_db;create=true</value>
    </property>
    <property>
    <name>hive.metastore.schema.verification</name>
    <value>false</value>
    </property>
    </configuration>

初始化与启动

  • 初始化metastore

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     $HIVE_HOME/bin/schematool -initSchema -dbType derby
    # 遇到以下报错
    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/root/bigData/hive-3.1.2/lib/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/root/bigData/hadoop-3.3.0/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
    Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;)
    at org.apache.hadoop.conf.Configuration.set(Configuration.java:1380)
    at org.apache.hadoop.conf.Configuration.set(Configuration.java:1361)
    at org.apache.hadoop.mapred.JobConf.setJar(JobConf.java:536)
    at org.apache.hadoop.mapred.JobConf.setJarByClass(JobConf.java:554)
    at org.apache.hadoop.mapred.JobConf.<init>(JobConf.java:448)
    at org.apache.hadoop.hive.conf.HiveConf.initialize(HiveConf.java:5141)
    at org.apache.hadoop.hive.conf.HiveConf.<init>(HiveConf.java:5104)
    at org.apache.hive.beeline.HiveSchemaTool.<init>(HiveSchemaTool.java:96)
    at org.apache.hive.beeline.HiveSchemaTool.main(HiveSchemaTool.java:1473)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.hadoop.util.RunJar.run(RunJar.java:323)
    at org.apache.hadoop.util.RunJar.main(RunJar.java:236)

解决方案为:比较hadoop安装目录下share/hadoop/common/lib内guava.jar版本,与hive安装目录下lib内guava.jar的版本 如果两者不一致,删除版本低的,并拷贝高版本的。

  • hive命令行启动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $HIVE_HOME/bin/hive

    # 启动后,会发现多了一个RunJar进程
    [root@aliyun-vm ~]# jps
    6882 NodeManager
    6754 ResourceManager
    4164 SecondaryNameNode
    3814 NameNode
    3946 DataNode
    8236 RunJar
    8430 Jps
  • metastore启动

    1
    $HIVE_HOME/bin/hive --service metastore
  • hiveserver2启动

    1
    2
    3
    4
    5
    6
    $HIVE_HOME/bin/hiveserver2 --hiveconf hive.root.logger=DEBUG,console

    # 使用客户端连接
    $HIVE_HOME/bin/beeline
    beeline> !connect jdbc:hive2://127.0.0.1:10000/default #账号密码为前面设置的hive/hive
    beeline> !close

HSQL配置

创建一个库表,表名为 cwqsolo, 含有2个字段 id, name,并且记录中各个字段的分割符为 ‘,’,在ascii中表示为 ‘\054’;

1
CREATE TABLE cwqsolo(id INT, name STRING)ROW FORMAT DELIMITED FIELDS TERMINATED BY '\054’;

修改字段名为ID的字段,修改后名称为 myid, string类型

1
ALTER TABLE cwqsolo CHANGE id myid String;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE `employees`
(
`employeenumber` int,
`lastname` string,
`firstname` string,
`extension` string,
`email` string,
`officecode` string,
`jobtitle` string
)
COMMENT 'import from mysql'
PARTITIONED BY (reportsto int)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS textfile;

增加一个字段,字段名为 sex,性别

1
ALTER TABLE cwqsolo ADD COLUMNS (sex STRING COMMENT 'sex type');

加载文件

1
2
3
4
[root@archive studydata]# vi  test1.txt  
1001,cwq,male
1101,lxj,female
hive> LOAD DATA LOCAL INPATH '/home/hadoop/hive/studydata/test1.txt' INTO TABLE cwqsolo;

导入employees数据到表

1
LOAD DATA LOCAL INPATH './classicmodels_employees.csv' OVERWRITE into table employees PARTITION (reportsto='1002');

插入条目

1
2
insert into  table cwqsolo values ( '1005', 'ddd','male' );   # 追加
insert overwrite table test_insert select * from test_table; # 覆盖

执行SQL文本

1
hive -f /home/hadoop/hive/sql/creat_table_biss.sql

导出文本到本地

1
2
3
insert overwrite local directory '/home/data/cwq/' select * from  zc_bicc_isup_bdr  where  calling= '13003097698'; 

insert overwrite local directory '/home/data/cwq/output1’ row format delimited fields terminated by ',' select * from zc_bicc_isup_bdr where calling= '13003097698’; # 指定分隔符

0%