Hive-学习日志-20181226

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_1018944104/article/details/85269248

目录

1、Hive产生背景及作用

2、Hive的数据组织形式

3、Hive的视图

4、数据存储

5、Hive的DDL 和 DML 操作

6、Hive的数据类型

7、Hive的函数

8、内置函数

9、自定义函数:Java语言实现

10、json解析

11、多字节分隔符

12、transform的方式???

13、Hive的beeline连接

14、Hive的Shell

15、Hive的执行过程

16、Hive的数据倾斜

17、Hive的优化

补充知识

Hive的练习


1、Hive产生背景及作用

背景:MapReduce开发成本过高

Hive是什么:底层数据存储在HDFS上,执行引擎是MapReduce

Hive的架构:

  • 用户接口:Cli    ODBC/JDBC    WEB UI
  • thift server:跨语言服务
  • 元数据库:默认元数据库是derby,适合单用户;企业一般使用MySQL作为元数据库。
  • 驱动层:
    • 解释器    hql——>语法树
    • 编译器    语法树——>逻辑执行计划
    • 优化器    对编译的结果进行进行优化,合并相同的MapReduce
    • 执行器    执行最终的优化结果

安装Hive:https://blog.csdn.net/qq_1018944104/article/details/84346764#2.Hive%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA

2、Hive的数据组织形式

库:和关系型数据库中的库概念一致,便于数据库管理,将不同模块的数据存储在不同的数据库中。

表:和关系型数据库中的表概念一致,也是二维表。

Hive中表的分类:

根据属性分为 内部表 和 外部表

  • 内部表,又称为管理表(manage table),表的数据是Hive自己进行管理的,可以自己决定数据的删除或添加,内部表在进行删除的时候原始数据和元数据一并删除。
  • 外部表,表的数据由HDFS管理,Hive只能使用,无法删除。对于外部表来说,Hive仅仅相当于创建了一个和HDFS上的数据的关联的表。

根据功能分为 分区表 和分桶表

分区表(不同于MapReduce的分区)

1、当数据量比较大的时候,在进行查询时,如果每一次都进行全表扫描,必然造成查询性能低,这时就出现了分区表的概念。

2、分区表是将原表的原始数据进行分目录存储 ,相当于对原始表进行一个区块划分,将原来的表分成的很多的区域,这样做目的就是便于查询,在查询的时候可以减少查询的范围。

3、分区表的表现形式:

非分区表的数据存储:hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu(表存储目录),非分区表一个表的所有数据都存储在表的对应目录下,非分区表中一个表对应一个目录。

分区表:将表中的数据分别存储在不同的区下面,将表中的不同区的数据分别存储在不同的目录下。分区表指定一个分区字段,分区字段选择的依据查询的过滤字段。比如:学生信息表  查询的时候通常按照班级查询   分区字段班级
班级  3个   7年级  8年级  9年级
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=7/
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=8/
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=9/
分区表这里一个分区就会对应一个目录结构,我们在查询的过程中,按照分区字段进行过滤查询,这个时候只会扫描指定分区字段值的目录,select * from stu where class=8; 只会扫描 hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=8/
         
分区表总结:
1)分区表对应的不同的目录结构
2)分区表的作用  减少查询的时候的数据扫描范围  提升查询性能

分桶表(类似于mapreduce的分区的概念)
1、作用:1)提升抽样的性能;2)提升join的性能

数据量比较大的时候,先进行数据抽样,抽取样本数据测试,样本数据的要求具有代表性,抽取足够散列,桶表数据就是这样的数据,可以直接拿一个桶的数据作为样本数据,分桶可以提升join的性能。

2、目录划分:将不同的桶的数据进行分别存储在不同的文件中。分桶表来说,就是选择一个分桶字段(mapreduce中的分区字段),选择完成分桶字段之后指定桶的个数(分区的个数/reducetask的个数)。
3、分桶的数据划分:分桶字段.hash%分桶个数
余数0----桶0
余数1----桶1
4、最后数据目录:hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu,按照age分桶  3个桶
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00000  age.hash%3=0
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00001
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00002

3、Hive的视图

视图的作用主要为了提升hql语句的可读性:
1)hive中的视图只存在逻辑视图,不存在物化视图
    逻辑视图:只存储视图代表的hql语句,不会进行执行
    物化视图:将视图对应的查询语句执行出来结果
2)hive的视图相当于一条查询语句的快捷方式
3)hive中的视图,在查询视图的时候才会真正的执行
    create view age_view as select * from stu where age>18;
    select * from age_view; 这个时候视图才会执行
4)hive中的视图不支持insert、update 等操作,只支持查询操作

特点:
    1.只有逻辑视图  没有物化视图
    2.不支持增删改  只支持查询
    3.视图相当于一个hql的快捷方式
    4.视图在查询视图的时候才会真正的执行
    5.hive的视图存储的时候 存在元数据库中仅仅存储的视图代表的sql语句
操作
1.创建视图  ****
create view viewname as select ...
create view age_19 as select * from student_test where age>19;
 
2.显示视图列表
show tables;  展示当前数据库下的所有的表及视图
show views;  只能显示视图
 
3.显示视图描述信息
desc viewname;
desc formatted viewname;
 
4.查询视图  将视图看做普通表   ****
select * from age_19;
 
5.删除视图
drop table viewname ;  不可用
drop view viewname;

4、数据存储

