什么是Hive

  • Hive由Facebook实现并开源,是基于Hadoop的一个数据仓库工具,可以将结构化的数据映射为一张数据库表,并提供HQL(Hive SQL)查询功能,底层数据是存储在HDFS上
  • Hive本质:将SQL语言转化为MapReduce任务执行,便不熟悉MapReduce的用户很方便地利用HQL处理和计算HDFS上的结构化的数据,是一款基于HDFS的MapReduce计算框架
  • 主要用途:用来做离线数据分析,比直接用MapReduce开发效率更高

为什么使用Hive

  • 直接使用Hadoop MapReduce处理数据所面临的问题
    • 人员学习成本太大
    • MapReduce实现复杂度查询逻辑开发难度太大
  • 使用Hive

Hive 简介:

  • 基于Hadoop数据保存到HDFS
  • 数据仓库工具
  • 结构化的数据映射为一张数据库表
    • 01 张三 89
    • 02 李四 91
    • 03 赵武 92
  • HQL查询功能(hive SQL)
  • 本质把HQL翻译成MapReduce降低使用hadoop计算的门槛
  • 离线数据分析开发效率比直接使用MapReduce高
  • 用户接口:shell命令行

  • 元数据存储

    • 数据库 表 都保存在哪一些位置上
    • 表中的字段名字 类型
    • mysql derby(自带)
  • Drive
    • 负责把HQL翻译成mapreduce
    • 或者翻译成shell命令

Hive与Hadoop的关系(hive其实只做一件事情,那就是翻译)
Hive利用HDFS存储数据,利用MapReduce查询分析数据
Hive是数据仓库工具,没有集群的概念,如果想提交Hive作业只需要在hadoop集群Master节点上装Hive就可以了

hive支持的数据类型

  • 原子数据类型
    • TINYINT SMALLINT INT BIGINT BOOLEAN FLOAT DOUBLE STRING BINARY TIMESTAMP DECIMAL CHAR VARCHAR DATE
    • 复杂数据类型
      • APPAY(数组)
      • MAP(字典)
      • STRUCT(结构体)
    • Hive中表的类型
      • 托管表(managed table)(内部表)
      • 外部表

        一、hive的基本使用

        1、创建数据库

        CREATE DATABASE TEST;

        2、显示所有的数据库

        SHOW DATABASE;
        注:在实践中利用show databases;(多加一个了s)
        问题1:为什么我的集群无法显示DATABASE
        问题2:我为什么只用hadoop10来进行hive数据表格的读取,而忽略了其他可以实现的功能。

        3、创建表

        1. CREATE TABLE STUDENT(calssNo string,stuNo string,score int) ROW format delimited fields terminated BY ',';

注:row format delmited fields terminated by ',';指定了的分隔符为逗号,所以load数据的时候,load的文本也要为逗号,否则加载后为NULL,hive只支持单个字符的分隔符,所以hive默认的分隔符为是\001

4、将数据load到表中

在本地文件系统中创建一个如下的文本文件:/home/hadoop/tmp/student.txt

  • c01,N0101,82
  • c01,N0102,59
  • c01,N0103,65
  • c02,N0201,81
  • c02,N0202,79

load data local inpath '/home/hadoop/tmp/student.txt' overwrite into table student;(在hive表格里面实行,将数据hive表格里面)
注:这个命令将student.txt文件复制到hive的warehouse目录中,这个目录由hive.metastore.warehouse.dir配置项设置,默认为/user/hive/warehouse。Overwrite选项将导致Hive事先删除student目录下所有的文件,并将文件的内容映射到表中。
Hive不会对student.txt做任何处理,因为Hive本身并不强调数据的存储格式
疑问:我用满哥的账号是无法访问:/hive/warehouse目录下的文件
问题一:我show tables 显示的表格到底是什么表格,为什么不能访问
问题二:show tables为什么不能显示我自己临时建立的表格:猜想:warehouse下面有一些库,有的库比如临时表库我可以访问,而有的库我却无法再访问。
问题三(猜想):show tables显示出来建立的表格可能不是放在某一个数据库的下面。

5、查询表中的数据更SQL类似

  1. select * from student;

