HIVE学习之路

HIVE是什么?

 Hive 依赖于 HDFS 存储数据,Hive 将 HQL 转换成 MapReduce 执行,所以说 Hive 是基于 Hadoop 的一个数据仓库工具,实质就是一款基于 HDFS 的 MapReduce 计算框架,对存储在 HDFS 中的数据进行分析和管理

为了能够在大数据上使用SQL来处理数据,Hive应用而生。Hive实际上是在Hadoop上进行结构化数据处理的一个解决方案,目的是能让用户通过编写SQL来处理数据。当然,只有结构化后数据才可以进行查询,Hive中的一个核心模块metastore用来存储结构化的信息,简单来说,就是一个表信息,比如表中有多少列,每个列有什么数据结构。Hive中的执行引擎就会把一条SQL语句进行语法分析,生成语法树,这两个步骤和普通关系型数据库没有本质的区别,主要区别在执行阶段,Hive的执行引擎会把SQL语句翻译成一个MapReduce任务去执行,然后将执行结果返回给用户。从工程角度来看,效率和灵活性是一对矛盾体,从Hive例子中可以看出,SQL出现使得大数据处理开发的任务效率提高了,但是在数据处理的表达和灵活性上,不如直接写MapReduce程序。不过,具体要根据实际的场景去选择。当然,在Hadoop上写SQL也不是只有Hive一个方案,像impala,presto等都是SQL在Hadoop上替换方案。
为什么要使用hive?
直接使用 MapReduce 所面临的问题:
​
  1、人员学习成本太高
​
  2、项目周期要求太短
​
  3、MapReduce实现复杂查询逻辑开发难度太大
使用hive的好处:
​
  1、更友好的接口:操作接口采用类 SQL 的语法,提供快速开发的能力
​
  2、更低的学习成本:避免了写 MapReduce,减少开发人员的学习成本
​
  3、更好的扩展性:可自由扩展集群规模而无需重启服务,还支持用户自定义函数
  
hive中为什么不使用自带的默认数据库derby? --因为derby数据库一次只能打开一个会话

MetaStore:存储和管理Hive的元数据,使用关系数据库来保存元数据信息。

解析器和编译器:将SQL语句生成语法树,然后再生成DAG形式的Job链,成为逻辑计划