1、元数据存储
元数据是指Hive中库、表的描述信息,存储在关系型数据库中, 默认derby,但一般使用MySQL。
元数据对应的mysql的位置:
        <property>
                    <name>javax.jdo.option.ConnectionURL</name>
                    <value>jdbc:mysql://localhost:3306/myhive?createDatabaseIfNotExist=true</value>
                    <description>元数据库的连接url,myhive指的是hive的元数据在mysql中的库名</description>
                    <!-- 如果 mysql 和 hive 在同一个服务器节点,那么请更改 hadoop02 为 localhost -->
        </property>
元数据的结构:
            hive中的数据库的描述信息:
            DBS表存储的是hive的数据库的描述信息
                    原始数据的hdfs的存储目录
            6       hdfs://hadoop01:9000/user/hive/warehouse/test_bd1808.db test_bd1808 hadoop  USER
            每当hive中创建一个数据库的时候  这个表中就会添加一条数据
             
             
            hive中的表的描述信息的元数据:
            TBLS
                            所属数据库的id            表名  表类型
            1   1542194091  2   0   hadoop  0   1   stu MANAGED_TABLE           0
            每当在hive中创建一个表  这个表中就会添加一条数据
             
            hive表的字段的描述信息:
            COLUMNS_V2
            所属表id 字段名       字段类型    字段顺序
            1       course      string      0
             表中每当添加一个字段  这个表中就会添加一条数据
 
注意:元数据信息可以直接在MySQL中进行修改,一旦元数据修改会造成Hive表结构修改,Hive的表结构(库信息  表信息  字段信息)相关数据从元数据库加载的,所以元数据信息慎重修改。

2、原始数据存储
原始数据:表中存储的数据
存储在hdfs上,默认位置/user/hive/warehouse/
读取的配置文件:hive-default.xml
          <property>
                      <name>hive.metastore.warehouse.dir</name>
                      <value>/user/hive/warehouse</value>
                      <description>location of default database for the warehouse</description>
          </property>
修改:在hive-site.xml添加配置项
        <property>
                    <name>hive.metastore.warehouse.dir</name>
                    <value>/user/hive/hivedata</value>
                    <description>location of default database for the warehouse</description>
        </property>
        修改完成后重新进入hive的客户端就可以了。注意 :修改生效从修改之后创建的库、表的数据目录才会修改,之前的是不会修改的。
原始数据存储的目录结构:/user/hive/warehouse/test.db/stu,当前目录下存储的是该表对应的原始数据文件。

hive的原始数据存储的配置:
    1)hive-default.xml   2)hive-site.xml   3)建表语句 LOCATION
    加载顺序:1)---2)---3)
    生效规则:最后加载的最终生效

5、Hive的DDL 和 DML 操作

具体内容https://blog.csdn.net/qq_1018944104/article/details/85272151

库的操作

    1)创建数据库:create database name;
    2)切换库:use name;
    3)查看库列表:show databases; 或 show databases like 'test*';
    4)查看数据库的描述信息:desc database name; 或 desc database extended db_name; #查看数据库的详细信息
    5)查看正在使用的库:select current_database();
    6)删除库
    drop database name;  只能删除空的
    drop database name restrict;  严格模式下的删除库,会进行库的检查,如果删除库不是空的不允许
    drop database name cascade;  删除非空数据库,级联删除
     
    防报异常操作:
    创建库为了防止异常:create database if not exists test;
    删除库为了防止异常:drop database if exists test;
    注:这两个操作同样适用于表和分区的操作。

表的操作

    1)创建表
    2)查看表的描述信息
    3)查看表的列表
    4)表的修改
    5)表数据的清空
    6)删除表
    7)查看详细建表语句

6、Hive的数据类型

基本数据类型:---  java语言
tinyint  smallint int  bigint
boolean 
float
double
string
timestamp
 
复杂数据类型
1)array:数组类型  一组数据的,数据单一  类型一致的时候使用
数据:id  names
1   zs,xsz,gs
2   ls,xlz,yl,dg
3   ww,xw
字段:id   int
names array
建表语句:create table test_array(id int,names array<string>) row format delimited fields terminated by '\t' collection items terminated by ',';
collection items terminated  指定集合元素之间的分割符
数据加载:load data local inpath '/home/hadoop/tmpdata/test_array' into table test_array;
 结果:1       ["zs","xsz","gs"]
 访问:通过下标   从0开始的  例如 select id,names[2] from test_array;
 
2)map 映射   key-value类型的
数据:id  family
1   dad:zs,mum:hanmeimei
2   sister:lily,brother:john,mum:Alice
字段:id  int
family  map<string,string>
建表语句: create table test_map(id int,family map<string,string>) row format delimited fields terminated by '\t' collection items terminated by ',' map keys terminated by ':';
数据加载:load data local inpath '/home/hadoop/tmpdata/test_map' into table test_map;
访问:通过key找value  [] ,例如 select id,family["mum"] from test_map;
map keys terminated by   map集合中的key  value之间的分割符
注意:分割符指定的时候由外向里指定的,即大——>小