6、分组查询group by和统计count

  1. select classNo,count(score) from student where score>=60 group by classNo;

从执行结果可以看出hive把查询的结果变成MapReduce作业通过hadoop执行

总结1

Hive的基本使用
1、创建表
2、字段不需要指定多少字节
3、需要通过row format delimited fields terminated by ',';指定列的分隔符
4、加载表数据的时候尽量使用load data方式 把整个文件put上去

Hive的内部表和外部表
创建一个外部表

  1. CREATE EXTERNAL TABLE student2 (classNo string,stuNo string,score int) row format delimited fields terminated by ',' location '/tmp/student';

装载数据

  1. load data local inpath '/home/hadoop/tmp/student.txt' overwrite into table students;

显示表信息

  1. desc formatted table_name;

删除表查看结果

  1. drop table student;

再次创建外部表student2
不插入数据直接查询查看结果

  1. select * from student2;

总结2

1、managed table
创建表的时候:
CREATE TABLE 表名(字段名 字段类型)row format delimited fields terminated by ',';
删除表part
删除元数据和数据一起删除
数据位置/user/hive/warehouse,默认是
2、external table
建立表的时候
CREATE External TABLE 表名(字段名 字段类型)row format delimited fields terminated by ',' location ';数据再hdfs上的路径’;
删除表
只删除元数据,数据会保留
数据可以在hdfs上面的任意的位置

二、分区表

2.1 什么是分区表

1、随着表的不断增大,对于新记录的增加,查找,删除等(DML)的维护也更加的困难,对于数据库中的超大型的表格。可以通过把它的数据分成若干个小表,从而简化数据库的管理活动,对于每一个简化后的小表,我们称为一个单个的分区
2、hive中分区表实际就是对应hdfs文件系统上的独立文件夹,该文件夹内的文件就是该分区所有的数据文件
3、分区可以理解为分类,通过分类把不同类型的数据放置到不同的目录下
4、分类的标准就是分区字段,可以是一个,也可以是多个
5、分区表的意义在于优化查询,查询时尽量利用分区字段,如果不使用分区字段,就会全部扫描。

  1. create table employee (name string,salary bigint) partitioned by (datel string) row format delimited fields terminated by ',' lines terminated by '\n' stored as textfile;

2.2 查看表的分区

  1. show partitions employee;

2.3 添加分区

  1. alter table employee add if not exists partition(datel='2018-12-01');

2.4 加载数据到分区

  1. load data local inpath '/home/hadoop/tmp/employee.txt' into table employee partition(datel='2018-12-01');

如果重复加载同名文件,不会报错,会自动创建一个*_copy_1.txt(这样的分区表会增加一倍)
外部分区表即使有分区的目录结构,也必须要通过hql添加分区,才能看到相应的数据

  1. hadoop fs -mkdir /user/hive/warehouse/emp/dt=2018-12-04
  2. hadoop fs -copyFromLocal /tmp/employee.txt /user/hive/warhouse/test.db/emp/dt=2018-12-04/employee.txt

此时查看表中的数据发现数据并没有变化,需要通过hql添加分区

如果手动的加入数据的话,先无法显示然后,需要alter table 一下

  1. alter table employee add if not exists partition(date1='2018-12-03')

2.5 分区表总结

1、当数据量比较大的时候,使用分区表可以缩小查询的数据范围
2、分区表实际上就是在表的目录下创建的子目录
3、如果有分区表的话查询的时候,尽量要使用分区字段
4、创建分区表的语句
create table 表名(字段名,字段类型....)partitioned by (分区字段名 分区字段类型) row format``delimited fields terminated by ',' lines terminated by '\n' sorted as textfile;
5、向分区表中插入数据
load data local inpath '/home/hadoop/tmp/employee.txt' into table 表名 partiton(分区字段名字='分区的具体值');
6、添加分区
alter table 表名 add if not exists partition(分区字段名字='分区的具体值');
7、利用分区表减少查询时需要扫面的数据量
8、分区仅仅时一个目录名
9、查看数据时,hive会自动添加分区列
10、支持多级分区,多级子目录

2.6 动态分区