优化器:只提供了基于规则的优化

  • 列过滤:去除查询中不需要的列

  • 行过滤:Where条件判断等在TableScan阶段就进行过滤,利用Partition信息,只读取符合条件的Partition

  • 谓词下推:减少后面的数据量

  • Join方式 。 Map端join: 调整Join顺序,确保以大表作为驱动表,小表载入所有mapper内存中 。 shuffle join:按照hash函数,将两张表的数据发送给join 。对于数据分布不均衡的表Group by时,为避免数据集中到少数的reducer上,分成两个map-reduce阶段。第一个阶段先用Distinct列进行shuffle,然后在reduce端部分聚合,减小数据规模,第二个map-reduce阶段再按group-by列聚合。 。 sort merge join:排序,按照顺序切割数据,相同的范围发送给相同的节点(运行前在后台创建立两张排序表,或者建表的时候指定) 。 在map端用hash进行部分聚合,减小reduce端数据处理规模。

    执行器:执行器将DAG转换为MR任务。执行器会顺序执行其中所有的Job,如果Job不存在依赖关系,采用并发的方式进行执行。

    Hive 与 HDFS 之间的联系

    (1)hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供完整的 sql 查询功能,可以将 sql 语句转换为 MapReduce 任务进行运行。其优点是学习成本低,可以通过类 SQL 语句快速实现简单的 MapReduce 统计,不必开发专门的 MapReduce 应用,十分适合数据仓库的统计分析。

    (2)Hive 是建立在 Hadoop 上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载(ETL),这是一种可以存储、查询和分析存储在 Hadoop 中的大规模数据的机制。Hive 定义了简单的类 SQL 查询语言,称为 HQL,它允许熟悉 SQL 的用户查询数据。同时,这个语言也允许熟悉 MapReduce 开发者的开发自定义的 mapper 和 reducer 来处理内建的 mapper 和 reducer 无法完成的复杂的分析工作。

    Hive可以看做是MapReduce的客户端
     因为Hive的底层运算是MapReduce计算框架,Hive只是将可读性强,容易编程的SQL语句通过Hive软件转换成MR程序在集群上执行。hive可以看做mapreduce客户端,能用mapreduce程序完成的任务基本都可以对应的替换成hql(Hive SQL)编写的hive任务。所以因为hadoop和hdfs的本身设计的特点,也限制了hive所能胜任的工作特性。Hive最大的限制特点就是不支持基于行记录的更新,删除,增加。但是用户可以通过查询生成新表,或者将查询结果导入文件中来“实现”hive基于行记录的操作。
    Hive 与 关系型数据库 的区别

    使用 hive 的命令行接口,感觉很像操作关系数据库,但是 hive 和关系数据库还是有很大的不同,下面我就比较下 hive 与关系数据库的区别,具体如下:

    Hive 和关系数据库存储文件的系统不同,Hive 使用的是 hadoop 的 HDFS(hadoop 的分布式文件系统),关系数据库则是服务器本地的文件系统;
    ​
    hive 使用的计算模型是 mapreduce,而关系数据库则是自己设计的计算模型;
    ​
    关系数据库都是为实时查询的业务进行设计的,而 Hive 则是为海量数据做数据挖掘设计的,实时性很差;实时性的区别导致 Hive 的应用场景和关系数据库有很大的不同;
    ​
    Hive 很容易扩展自己的存储能力和计算能力,这个是继承 hadoop 的,而关系数据库在这个方面要比数据库差很多。

    hive和hdfs,mysql,mapreduce之间的关系

    举例说明hive,mysql和hdfs之间的关系

    下面是一个完成的流程,从hive中创建表,到往表里导入数据,1-9说明了hive,mysql,hdfs之间的流程。

    1.Hive不存储数据,Hive需要分析计算的数据,以及计算结果后的数据实际存储在分布式系统上,如HDFS上。

    2.Hive某种程度来说也不进行数据计算,只是个解释器,只是将用户需要对数据处理的逻辑,通过SQL编程提交后解释成MapReduce程序,然后将这个MR程序提交给Yarn进行调度执行。所以实际进行分布式运算的是MapReduce程序

    3.因为Hive为了能操作HDFS上的数据集,那么他需要知道数据的切分格式,如行列分隔符,存储类型,是否压缩,数据的存储地址等信息。为了方便以后操作所以他需要将这些信息通过一张表存储起来,然后将这张表(元数据)存储到mysql中。为了啥存储到mysql里(实际是远程mysql),因为hive本身就是一个解释器,所以他不存储数据。

    在hive中使用explain +extended来实现mapreduce过程

    explain会把查询语句转化成stage组成的序列,主要由三方面组成:

    1. 查询的抽象语法树

    2. plane中各个stage的依赖情况

    3. 每个阶段的具体描述:描述具体来说就是显示出对应的操作算子和与之操作的对应的数据,例如查询select算子,filter算子,fetch算子等等。

    hive> explain select * from students limit 10;
    OK
    STAGE DEPENDENCIES:
      Stage-0 is a root stage
    ​
    STAGE PLANS:
      Stage: Stage-0
        Fetch Operator
          limit: 10
          Processor Tree:
            TableScan
              alias: students
              Statistics: Num rows: 6 Data size: 107 Basic stats: COMPLETE Column stats: NONE
              Select Operator
                expressions: id (type: int), rq (type: string), num (type: int), stu_len (type: int)
                outputColumnNames: _col0, _col1, _col2, _col3
                Statistics: Num rows: 6 Data size: 107 Basic stats: COMPLETE Column stats: NONE
                Limit
                  Number of rows: 10
                  Statistics: Num rows: 6 Data size: 107 Basic stats: COMPLETE Column stats: NONE
                  ListSink
    ​
    内部表和外部表的区别
    内部表 :
       指定Hive表的数据的存储位置,一般在数据已经上传到HDFS,想要直接使用,会指定Location,通常Locaion会跟外部表一起使用,内部表一般使用默认的location。
       
       create table students_internal
    (
        id bigint
        ,name string
        ,age int
        ,gender string
        ,clazz string
    )
    ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
    LOCATION '/input2';
    ​
    ​
    外部表:
     create external table students_external
    (
        id bigint
        ,name string
        ,age int
        ,gender string
        ,clazz string
    )
    ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
    location '/user/hive/warehouse/external';
    ​
    -- 创建外部表时,需要指定其数据存储的hdfs路径 ,且外部表被删除时,只会删除其员数据信息,其数据仍然保存在hdfs中,且文件不受元数据格式的影响。
    ​
    -- Hive 内部表(Managed tables)与 外部表(External tables)的区别 
    //查看所有表
    show tables;
    ​
    //删除内部表students_internal
    drop table students_internal;
    show tables;
    ​
    //删除外部表students_external
    drop table students_external;
    show tables;
    ​
    可以看出,删除内部表的时候,表中的数据(HDFS上的文件)会被同表的元数据一起删除删除外部表的时候,只会删除表的元数据,不会删除表中的数据(HDFS上的文件)一般在公司中,使用外部表多一点,因为数据可以需要被多个程序使用,避免误删,通常外部表会结合location一起使用外部表还可以将其他数据源中的数据 映射到 hive中,比如说:hbase,ElasticSearch…
    -- 设计外部表的初衷就是让表的元数据与数据解耦
    ​
    建表语句
    create table students2
    (
        id bigint
        ,name string
        ,age int
        ,gender string
        ,clazz string
    )
    row format delimited fields terminated by ',' -- 指定分隔符
    LOCATION '/input3' -- 指定存储路径
    stored as rcfile;  -- 指定存储格式
    ​
    ​
    数据加载 -- hive> load data local inpath 'xxx/a.txt' into table student
    ​
    分区分桶

    为什么要分区分桶?分区分桶的作用是什么?有什么好处?

    在Hive中的数据仓库中,也有分区分桶的概念,在逻辑上,分区表与未分区表没有区别,在物理上分区表会将数据按照分区间的列值存储在表目录的子目录中,目录名=“分区键=键值”。其中需要注意的是分区键的列值存储在表目录的子目录中,目录名=“分区键=键值”。其中需要注意的是分区键的值不一定要基于表的某一列(字段),它可以指定任意值,只要查询的时候指定相应的分区键来查询即可。我们可以对分区进行添加、删除、重命名、清空等操作。

    分桶则是指定分桶表的某一列,让该列数据按照哈希取模的方式随机、均匀的分发到各个桶文件中。因为分桶操作需要根据某一列具体数据来进行哈希取模操作,故指定的分桶列必须基于表中的某一列(字段)。分桶改变了数据的存储方式,它会把哈希取模相同或者在某一个区间的数据行放在同一个桶文件中。如此一来便可以提高查询效率。如果我们需要对两张在同一个列上进行了分桶操作的表进行JOIN操作的时候,只需要对保存相同列值的通进行JOIN操作即可。

    还有一点需要点一下:在hive中的数据是存储在hdfs中的,我们知道hdfs中的数据是不允许修改只能追加的,那么在hive中执行数据修改的命令时,就只能先找到对应的文件,读取后执行修改操作,然后重新写一份文件。如果文件比较大,就需要大量的IO读写。在hive中采用了分桶的策略,只需要找到文件存放对应的桶,然后读取再修改写入即可。

    区别

    1、分桶对数据的处理比分区更加的细化,分区针对的是数据的储存路径,分桶针对的是数据文件
    ​
    2、分桶是按照hash值进行切分的,相对来说比较公平,分区是按照列的值划分,容易造成数据倾斜
    ​
    3、分桶、分区不干扰,分区表可以划分为分桶表

    分区:

    hive中分区分为 : 单值分区、范围分区。
    ​
    单值分区: 静态分区 动态分区
    ​
    如下所示,现在有一张persionrank表,记录每个人的评级,有id、name、score字段。我们可以创建分区rank(rank不是表中的列,我们可以把它当做虚拟列),并将相应的数据导入指定分区(将数据插入指定目录)。

    单值分区:

    单值静态分区:导入数据时需要手动指定分区

    单值动态分区:导入数据时,系统可以动态判断目标分区

    1.静态分区创建:

    直接在PARTITI1ONED BY后面跟上分区键、类型即可(指定的分区键不能出现在定义列名中)

    CREATE [EXTERNAL] TABLE     (  [,   ...])
        -- 指定分区键和数据类型
        PARTITIONED BY  ( , ...) 
        [CLUSTERED BY ...] 
        [ROW FORMAT ] 
        [STORED AS TEXTFILE|ORC|CSVFILE]
        [LOCATION '']    
       [TBLPROPERTIES (''='', ...)];

    2.静态分区写入:

    -- 覆盖写入
    INSERT OVERWRITE TABLE     PARTITION (=[, =, ...]) 
        SELECT ;
    ​
    -- 追加写入
    INSERT INTO TABLE     PARTITION (=[, =, ...])
        SELECT ;

    3.添加分区:

    //只能添加分区列的值,不能添加分区列,如果是多个分区列,不能单独添加其中一个
    alter table tablename add partition(col=value)

    4.删除分区:

    //可以删除一个分区列,但是会把表中所有包含当前分区列的数据全部删除
    alter table tablename drop partition(col=value)

    5.修复分区:

    //手动向hdfs中创建分区目录,添加数据,创建好hive的外表之后,无法加载数据,
    //元数据中没有相应的记录
    msck repair table tablename

    6.动态分区创建:

    创建方式与静态分区表完全一样,一张表可同时被静态分区和动态分区键分区,只是动态分区键需要放在静态分区键的后面(HDFS上的动态分区目录下不能包含静态分区的子目录),如下spk即static partition key(静态分区键),dpk为dynamic partition key(动态分区键)

    CREATE TABLE  PARTITIONED BY ([ , ... ,]  , [,...]);

    7.动态分区写入:

    根据表中的某一个列值来确定hdfs存储的目录:

    优点:

    动态可变,不需要人为控制。假如设定的是日期,那么每一天的数据会单独存储在一个文件夹中

    缺点:

    需要依靠MR完成,执行比较慢

    静态分区键要用 = 指定分区值;动态分区只需要给出分出分区键名称

    -- 开启动态分区支持,并设置最大分区数
    set hive.exec.dynamic.partition=true;
    //set hive.exec.dynamic.partition.mode=nostrict;
    set hive.exec.max.dynamic.partitions=2000;
    ​
    insert into table1 select 普通字段 分区字段 from table2

    范围分区:

    单值分区每个分区对应于分区键的一个取值,而每个范围分区则对应分区键的一个区间,只要落在指定区间内的记录都被存储在对应的分区下。分区范围需要手动指定,分区的范围为前闭后开区间 [最小值, 最大值)。最后出现的分区可以使用 MAXVALUE 作为上限,MAXVALUE 代表该分区键的数据类型所允许的最大值。

    CREATE [EXTERNAL] TABLE     ( ,  , ...)
        PARTITIONED BY RANGE ( , ...) 
            (PARTITION [] VALUES LESS THAN (), 
                [PARTITION [] VALUES LESS THAN (),
                  ...
                ]
                PARTITION [] VALUES LESS THAN (|MAXVALUE) 
            )
        [ROW FORMAT ] [STORED AS TEXTFILE|ORC|CSVFILE]
        [LOCATION '']    
        [TBLPROPERTIES (''='', ...)];

    多个范围分区键的情况:

    DROP TABLE IF EXISTS test_demo;
    CREATE TABLE test_demo (value INT)
    PARTITIONED BY RANGE (id1 INT, id2 INT, id3 INT)
    (
    -- id1在(--∞,5]之间,id2在(-∞,105]之间,id3在(-∞,205]之间
    PARTITION p5_105_205 VALUES LESS THAN (5, 105, 205),
    -- id1在(--∞,5]之间,id2在(-∞,105]之间,id3在(205,215]之间
    PARTITION p5_105_215 VALUES LESS THAN (5, 105, 215),
    PARTITION p5_115_max VALUES LESS THAN (5, 115, MAXVALUE),
    PARTITION p10_115_205 VALUES LESS THAN (10, 115, 205),
    PARTITION p10_115_215 VALUES LESS THAN (10, 115, 215),
    PARTITION pall_max values less than (MAXVALUE, MAXVALUE, MAXVALUE)
    );

    分桶: 把一个大文件拆分小文件来处理

    对Hive(Inceptor)表分桶可以将表中记录按分桶键的哈希值分散进多个文件中,这些小文件称为桶。

    为什么要分桶?

    1、对于分区数量过于庞大、找不到合理的分区字段的时候,可以使用分桶

    2、分区中的数据进一步拆分为桶:采用哈希值将数据打散,然后分发到不同的桶中来完成分桶的工作

    3、分桶的计算方式:hive使用分桶所用的值进行hash,并用hash值得结果除以桶的个数做取余运算的方式,从而保证了每个桶中有数据,但是数据条数不一定相等

    4、如果两个表join的时候,两个表都是分桶表,这就意味着不用再去扫描整个表了,只需要匹配对应的桶,就可以了。该方法也可以提升效率

    5、数据量足够大的情况下,分桶比分区的效率更高

    数据采样

    在开发中,数据量大的情况下,我们为了针对开发做测试,就可以采用分桶来进行数据采样,采样得到的结果是一个具有代表性的查询结果,可以达到快速开发的目的。

    1.创建分桶表:

    分桶表的建表有三种方式:直接建表,CREATE TABLE LIKE 和 CREATE TABLE AS SELECT ,单值分区表不能用 CREATE TABLE AS SELECT 建表。这里以直接建表为例:

    create table  分桶表的表名 ( 字段名1 数据类型,字段名 2  数据类型2 , ....    )
    clustered by ( 分桶字段 )  into  分桶的个数  buckets
    row format delimited    
    fields terminated by '\001'       --指定字段分隔符
    collection items terminated by '\002'  -- 指字集合的分隔符
    map keys terminated by '\003'    --指定map的分隔符
    lines terminated by '\n'   --指定行的分隔符
    create table   students_bucket ( id string, name string, age int   ) 
    clustered by ( id )  sorted by ( id  asc ) into  4 buckets
    ​
    ​
    create table   students_bucket ( id string, name string, java float,c float,oracle float,hadoop float ,sex string  ) 
    clustered by ( id )  sorted by ( id  asc ) into  4 buckets
    row format delimited    
    fields terminated by ',' ;     --指定字段分隔符
    ​
    ​
    insert into students_bucket  select id,name,java,c, mysql as oracle,hadoop,sex from students;
    ​
    -- 分桶只能使用insert 方法来执行MR程序 进行分桶 不能使用load data
    load data local inpath '/home/zx/data/students2.csv' overwrite  into table  students_bucket
    ​