3)struct 类似于java中的对象类型 每一个对象----Class
struct用于存储一组具有相同结构的数据,相同结构指具有相同的列数,每一列对应的含义是一致的。
数据:id  stuinfo
1   zs,23,xian
2   ls,20,wuhan
3   ww,19,sichaun
字段:id  int
stuinfo struct
结构体定义: 
class stu{
string name;
int age;
string jiguan;
}
建表语句:create table test_struct(id int,stuinfo struct<name:string,age:int,jiguan:string>) row format delimited fields terminated by '\t' collection items terminated by ',';
加载数据:load data local inpath '/home/hadoop/tmpdata/test_struct' into table test_struct;
访问:对象类型的访问,对象.属性,例如 select id,stuinfo.name from test_struct;

练习题目:
id  courses     hobby       info
1   sx:23,yw:67 sleep,chi   zxm,28,jx
2   ty:99,yy:89 singing,sport,sleep cx,35,sz
建表关联  并进行数据导入

7、Hive的函数

MySQL中也有函数,比如 sum、avg、max、min、count,为了便于数据处理和统计分析就出现了函数。
按照来源分:  内置函数 和 自定义函数
内置函数:hive中自带的函数
show functions;   查看所有的内置函数
desc function name;  查看函数的描述信息,比如 floor(参数) - Find the largest integer not greater than x
desc function extended name;  查看函数的详细描述信息,带案例的描述信息
常用的内置函数: 这个很重要,收集资料来搞定

8、内置函数

1、数值类型:
        round(需要处理的数据,[位数])  四舍五入取近似值
        floor  向下取整
        ceil   向上取整
        rand([seed])   rand()  取随机值
2、字符串:
        字符串的起始下标从1开始的,从左侧向右侧
        字符串也可以从右向左访问  -1
        字符串截取:substr(需要处理的字符串,起始下标,截取长度) 或 substring
        字符串拼接:concat 或 concat_ws  年-月-日
        字符串的切分:split  返回的类型数组类型  这也是获取数组类型的一种方式
        字符串查找:instr(str,substr)  存在则返回子字符串第一个字符所在的位置 >0的数,不存在则返回0
        字符串替换:replace(str, search, rep)
        if(判断条件,返回1,返回2)   三目表达式
        select if(names[2] is not null,names[2],"dlreba") from test_array;
        处理null值的函数:nvl(查询的字段, 默认值),如果第一个参数为null   则返回第二个参数的值,不为null  则返回第一个参数 
        类型转换的函数:cast(需要处理的数据 as 处理成的类型),例如 select cast("1" as int);
        求字符串长度:length
3、集合生成函数:
        array  数组生成函数
        array_contains  判断数组中是否存在某个元素
        map  映射生成函数  参数必须是偶数个
        奇数位置的  key   偶数位置  value
4、日期处理函数:
        unix_timestamp(date[, pattern])   生成时间戳的
        将给定的日期转换为时间戳
        unix_timestamp()   获取当前系统的时间戳
        current_timestamp() 获取当前系统的时间戳
        select unix_timestamp('2018-11-11','yyyy-MM-dd');
        时间戳转日期
        from_unixtime(unix_time, format)
        select from_unixtime(1541865600,"yyyy/MM/dd HH:mm:ss");
        year   取给定的日期或时间戳的年份
        month  取月
        day  取日期的
        hour  取小时
        weekofyear    select weekofyear("2018-11-22");
5、表生成函数:进一路出多路
        explode(array|map) : explode炸裂完成之后可以看做一个表
        1)将数组多个元素炸裂到多行,每一个元素放在一行,每一行只有一列
        [1,2,3]
        1
        2
        3

        2)将map集合的元素炸裂到多行,每一个元素一行,每一行都有两列 key 和 value
        {1:2,3:4}
        1   2
        3   4
        select id,explode(names) from test_array; 相当于 select id,表 from 表; 后者是不允许的
        UDTF's are not supported outside the SELECT clause
        解决:当explode和其他字段一起查询时,explode函数炸裂出来的内容可以当做一个表  
        select id,v.* from test_array lateral view explode(names) v;
        select id,v.* from test_map lateral view explode(family) v;
        select id,v.* from test_map lateral view explode(family) v as 列别名;
        select id,v.vk from test_map lateral view explode(family) v as vk,vv;
        虚拟视图的名字v
        vk      vv
        dad     zs
        mum     hanmeimei
         
        explode函数和表中的普通字段一起查询的时候,一定要注意将explode炸裂的内容放在一个虚拟lateral view中。
        分析函数:数据统计分析的时候用的
        row_number 添加行号的(分组求topN),必须和over子句一起用,而 over子句是用于添加规则的。
        语法:row_number() over(分组条件,排序条件)
        over子句中可以放两种形式:
        1)distribute by 指定分桶  sort by 指定排序
        2)partition by 指定分桶   order by指定排序
        在每一组中进行顺序添加行号。
        案例:学生信息表的每一个部门中年龄最大的前2个
        分组:部门  排序:年龄,求的是局部的年龄最大的
        select *,row_number() over(distribute by department sort by age desc) from student_test;
        执行过程:先按照指定的分桶(分区)规则进行分桶/分区
                            再在每一个分桶/分区中按照指定的排序规则进行排序
                            最后再在每一个桶中添加行号
        结果:
        95006   孙庆    男      23      CS      1
        95013   冯伟    男      21      CS      2
        95001   李勇    男      20      CS      3
        95012   孙花    女      20      CS      4
        95014   王小丽  女      19      CS      5
        95010   孔小涛  男      19      CS      6
        95008   李娜    女      18      CS      7
 
        95020   赵钱    男      21      IS      1
        95002   刘晨    女      19      IS      2
        95004   张立    男      19      IS      3
        95019   邢小丽  女      19      IS      4
        95018   王一    女      19      IS      5
        95017   王风娟  女      18      IS      6
 
        95003   王敏    女      22      MA      1
        95022   郑明    男      20      MA      2
        95007   易思玲  女      19      MA      3
        95015   王君    男      18      MA      4
        95011   包小柏  男      18      MA      5
        95009   梦圆圆  女      18      MA      6
        95005   刘刚    男      18      MA      7
        95021   周二    男      17      MA      8 
        最终的语句
        select * from (select *,row_number() over(distribute by department sort by age desc) index 
        from student_test) t where index<=2; 
        应用场景:分组求topN

        添加排名rank 和 dense_rank,也要over子句一起用,over子句指定分桶/分区的依据,指定排序的依据
        例子:求每个部门的按年龄降序添加排名
        select *,rank() over(distribute by department sort by age desc)  from student_test;
        结果: 排名的时候将并列的进行直接累加跳过
        95006   孙庆    男      23      CS      1  
        95013   冯伟    男      21      CS      2  
        95001   李勇    男      20      CS      3  
        95012   孙花    女      20      CS      3  
        95014   王小丽  女      19      CS      5   
        95010   孔小涛  男      19      CS      5   
        95008   李娜    女      18      CS      7  
        select *,dense_rank() over(distribute by department sort by age desc)  from student_test;
        结果:顺序添加排名的  有并列的不进行排名名次累加的
        95006   孙庆    男      23      CS      1
        95013   冯伟    男      21      CS      2
        95001   李勇    男      20      CS      3
        95012   孙花    女      20      CS      3
        95014   王小丽  女      19      CS      4
        95010   孔小涛  男      19      CS      4
        95008   李娜    女      18      CS      5