在写入数据时自动创建分区(包括目录结构)
创建表

  1. create table employee2 (name string,salary bigint) partitioned by (date1 string) row format delimited fields terminated by ',' lines terminated by '\n' stored as textfile;

导入数据

  1. insert into table employee2 partition(date1) select name,salary,date1 from employee;

使用动态分区需要设置参数

  1. set hive.exec.dynamic.partition.mode=nonstrict;

三、Hive函数

3.1 内置运算符

在hive上面有4中运算符
1、关系运算符
2、算术运算符
3、逻辑运算符
4、复杂运算
(内容较多,见《Hive官方文档》)

3.2 内置函数

1、简单函数:日期函数 字符串函数 类型转换
2、统计函数:sum avg distinct
3、集合函数
4、分析函数
5、show functions;显示所有函数
6、desc function 函数名
7、desc function extended 函数名;

3.3 Hive自定义函数和Transform

3.3.1 UDF

当Hive提供的内置函数无法满足你的业务需求时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)
UDF:就是做一个mapper,对于每一条输入数据映射为一条输出数据
UDAF:就是一个reducer,把一组输入数据映射为一条或者多条输出数据
一个脚本至于是做mapper还是做reducer,又或者说是做udf还是做udaf,取决于我们把它放在什么样的hive操作中,放在select中的基本就是udf,放在distribute by和cluster by中就是reducer.

3.3.2 java UDF

UDF示例(运行java已经编写号的UDF)
在hdfs中创建/user/hive/lib 目录

  1. hadoop fs -mkdir /user/hive/lib

把hive目录下lib/hive-contrlib-1.1.0-cdh5.7.0.jar放到hdfs中

  1. hadoop fs -put hive-contrlib-1.1.0-cdh5.7.0.jar /user/hive/lib/

把集群中jar包的位置添加到hive中

  1. add jar hdfs////user/hive/lib/hive-contrlib-1.1.0-cdh5.7.0.jar;

在hive中创建临时UDF

  1. CREATE TEMPORARY FUNCTION row_sequence as 'org.apache.hadoop.hive.contrib.udf.UDFRowSequence';

在之前的案例中使用临时的自定义函数(函数功能:添加自增长的行号)

  1. Select row_sequence(),* from employee;

创建非临时自定义函数

  1. CREATE FUCTION row_sequence as 'org.apache.hadoop.hive.contrib.udf.UDFRowSequence' using jar'hdfs:///user/hive/lib/hive-contrib-1.1.0-cdh5.7.0.jar';

3.3.3 python UDF

准备案例环境
1、创建表

  1. CREATE table u(fname STRING,lname STRING);

向表中插入数据

  1. insert into table u values('George','washington');

2、编写map风格脚本

  1. import sys
  2. for line in sys.stdin
  3. line = line.strip()
  4. fname,lname = line.split('\t')
  5. l_name = lname.upper()
  6. print ('\t'.join([fname,str(l_name)]))

3、通过hdfs向hive中加入ADD file
加载文件到hdfs

  1. Hadoop fs -put udf.py /user/hive/lib/

hive从hdfs中加载到python脚本

  1. ADD FILE hdfs:///user/hive/lib/udf.py;
  2. ADD FILE /root/tmp/udf1.py;(直接从本地进行加载)

3.3.4 Transform

  1. Select Transfrom(fname,lname) USING 'python udf2.py' AS (fname,l_name) FROM u;

注:此处运行有问题:ParseException line 1:30 cannot recognize input near ‘USING’ ‘’python udf2.py’’ ‘AS’ in serde specification(存疑中?)

UDF自定义函数
hive提供的函数不能满足需求的时候可以使用自定义函数
1、使用别人已经编译好的.jar
2、jar可以在hdfs上也可以在centors上
创建一个临时函数

  1. CREATE TEMPORARY FUNCTION 自定义函数名 as '自定义函数在jar包中的包名';

创建一个永久函数

  1. CREATE FUCTION 自定义函数名 as '自定义函数在jar包中的包名' using jar 'jar位置';

自己写python脚本实现udf,udaf

  1. Select Transfrom(fname,lname) USING 'python udf1.py' AS (fname,l_name) FROM u;

四、hive的综合案例