注意:Hive中271个内置函数,当内置函数无法满足业务需求的时候需要自定义函数

9、自定义函数:Java语言实现

Hive的自定义函数可以分为3类
1、UDF---USER DEFINE FUNCTION 用户自定义函数
    处理一条数据处理完成之后还是一条数据,字符串函数
2、UDAF---USER DEFINE AGGREGATE FUNCTION  用户定义聚合函数
    一次加载多条数据 处理完成就剩一条数据,进多路出一路,比如 count、sum、max、min
3、UDTF---USER DEFINE TABLE FUNCTION 用户自定义表函数
    一次加载一条数据,处理完成之后变为多条数据,比如 explode(array|map)

自定义函数实现步骤:
1)创建工程  导入依赖包
2)创建一个类继承UDF类
3)实现一个或多个名字为evaluate的方法
    evaluate方法是会被hive底层执行器和解析器调用到的
    evaluate  的返回值和参数是根据实际的业务需求自己定义的
    参数:函数调用的时候传入的参数
    返回值:函数调用完成后返回的值
    hive> select round(2.3);
    OK
    2
    round函数:
    evaluate方法
    参数类型:double
    返回值:int
    public int evaluate(double a){}
    求三个数的和
4)定义完成之后,将UDF的代码 打成jar包上传到服务器
5)将jar包添加到hive的classpath下
Hive的客户端执行:add jar /home/hadoop/tmpdata/myudf.jar;
验证:list jar/jars;
hive> list jar;
/home/hadoop/tmpdata/myudf.jar
6)创建一个临时函数  关联UDF
create temporary function three_add as 'com.ghgj.cn.testUDF.MyUDF';
验证:show functions; 
使用:select three_add(1,3,4); 当使用这个自定义函数的时候 本质调用com.ghgj.cn.testUDF.MyUDF下的evaluate方法
注意
1、临时函数的作用域只对当前客户端生效,当前客户端退出临时函数就删除了,再次进入客户端  如果还想用函数  需要重新添加,重新操作上面的5   6步。
2、同一个UDF中可以写多个evaluate方法的,调用的时候根据传入的参数的不同调用不同的evaluate方法。
3、写evaluate方法的时候注意:方法必须是public的 并且 返回值不能为void

10、json解析

Hive中接触的数据有很多数据都是json格式的数据。
json格式类似于java中的类的结构  对象
{
      属性:值,
      属性:[{},{}],
      属性:{
            属性:值,
            属性:{
            }
      }
}
web  前后台之间数据传输的时候 json格式
json格式数据的解析方式:1)自定义函数;2)内置函数解析 get_json_object
用法:get_json_object(json_txt, path)
          参数1:需要解析的json串
          参数2:需要解析的json中的属性的位置
          返回值:返回的是查询的路径对应的值
{
      属性1:值,
      属性2:[{},{}],
      属性0:{
            属性3:值,
            属性:{
            }
      }
}
json串的目录结构
            最外层的结构叫做json的根目录   $  根目录的对象
            属性1叫做根目录的子目录  取的时候用 $.属性0.属性3    $.属性2[0]
支持的路径的表达:
            $   : 根目录
            .   : 子节点  
            []  : 属性对应的值如果是数组的时候 用于取下标的  取到数组中对应的值
例如:[{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}] ,取rate对应的值路径:$.content[0].rate
select get_json_object('{"content":[{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}]}','$.content[0].rate');

11、多字节分隔符

Hive中的数据的分割符默认的都是单字节形式的,但是在实际中采集的数据有多字节分割符的数据,比如 1::zs::23
建表:create table test01(id int,name string,age int) row format delimited fields terminated by '::';
加载完数据并查询:1               NULL
原因:虽然表定义了多字节的分割符,但默认类中不能识别,只能识别单字节的分割符,实际分割的时候按照 :分割的。
所以,1::zs::23 分割完成:1   空   zs(null)    

如何解决多字节分隔符
    1)将数据中的多字节分割符替换为单字节分割符
    这种方式替换的时候要求必须足够的了解数据,防止替换的单字节在原始数据中原来已经存在。
    2)可以修改源码,将源码中的单字节替换为多字节分割符,不可取, 一般不用
    3)采用自定义的输入输出的格式
    在建表的时候 
    SerDe Library:          org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe 只支持单字节的      
    InputFormat:            org.apache.hadoop.mapred.TextInputFormat         
    OutputFormat:           org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat    
    输入:正则表达式  
    (.*)::(.*)::(.*)
    输出:在正则表达式中取
    取正则表达式的第一组  第二组
    1::sgds:wgw::23 
    2::sgds:wgw::23
    3::sgds:wgw::23 
    解决:
    解析类:正则表达式的解析
    输入类:正则表达式
    输出:取
    create table test02(id int,name string,age int) 
    row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
    with serdeproperties('input.regex'='(.*)::(.*)::(.*)', 'output.format.string'='%1$s %2$s %3$s')
    stored as textfile;

    serde:指定解析类:org.apache.hadoop.hive.serde2.RegexSerDe
    with serdeproperties  指定解析属性的
        input.regex指定输入的正则表达式的
        output.format.string  指定需要取的组
    加载数据:
    load data local inpath '/home/hadoop/tmpdata/test01' into table test02;
    1|||rs|||67|||78
    create table test03(id int,name string,yuwen int,shuxue int) 
    row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
    with serdeproperties('input.regex'='(.*)\\|\\|\\|(.*)\\|\\|\\|(.*)\\|\\|\\|(.*)', 'output.format.string'='%1$s %2$s %3$s %4$s')
    stored as textfile;

12、transform的方式???

Hive 解析脚本的方式。
案例:test_json中求周一到周日期间哪一天的评分人数最多?
1)将数据评分时间的字段--->周几
    1)自定义函数
    2)内置函数
    3)用脚本方式
     
python脚本
#!/usr/bin/python
import sys
import datetime
for line in sys.stdin://取每一条数据
 line = line.strip()
 movie,rate,unixtime,userid = line.split('\t')
 weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
 print '\t'.join([movie, rate, str(weekday),userid])

concat_ws()
strip()  trim    

13、Hive的beeline连接

beeline连接是生产用常用的一种连接Hive的方式,即Hive的远程连接的方式。
远程连接hive的步骤:
    1)将hive的服务端启动
    命令:hiveserver2   #开启hive的服务端
    2)远程使用beeline连接
    命令:beeline   #启动beeline工具
    beeline>   #进入beeline的客户端
    3)进行配置hive的连接
    命令:!connect  jdbc:hive2://hadoop02:10000 #末尾是Hive连接的url,类比 mysql连接url   jdbc:mysql://主机:3306
    进行连接
    beeline> !connect jdbc:hive2://hadoop02:10000
    Enter username for jdbc:hive2://hadoop02:10000: 输入安装hive的linux的用户名
    Enter password for jdbc:hive2://hadoop02:10000: 输入的是安装hive的用户对应的密码
    报错
    Error: Could not open client transport with JDBC Uri: 
    jdbc:hive2://hadoop02:10000: 
    Failed to open new session: 
    java.lang.RuntimeException: 
    这是hadoop的远程连接报错  hadoop的安全认证错误
    org.apache.hadoop.ipc.RemoteException
    (org.apache.hadoop.security.authorize.AuthorizationException)
    : User: hadoop is not allowed to impersonate hadoop (state=08S01,code=0)
    报错原因
    beeline连接hive的时候 实质上连接hadoop集群
    hadoop集群没有远程安全验证
    解决方案
    修改hadoop的配置文件  将hadoop的远程连接的安全认证添加上,具体如下:
    修改hadoop集群的所有节点的以下两个配置文件
    1)hdfs-site.xml
    <property>
     <name>dfs.webhdfs.enabled</name>
     <value>true</value>
    </property>

 
    2)core-site.xml
    <property>
    <name>hadoop.proxyuser.hadoop.hosts</name>
    <value>*</value>
    </property>
    <property>
    <name>hadoop.proxyuser.hadoop.groups</name>
    <value>*</value>
    </property>

    注意
    hadoop.proxyuser.远程连接的用户.hosts
    hadoop.proxyuser.远程连接的用户.groups
    将上面的两个配置文件  远程发送到其他节点  **********
    scp core-site.xml hadoop02:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
    scp core-site.xml hadoop03:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
    scp hdfs-site.xml hadoop02:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
    scp hdfs-site.xml hadoop03:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
    重启集群  *****
     
    上面的修正完成,就可以
    1)将hive的服务端启动
    hiveserver2   开启hive的服务端
    2)远程使用beeline连接
    beeline   启动beeline工具
    beeline>   进入beeline的客户端
    进行连接
    beeline> !connect jdbc:hive2://hadoop02:10000
    Connecting to jdbc:hive2://hadoop02:10000
    输入hive的用户名  安装hive的用户名
    Enter username for jdbc:hive2://hadoop02:10000: hadoop
    输入安装hive的用户的密码
    Enter password for jdbc:hive2://hadoop02:10000: ******
    Connected to: Apache Hive (version 2.3.2)
    Driver: Hive JDBC (version 2.3.2)
    Transaction isolation: TRANSACTION_REPEATABLE_READ
    0: jdbc:hive2://hadoop02:10000> 连接成功  执行hql
    注意:在启动hiveserver2的时候  想要启动为后台的进程
    nohup hiveserver2 1>/home/hadoop/hiveserver.log 2>/home/hadoop/hiveserver.err &
    nohup   表示no hang up  不要挂起
    1  代表的标准输出
    2  代表的是错误输出