用户画像的开发
有两张表格分别是:1、用户行为数据 2、文章数据
1、将数据上传到hdfs上面

  1. hadoop fs -mkdir /tmp/demo
  2. hadoop fs -mkdir /tmp/demo/user_action

2、创建外部表
用户行为表:

  1. DROP TABLE IF EXISTS user_actions;
  2. CREATE EXTERNAL TABLE user_actions(user_id STRING,
  3. article_id STRING,
  4. time_stamp STRING) ROW FORMAT delimited fields terminated BY ',' LOCATION '/tmp/demo/user_action';

文章表:

  1. DROP TABLE IF EXISTS articles;
  2. CREATE EXTERNAL TABLE articles(article_id STRING,
  3. ur1 STRING,
  4. key_words array<STRING>) ROW FORMAT delimited fields terminated BY ',' COLLECTION ITEMS terminated BY '|' LOCATION '/tmp/demo/article_keywords';

注:/key_words array<STRING> 数组的数剧类型 COLLECTION ITEMS terminated BY '|' 数组的元素之间用’|’分割 /

3、查看数据

  1. select * from user_action;
  2. select * from articles;

4、分组查询每个用户的浏览记录
collect_set/collect_list的作用:
1、将group by中的某列转为一个数组返回
2、collect_list不去重而collect_set去重

collect_set

  1. select user_id,collect_set(article_id) from user_actions group by user_id;

11 [“101”,”104”]
22 [“102”,”103”,”104”]
33 [“103”,”102”,”101”]
35 [“105”,”102”]
77 [“103”,”104”]

sort_array:对数组进行排序

  1. SELECT user_id,
  2. sort_array(collect_list(article_id)) AS CONTENTS
  3. FROM user_actions
  4. GROUP BY user_id;

22 [“102”,”103”,”104”]
33 [“103”,”102”,”101”]
11 [“101”,”104”]
35 [“105”,”102”]
77 [“103”,”104”]

查看每一篇文章的关键字lateral view explode
explode函数将array拆分

  1. select explode(key_words) from articles;

lateral viewexplode配合使用,将一行数据拆分成多行数据,在此基础上可以对拆分的数据进行聚合

  1. SELECT article_id,
  2. kw
  3. FROM articles LATERAL VIEW explode(key_words) t AS kw;

101 kw8
101 kw1
102 kw6

为了不把空的数组丢弃,再加入一个

  1. SELECT article_id,
  2. kw
  3. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw;

101 kw8
101 kw1
102 kw6
105 NULL

  1. SELECT a.user_id,
  2. b.kw
  3. FROM user_actions AS a
  4. LEFT OUTER JOIN
  5. (SELECT article_id,
  6. kw
  7. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw) b ON (a.article_id = b.article_id)
  8. ORDER BY a.user_id;

11 kw1
11 kw8
11 kw5
22 kw1
22 kw2
22 kw3
33 kw8
33 kw6
35 NULL
35 KW6

根据文章id找到用户查看文章的关键字并统计频率

  1. SELECT a.user_id,
  2. b.kw,
  3. count(1) AS weight
  4. FROM user_actions AS a
  5. LEFT OUTER JOIN
  6. (SELECT article_id,
  7. kw
  8. FROM articles LATERAL VIEW OUTER exploed(ley_words) t AS kw) b ON (a.article_id = b.article_id)
  9. GROUP BY a.user_id,
  10. b.kw # 分组
  11. ORDER BY a.user_id,
  12. weight DESC; # 排序

11 kw1 4
11 kw8 3
11 kw5 1
22 kw7 2

总结: 统计出现次数分组
CONCAT
CONCAT(str1,str2,…)
返回结果为连续参数产生的字符串,如果任何一个参数为NULL,则返回值为NULL

  1. select concat(user_id,article_id) from user_actions;

CONCAT_WS:
使用语法:**CONCAT_WS(separator,str1,str2,....)**
CONCAT_WS()代表CONCAT WIth Separator,是Concat()的特殊的形式,第一个参数是其它参数的分隔符,分隔符的位置要放在连接两个字符串之间。分隔符是一个常见的字符串,也可以是一个字符串,也可以是其他参数,如果分隔符为NULL,则结果为NULL

  1. SELECT concat_ws(":",user_id,article_id)
  2. FROM user_actions;