14、Hive的Shell

进入hive的客户端之后
    quit 退出hive的客户端
    set key=value   进入hive的客户端之后进行参数设置,key: 就是 hive-default.xml 和 hive-site.xml 中的   <name>key</name>
    这种参数设置只对当前的客户端生效,当前客户端退出则参数失效。
    比如 set hive.exec.mode.local.auto=true;
    set key  查看指定参数的值
    set -v 打印所有的hive或hadoop的参数配(了解)
    add FILE [file]   添加文件到hive的classpath下
    add jar jarname  UDF 中  添加jar文件到hive的classpath下
    list FILE /files
    list jar/jars     查看当前的classpath下的文件或jar包资源
    ! linux命令   在hive的客户端执行linux命令,仅限查询相关的一些命令
    dfs [dfs cmd]   ***** 在hive的客户端执行hdfs相关的命令,比如 dfs -ls /;
    hadoop fs -ls /  老的命令   
    hdfs dfs -ls /   新的命令
    hadoop/hdfs  开启hadoop/hdfs的客户端的  
    ddl   dml   
    source FILE
   在hive的客户端执行sql脚本的
    source sql脚本的位置    执行一个sql脚本
进入hive的客户端之前
    语法结构:hive [-hiveconf x=y]* [<-i filename>]* [<-f filename>|<-e query-string>] [-S]
    说明:
    1、-i 从文件初始化 HQL
    2、-e 从命令行执行指定的 HQL
    3、-f 执行 HQL 脚本
    6、-hiveconf x=y(Use this to set hive/hadoop configuration variables)
    7、-S:表示以不打印日志的形式执行命名操作
     
    -hiveconf key=value
            在启动hive之前设置hive的相关参数,每次只能初始化一个参数
            比如 hive -hiveconf hive.exec.mode.local.auto=true
    -i   init  从文件初始化hive的相关参数,一次性在启动hive的时候初始化多个参数
    -e hql  不进入hive客户端的情况下执行hql语句,比如 hive -e "show databases"
            注意:执行查询语句的时候 表名之前一定加上库名的
            hive -e "select * from yingping.users limit 10"
    -f   执行一个hql语句的脚本,类似mysql中的source的作用
          hive -f hql脚本的路径

15、Hive的执行过程

cli  thift server    元数据   驱动(解释  编译  优化 执行)
解释器:将hql语句----- 语法树   一组操作符构成的
 
查看hive语句的执行计划:
explain select 
a.Gender Gender,c.Title Title,avg(b.Rating) avgrate 
from 
users a join ratings b on a.UserID=b.UserID 
join movies c on b.MovieID=c.MovieID 
where a.Gender='F' 
group by a.Gender,c.Title 
order by avgrate desc limit 10;
 
Fetch Operator  数据抓取操作符
Filter Operator   过滤的操作符
Map Join Operator    关联的操作符
Group By Operator     分组操作符
 
hive首先将hql语句转换成一组操作符的 树
    操作符是hive处理数据的最小单元
    操作符:
        hdfs的读写操作  fetch  
        MapReduce任务

几个语句的执行过程
1、join过程
    mapjoin
        大*小
        小*小
        将其中一个小表放在每一个maptask的节点的缓存中
        在map端
            setup中读取缓存中的表  放在map中  key=关联建  value=其他
            map中读取另外一个表进行关联
    reducejoin
        users   ratings
        select * from users a join ratings b on a.userid=b.userid; 
        map端:
            key:关联建
            value:打标记+其他
        reduce端:
            两个表中关联建相同的的所有的数据
            直接进行关联
2、group by的过程:
SELECT pageid, age, count(1) FROM pv_users GROUP BY pageid, age;  
map端:
    key:pageid, age
    value: 1
reduce端:
    对value求和就可以了
    注意:当group by和聚合函数一起使用的时候 hive会默认进行优化,比如 sum、max、min等会先在map端执行combiner
3、去重过程:
将需要去重的字段放在map端的key中,这样的话在reduce端就会分到一组 一组中取一个。
SELECT age, count(distinct pageid) FROM pv_users GROUP BY age;
map端的key:age+pageid

16、Hive的数据倾斜

hadoop(广义  hadoop,hive,hbase)不怕数据量大  怕数据倾斜
概念:进行数据计算的时候,由于数据分布不均匀,造成某一个节点上分配的数据量很多,造成这个节点的计算任务很大
表现:hive的数据倾斜,说到底就是mapreduce的数据倾斜,本质就是reduce端的数据倾斜
    reduce端的数据分配取决于分区算法(默认  hash   自定义)
    10个分区中,若9个分区--9reducetask处理200M的数据,另外1个分区--reducetask处理5G的数据,那么结果如下:
    map 20%   reduce 0%
    map 80%   reduce 0%
    map 100%   reduce 40%
    map 100%   reduce 90%
    map 100%   reduce 90%
    map 100%   reduce 90%
    map 100%   reduce 90%
    map 100%   reduce 90%
    ......................
    以上展示的结果就是产生数据倾斜了
 
不会产生数据倾斜的场景
    1)hive执行过程中fetch的过程都不会产生数据倾斜,fetch的过程不需要转换为MR任务。
    more
    select 
    filter where
    limit
    2)group by 和聚合函数一起使用的时候
    group by和聚合函数一起使用的时候默认在map端执行一次聚合函数(combiner),大大减少reduce端的数据量。

会产生数据倾斜的场景
    1)聚合函数不和group by一起使用的时候
    select count(*) from weibo;
    weibo    3T
    map端:
        key:“a”
        value:1
    reduce端:
        所有的数据分配到一个节点上  一个reducetask上
    求的聚合函数就是全局的聚合,因此只能一个reducetask任务
    2)count(distinct )
    先去重   多个reducetask,count()   全局的
    3)join——reducejoin
    mapjoin   不会产生数据倾斜的
    reducejoin   很大程度上会产生数据倾斜
        map-key:join的关联建
        log     address  北京     内蒙古
        user    address  北京     内蒙古
                        9T          1T
        REDUCETSK:
        reducetask0---北京   9T
        reducetask1---内蒙古  1T
         
分析场景:
1、join的时候null值过多
    log----电商日志  10T   userid=null  4T
    userid   order   money   product  ...
    user----用户注册的时候生成的
    userid  name   address  num
    select * from log a join user b on a.userid=b.userid;
    所有的userid=null的数据全部分配到一个reducetask中
    就会产生数据倾斜
    解决方案
    1)null值不参与连接
    select * from (select * from log where userid is not null) a join user b on a.userid=b.userid;
    select * from (select * from log where userid is not null) a join user b on a.userid=b.userid 
    union all 
    select *,null,null,null from log where userid is null;
    2)给null值加随机数
    null--null123
    null---null234 
    select * from log a join user b on case when a.userid is null then concat(a.userid,rand()) then a.userid end=b.userid;
    方案2优于方案1
2、join的关联建的数据类型不统一
    select * from log a join user b on a.userid=b.userid;
    a.userid = string
    b.userid = int 
    hive中默认关联的时候会将string类型转换为int类型
    string ----  "123"   ---int    123
    string ----  "123 "   ---int    null 
    解决方案
    将其中一个表的数据类型转换  将两个表的类型统一,比如 b.userid   int ---- string
3、大*大关联的时候
    1)大*小  小*小   hive中mapjoin   reducejoin
    决定hive是否启动mapjoin的是 hive.auto.convert.join=true 默认启动hive的mapjoin。
    但不是所有的join都执行mapjoin,这是有文件大小限制的。     
    决定执行join时候,若小表的大小如果在下面的范围内则默认执行mapjoin
    hive.smalltable.filesize or 
    hive.mapjoin.smalltable.filesize
    hive.mapjoin.smalltable.filesize=25000000
    也就是说,在进行join的小表大小在25M以内,则默认执行的都是mapjoin
    若小表大小超过25M,则默认执行的是reducejoin
    2)大*中   
    中表指的是表的大小超过25M的但是有不是很大的  每个节点中的缓存可以承受的
    大    2T   user
    中    200M  log
    默认执行执行reducejoin
    1)效率低
    2)容易产生数据倾斜
    那么,就强制执行mapjoin
    语法:/*+mapjoin(a)*/
    select  /*+mapjion(a)*/*  from log a join user b on a.userid=b.userid;
    3)大*大
    user   3T  存储的是建站以来的所有用户信息
    log    某一天的日志   20G   
    解决方案
              1)将一个大表进行切分
              分区表:将user表切成分区表, log表关联每一个分区表,大*大===n  大*小
              分桶表:两个表都进行按照统一的规则进行切分大*大===n  小*小   大*小
              2)将其中一个表瘦身
              将其中的一个表的数据进行一步过滤,抽取出来可以进行关联的数据,将不能进行关联的数据删除。
              user   3T  存储的是建站以来的所有用户信息 30亿
              log    某一天的日志   20G    2000w
              50W   50w     500M    500/10=50w
              500w       5G/10
              user表瘦身,根据log表进行瘦身如下:
              第一步:求出log表中去重之后userid
              select distinct userid from log;
              第二步,根据第一步的结果对user表瘦身
              select /*+mapjoin(a)*/b.* from (select distinct userid from log) a join user b on a.userid=b.userid;
              第三步,开始最后关联
              select /*+mapjion(c)*/* from (select /*+mapjoin(a)*/b.* from (select distinct userid from log) a 
              join user b on a.userid=b.userid) c 
              join log d on c.userid=d.userid;
            
如何获取分区表的一个分区中的数据呢?
在查询的时候将分区字段进行过滤 select * from biao where dt="20181123"
分区表在查询的时候,将分区字段作为普通字段查询就可以了。