注:可以传一个数据或者列表的格式

将用户查看的关键字和频率合并成key.value形式
cast()转化数据的类型

  1. SELECT a.user_id,
  2. concat_ws(":",b.kw,cast(count(1) AS string)) AS kw_w
  3. FROM user_actions AS aleft
  4. OUTER JOIN
  5. (SELECT article_id,
  6. kw
  7. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw) b ON (a.article_id = b.article_id)
  8. GROUP BY a.user_id,
  9. b.kw;

11 kw1:4
11 kw4:1
11 kw5:1
11 kw8:1

将用户查看关键字和频率合成key:value形式并按照用户聚合``

  1. SELECT cc.user_id,
  2. concat_ws(",",collect_set(cc.kw_w)) from
  3. (SELECT a.user_id,concat_ws(":",b.kw,cast(count(1) AS string)) AS kw_w
  4. FROM user_actions AS a
  5. LEFT OUTER JOIN
  6. (SELECT article_id,kw
  7. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw) b ON (a.article_id = b.article_id)
  8. GROUP BY a.user_id,b.kw) AS cc
  9. GROUP BY cc.user_id;

11 kw1:4,kw4:1,kw5:1,kw8:1
22 kw1:1,kw3:1,kw5:1,kw6:1
33 kw1:1,kw3:1,kw7:1,kw8:1

将上面的聚合结果转换成map

  1. SELECT cc.user_id,
  2. str_to_map(concat_ws(",",collect_set(cc.kw_w))) AS wm from
  3. (SELECT a.user_id,concat_ws(":",b.kw,cast(count(1) AS string)) AS kw_w
  4. FROM user_actions AS a
  5. LEFT OUTER JOIN
  6. (SELECT article_id,kw
  7. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw) b ON (a.article_id = b.article_id)
  8. GROUP BY a.user_id,b.kw) AS cc
  9. GROUP BY cc.user_id;

11 {“kw1”:”4”,”kw4”:”1”,”kw5”:”1”,”kw9”:”1”}
22 {“kw1”:”1”,”kw3”:”1”,”kw4”:”1”}ls

将用户的阅读偏好结果保存到表中

  1. CREATE TABLE user_kws AS
  2. SELECT cc.user_id,
  3. str_to_map(concat_ws(",",collect_set(cc.kw_w))) AS wm from
  4. (SELECT a.user_id,
  5. concat_ws(":",b.kw,cast(count(1) AS string)) AS kw_w
  6. FROM user_actions AS a
  7. LEFT OUTER JOIN
  8. (SELECT article_id,
  9. kw
  10. FROM articles LATERAL VIEW OUTER explode(key_words) t AS kw) b ON (a.article_id = b.article_id)
  11. GROUP BY a.user_id,
  12. b.kw) AS cc
  13. GROUP BY cc.user_id;

从表中通过key查询map中的值

  1. select user_id,wm['kw1'] from user_kws;

11 4
22 1
33 1

从表中获取map中的所有的key和所有的value

  1. select user_id,map_keys(wm),map_values(wm) from user_kws;

11 [“kw1”,”kw2”,”kw5”,”kw8”,”kw9”][“4”,”1”,”1”,”3”,”1”]
22 [“kw1”,”kw3”,”kw4”,”kw5”,”kw6”][“1”,”1”,”1”,”1”,”1”]

用lateral view explode把map中的数据转换成多列

  1. SELECT user_id,
  2. keyword,
  3. weight
  4. FROM user_kws LATERAL VIEW explode(wm) t AS keyword.weight;

11 kw1 4
11 kw4 1
11 kw5 1

总结:
lateral view explode
1、explode函数把复杂数据array map一行拆成多行
2、lateral view和explode函数配合使用创建虚拟视图,可以把explode的结果和其他列结合在一起进行查询。

CONCAT,CONCAT_WS
不同列的字符串拼接到一起
concat_ws可以把array中的元素拼接到同一个字符串中指定分隔符

str_to_map把具有key:value形式的字符串转化为map