如何获取某个桶中的数据?
语法:tablesample (bucket x out of y)
y:桶簇的个数
桶簇:一个(半个)或多个桶组成的集合
student_buk  3个桶
y=1   只有一个桶簇   这个桶簇中包含所有的桶的
y=2   分为两个桶簇   每一个桶簇包含1.5个桶的
y=3   分为3个桶簇    每一个桶簇1个桶
y=6   6个桶簇     每个桶簇3/6=0.5桶
x:代表取得数据是第几个桶簇的数据
x=1   代表取的是第一个桶簇的
 
y=3 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 3);  

y=6   取第2个桶的数据,6个桶簇  每个桶簇  0.5个桶的数据
select * from student_buk tablesample(bucket 2 out of 6);
select * from student_buk tablesample(bucket 5 out of 6);

y=9  取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 9);
select * from student_buk tablesample(bucket 5 out of 9);
select * from student_buk tablesample(bucket 8 out of 9);

    
面试题:mapreduce如何实现大数据(4T)量的全局排序?hive的order by你觉得是如何实现的?
答案:分区,范围分区

17、Hive的优化

1、排序
    全局排序 order by(reducetask只能是一个?  可以有多个的,性能消耗大)
    局部排序
            sort by 局部排序  对每一个reducetask结果进行排序的
            cluster by 先根据指定的字段分桶,再在每一个桶中排序
            distribute by +sort by   指定字段分桶  指定字段排序
    合理选择排序
2、合理使用笛卡尔积
    尽量避免使用笛卡尔积,若场景中非要做笛卡尔积,那么可以考虑以下情况
    大*(只有几条数据的小表)  
    开启hive的笛卡尔积的开关 
    select * from a,b;
    select * from a join b;
    大*中
    hive中笛卡尔积可以执行但是性能很低,map的key不好确定。
    解决方案
    人为的添加关联键
    1)小表的关联键随机添加
    2)将大表复制多份,小表去重后关联建的个数,给每一份大表数据添加关联建都是小表的其中一个关联建
    3)开始真正的关联
3、in/exists  性能低
left semi join  
4、合理maptask的个数
    切片
        太小  maptask个数很多,大量的时间浪费在maptask启动销毁上,不划算的。
        太大  maptask的并行度不够
        一个切片---block---maptask
    原始数据都是大量的小文件的时候,会进行一个小文件合并减少maptask的个数。
5、jvm重用
    一个maptask/reducetask---->container---->yarnchild
    mapred.job.reuse.jvm.num.tasks=1  默认值 
    默认情况下,一个container只会运行一个maptask或reducetask
    set mapred.job.reuse.jvm.num.tasks=5 #一个container只会运行多个task任务
    uber :false    针对maptask数据量比较小的时候,一个container中启动10个maptask,默认关闭的,参数值改为 true。
6、合理设计reducetask的个数
    最多不超过  datanode*0.95
7、小文件合并
    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    #执行 Map 前进行小文件合并
    set hive.merge.size.per.task = 256*1000*1000 #合并文件的大小
    默认情况下hive在map输入数据之前进行小文件合并的
    生产上一般会提前进行手动合并,减轻hdfs的namenode压力。 
8、合理进行分区和分桶
分区表:
当一个表中的数据很大的时候,这个时候为了提升表的查询性能,这个时候需要考虑将这个表建为分区表。
作用:减少以分区字段作为过滤条件的扫描范围,提升性能
student_ptn   分区字段   grade
select * from student_ptn where grade=1303; #只扫描分区1303
select * from student_ptn where yuwen>23; #全表扫描 
分区字段:选经常用于过滤的字段,可以多个--多级分区
分区字段在过滤查询的时候当做普通字段处理 
分桶表:
每个桶中的数据:分桶字段.hash%桶的个数
1)提升抽样的性能:直接抽取某一个桶的数据作为样本数据
2)提升join的性能:直接拿去两个表中对应的桶的数据进行关联就可以了 
如何获取某个桶中的数据?
语法:tablesample (bucket x out of y)
y:桶簇的个数
桶簇:一个(半个)或多个桶组成的集合
student_buk  3个桶
y=1   只有一个桶簇   这个桶簇中包含所有的桶的
y=2   分为两个桶簇   每一个桶簇包含1.5个桶的
y=3   分为3个桶簇    每一个桶簇1个桶
y=6   6个桶簇     每个桶簇3/6=0.5桶
x:代表取得数据是第几个桶簇的数据
x=1   代表取的是第一个桶簇的
 
y=3 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 3);  

y=6   取第2个桶的数据,6个桶簇  每个桶簇  0.5个桶的数据
select * from student_buk tablesample(bucket 2 out of 6);
select * from student_buk tablesample(bucket 5 out of 6);

y=9  取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 9);
select * from student_buk tablesample(bucket 5 out of 9);
select * from student_buk tablesample(bucket 8 out of 9);

如何获取分区表的一个分区中的数据呢?
在查询的时候将分区字段进行过滤 select * from biao where dt="20181123"
分区表在查询的时候,将分区字段作为普通字段查询就可以了。

补充知识

hive的本地模式:set hive.exec.mode.local.auto=true; 

Hive的练习

https://blog.csdn.net/qq_1018944104/article/details/85272888

hive-03中的作业包括:答案在hive-05中

1、微博案例(有讲解)

2、Hive影评案例(有讲解)

3、Hive面试题:https://blog.csdn.net/qq_1018944104/article/details/85298109

猜你喜欢

转载自blog.csdn.net/qq_1018944104/article/details/85269248