3.2.1.2 Hive,函数(日期,条件,YDTF...),内置/窗口/自定义, Hive事务(行事务), 元数据管理存储Metastore,HiveServer2, HCatalog, 存储格式

目录

第七部分 函数

第 1 节 系统内置函数

查看系统函数

日期函数【重要】

字符串函数

数学函数

条件函数【重要】

UDTF函数【重要】

第 2 节 窗口函数【重要】

over 关键字

partition by子句

order by 子句

Window子句

排名函数(常考)

序列函数

SQL面试题

1、连续7天登录的用户 (核心是根据减去序列号, 找到分组依据)

2、编写sql语句实现每班前三名,分数一样并列,同时求出前三名按名次排序的分差

3、行 <=> 列

第 3 节 自定义函数

第八部分 HQL操作之--DML命令

第 1 节 Hive 事务

Hive事务的限制:

第 2 节 Hive 事务操作示例

第九部分 元数据管理与存储

第 1 节 Metastore

metastore三种配置方式

第 2 节 HiveServer2

HiveServer2作用:

HiveServer2配置

第 3 节 HCatalog 

第 4 节 数据存储格式

行存储与列存储

TextFile

SEQUENCEFILE

RCFile

ORCFile

Parquet

文件存储格式对比测试


第七部分 函数

Hive内置函数:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-Built-inFunctions

第 1 节 系统内置函数

查看系统函数

-- 查看系统自带函数
show functions;

-- 显示自带函数的用法
desc function upper;
desc function extended upper;

日期函数【重要】

-- 当前前日期
select current_date;
select unix_timestamp();

-- 建议使用current_timestamp,有没有括号都可以
select current_timestamp();

-- 时间戳转日期
select from_unixtime(1505456567);
select from_unixtime(1505456567, 'yyyyMMdd');
select from_unixtime(1505456567, 'yyyy-MM-dd HH:mm:ss');

-- 日期转时间戳
select unix_timestamp('2019-09-15 14:23:00');

-- 计算时间差
select datediff('2020-04-18','2019-11-21');
select datediff('2019-11-21', '2020-04-18');

-- 查询当月第几天
select dayofmonth(current_date);

-- 计算月末:
select last_day(current_date);

-- 当月第1天:
select date_sub(current_date, dayofmonth(current_date)-1)

-- 下个月第1天:
select add_months(date_sub(current_date,
dayofmonth(current_date)-1), 1)

-- 字符串转时间(字符串必须为:yyyy-MM-dd格式)
select to_date('2020-01-01');
select to_date('2020-01-01 12:12:12');

-- 日期、时间戳、字符串类型格式化输出标准时间格式
select date_format(current_timestamp(), 'yyyy-MM-dd HH:mm:ss');
select date_format(current_date(), 'yyyyMMdd');
select date_format('2020-06-01', 'yyyy-MM-dd HH:mm:ss');

-- 计算emp表中,每个人的工龄  round(数据,保留位数)
select *, round(datediff(current_date, hiredate)/365,1)
workingyears from emp;

字符串函数

-- 转小写。lower
select lower("HELLO WORLD");

-- 转大写。upper
select lower(ename), ename from emp;

-- 求字符串长度。length
select length(ename), ename from emp;

-- 字符串拼接。 concat / ||
select empno || " " ||ename idname from emp;
select concat(empno, " " ,ename) idname from emp;

-- 指定分隔符。concat_ws(separator, [string | array(string)]+)
SELECT concat_ws('.', 'www', array('lagou', 'com'));
select concat_ws(" ", ename, job) from emp;

-- 求子串。substr
SELECT substr('www.lagou.com', 5);
SELECT substr('www.lagou.com', -5);
SELECT substr('www.lagou.com', 5, 5);

-- 字符串切分。split,注意 '.' 要转义
select split("www.lagou.com", "\\.");

数学函数

-- 四舍五入。round,  负数,表示在小数点前几位的地方四舍五入
select round(314.15926);
select round(314.15926, 2);
select round(314.15926, -2);

-- 向上取整。ceil
select ceil(3.1415926);

-- 向下取整。floor
select floor(3.1415926);

-- 其他数学函数包括:绝对值abs、平方power、开方sqrt、对数运算log、三角运算等

条件函数【重要】

条件函数主要有, if, case when, coalesce, isnull/isnotnull, nvl, nullif

-- if (boolean testCondition, T valueTrue, T valueFalseOrNull)
-- 将emp表的员工工资等级分类:0-1500、1500-3000、3000以上
select sal, if (sal<=1500, 1, if (sal <= 3000, 2, 3)) from emp;

-- CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END
-- 复杂条件用 case when 更直观
select ename,sal,
case when sal<=1500 then 1
     when sal<=3000 then 2
     else 3 end sallevel
from emp;

-- 以下语句等价
select ename, deptno,
case when deptno=10 then 'accounting'
     when deptno=20 then 'research'
     when deptno=30 then 'sales'
     else 'unknown' end deptname
from emp; 

select ename, deptno,
case deptno when 10 then 'accounting'
            when 20 then 'research'
            when 30 then 'sales'
            else 'unknown' end deptname
from emp;

-- COALESCE(T v1, T v2, ...)。返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL
select sal, coalesce(comm, 0) from emp;

-- isnull(a) isnotnull(a)
select * from emp where isnull(comm);
select * from emp where comn is null;

select * from emp where isnotnull(comm);
select * from emp where comn is not null;

-- nvl(T value, T default_value)
select empno, ename, job, mgr, hiredate, deptno, sal + nvl(comm,0) sumsal
from emp;

-- nullif(x, y) 相等为空,否则返回第一个参数x
SELECT nullif("b", "b"), nullif("b", "a");

UDTF函数【重要】

UDTF : User Defined Table-Generating Functions。用户定义表生成函数,一行输入,多行输出。

-- explode,炸裂函数
-- 就是将一行中复杂的 array 或者 map 结构拆分成多行
select explode(array('A','B','C')) as col;
select explode(map('a', 8, 'b', 88, 'c', 888));

-- UDTF's are not supported outside the SELECT clause, nor nestedin expressions

 SELECT pageid, explode(adid_list) AS myCol... is not supported
 SELECT explode(explode(adid_list)) AS myCol... is notsupported


 lateral view 常与 表生成函数explode结合使用

-- lateral view 语法:
-- lateralView: LATERAL VIEW udtf(expression) tableAlias AS columnAlias (',' columnAlias)*
-- fromClause: FROM baseTable (lateralView)*

-- lateral view 的基本使用
with t1 as (
    select 'OK' cola, split('www.lagou.com', '\\.') colb
)
select cola, colc
from t1
lateral view explode(colb) t2 as colc;

UDTF 案例1:

-- 数据(uid tags):
1 1,2,3
2 2,3
3 1,2

--编写sql,实现如下结果:
1 1
1 2
1 3
2 2
2 3
3 1
3 2

-- 建表加载数据
create table tab1(
 id int,
 tags string
)
row format delimited fields terminated by '\t';
load data local inpath '/hivedata/tab1.dat' into table tab1;

-- SQL
select id, split(tags, ',') from tab1;

select uid, tag
from t1
lateral view explode(split(tags,",")) t1 as tag;

UDTF 案例2:

-- 数据准备 (score.dat)
lisi|Chinese:90,Math:80,English:70
wangwu|Chinese:88,Math:90,English:96
maliu|Chinese:99,Math:65,English:60

-- 创建表
create table studscore(
 name string
,score map<String,string>
)
row format delimited
fields terminated by '|'
collection items terminated by ','
map keys terminated by ':';

-- 加载数据
load data local inpath '/home/hadoop/data/score.dat' overwrite
into table studscore;


-- 需求:找到每个学员的最好成绩

-- 第一步,使用 explode 函数将map结构拆分为多行
select explode(score) as (subject, socre) from studscore;
--但是这里缺少了学员姓名,加上学员姓名后出错。下面的语句有是错的, explode函数不能和表中字段同用
select name, explode(score) as (subject, socre) from studscore;

-- 第二步:explode常与 lateral view 函数联用,这两个函数结合在一起能关联其他字段
select name, subject, score1 as score from studscore
lateral view explode(score) t1 as subject, score1;

-- 第三步:找到每个学员的最好成绩
select name, max(mark) maxscore
from (
      select name, subject, mark
      from studscore lateral view explode(score) t1 as subject, mark
     ) t1
group by name;

-- 做成临时表也行
with tmp as (
  select name, subject, mark
  from studscore lateral view explode(score) t1 as subject, mark
)
select name, max(mark) maxscore
from tmp
group by name;

小结:

  • 将一行数据转换成多行数据,可以用于array和map类型的数据;
  • lateral view 与 explode 联用,解决 UDTF 不能添加额外列的问题

第 2 节 窗口函数【重要】

窗口函数又名开窗函数,属于分析函数的一种。用于解决复杂报表统计需求的功能强大的函数,很多场景都需要用到。窗口函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行。

窗口函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化。

over 关键字

使用窗口函数之前一般要要通过over()进行开窗

-- 查询emp表工资总和
select sum(sal) from emp;

-- 不使用窗口函数over, 是普通聚合函数, 有语法错误
select ename, sal, sum(sal) salsum from emp;

-- 使用窗口函数,是分析函数, 针对整个数据集进行计算  查询员工姓名、薪水、薪水总和
select ename, sal, sum(sal) over() salsum,
       concat(round(sal / sum(sal) over()*100, 1) || '%') ratiosal
from emp;

注意:窗口函数是针对每一行数据的;如果over中没有参数,默认的是全部结果集;

partition by子句

在over窗口中进行分区,对某一列进行分区统计,窗口的大小就是分区的大小

-- 查询员工姓名、薪水、部门薪水总和
select ename, sal, sum(sal) over(partition by deptno) salsum
from emp;

order by 子句

order by 子句对输入的数据进行排序

-- 增加了order by子句;sum:从分组的第一行到当前行求和
select ename, sal, deptno, sum(sal) over(partition by deptno order by sal) salsum
from emp;

Window子句

rows between ... and ...

如果要对窗口的结果做更细粒度的划分,使用window子句,有如下的几个选项:!clear

  • unbounded preceding。组内第一行数据
  • n preceding。组内当前行的前n行数据
  • current row。当前行数据
  • n following。组内当前行的后n行数据
  • unbounded following。组内最后一行数据

-- rows between ... and ... 子句
-- 等价。组内,第一行到当前行的和
select ename, sal, deptno,
   sum(sal) over(partition by deptno order by ename) 
from
emp;

select ename, sal, deptno,
       sum(sal) over(partition by deptno order by ename
          rows between unbounded preceding and currentrow
       )
from emp;

-- 组内,第一行到最后一行的和, 也即是全组的总和
select ename, sal, deptno,
       sum(sal) over(partition by deptno order by ename
          rows between unbounded preceding and unbounded following
       )
from emp;

-- 组内,前一行 + 当前行 +后一行
select ename, sal, deptno,
       sum(sal) over(partition by deptno order by ename
          rows between 1 preceding and 1 following
       )
from emp;

排名函数(常考)

都是从1开始,生成数据项在分组中的排名。通常用于求解topN的问题

  • row_number()。排名顺序增加不会重复;如1、2、3、4、... ...
  • RANK()。 排名相等会在名次中留下空位;如1、2、2、4、5、... ...
  • DENSE_RANK()。 排名相等会在名次中不会留下空位 ;如1、2、2、3、4、...
-- row_number / rank / dense_rank 排名方式的区别
100 1 1 1
100 2 1 1
100 3 1 1
99  4 4 2
98  5 5 3
98  6 5 3
97  7 7 4

-- 数据准备
class1 s01 100
class1 s03 100
class1 s05 100
class1 s07 99
class1 s09 98
class1 s02 98
class1 s04 97
class2 s21 100
class2 s24 99
class2 s27 99
class2 s22 98
class2 s25 98
class2 s28 97
class2 s26 96

-- 创建表加载数据
create table t2(
    cname string,
    sname string,
    score int
) row format delimited fields terminated by '\t';

load data local inpath '/home/hadoop/data/t2.dat' into table t2;

-- 按照班级,使用3种方式对成绩进行排名
select cname, sname, score,
       row_number() over (partition by cname order by score desc) rank1,
       rank() over (partition by cname order by score desc) rank2,
       dense_rank() over (partition by cname order by score desc) rank3
from t2;  

-- 求每个班级前3名的学员--前3名的定义是什么--假设使用dense_rank
select cname, sname, score, rank
from (select cname, sname, score,
      dense_rank() over (partition by cname order byscore desc) rank from t2
) tmp
where rank <= 3;

序列函数

  • lag。返回当前数据行的上一行数据, 用的多
  • lead。返回当前数据行的下一行数据, 用的多
  • first_value。取分组内排序后,截止到当前行,第一个值
  • last_value。分组内排序后,截止到当前行,最后一个值
  • ntile。将分组的数据按照顺序切分成n片,返回当前切片值
-- 测试数据 userpv.dat。cid ctime pv
cookie1,2019-04-10,1
cookie1,2019-04-11,5
cookie1,2019-04-12,7
cookie1,2019-04-13,3
cookie1,2019-04-14,2
cookie1,2019-04-15,4
cookie1,2019-04-16,4
cookie2,2019-04-10,2
cookie2,2019-04-11,3
cookie2,2019-04-12,5
cookie2,2019-04-13,6
cookie2,2019-04-14,3
cookie2,2019-04-15,9
cookie2,2019-04-16,7

-- 建表语句
create table userpv(
cid string,
ctime date,
pv int
)
row format delimited fields terminated by ",";

-- 加载数据
Load data local inpath '/home/hadoop/data/userpv.dat' into table userpv;

-- lag。返回当前数据行的上一行数据
-- lead。功能上与lag类似
select cid, ctime, pv,
       lag(pv) over(partition by cid order by ctime) lagpv,
       lead(pv) over(partition by cid order by ctime) leadpv
from userpv;

-- lag / lead 可以跟参数, 移动多行
select cid, ctime, pv,
       lag(pv, 2) over(partition by cid order by ctime) lagpv,
       lead(pv, 3) over(partition by cid order by ctime) leadpv
from userpv;



-- first_value / last_value
select cid, ctime, pv,
       first_value(pv) over (partition by cid order by ctime 
                             rows between unbounded preceding and unbounded following) as firstpv,
       last_value(pv) over (partition by cid order by ctime rows
                            rows between unbounded preceding and unbounded following) as lastpv
from userpv;   

-- ntile。按照cid进行分组,每组数据分成2份
select cid, ctime, pv,
       ntile(2) over(partition by cid order by ctime) ntile
from userpv;

SQL面试题

1、连续7天登录的用户 (核心是根据减去序列号, 找到分组依据)

-- 数据。uid dt status(1 正常登录,0 异常)
1 2019-07-11 1
1 2019-07-12 1
1 2019-07-13 1
1 2019-07-14 1
1 2019-07-15 1
1 2019-07-16 1
1 2019-07-17 1
1 2019-07-18 1
2 2019-07-11 1
2 2019-07-12 1
2 2019-07-13 0
2 2019-07-14 1
2 2019-07-15 1
2 2019-07-16 0
2 2019-07-17 1
2 2019-07-18 0
3 2019-07-11 1
3 2019-07-12 1
3 2019-07-13 1
3 2019-07-14 0
3 2019-07-15 1
3 2019-07-16 1
3 2019-07-17 1
3 2019-07-18 1

-- 建表语句
create table ulogin(
uid int,
dt date,
status int
)
row format delimited fields terminated by ' ';

-- 加载数据
load data local inpath '/home/hadoop/data/ulogin.dat' into table ulogin;

-- 连续值的求解,面试中常见的问题。这也是同一类,基本都可按照以下思路进行
-- 1、使用 row_number 在组内给数据编号(rownum)
select uid, dt,
       row_number() over (partition by uid order by dt) rownum
from ulogin
where status=1;


-- 2、某个值 - rownum = gid,得到结果可以作为后面分组计算的依据
select uid, dt,
       date_sub(dt, row_number() over (partition by uid order by dt)) gid
from ulogin
where status=1;

-- 3、根据求得的gid,作为分组条件,求最终结果
select uid, count(*) countlogin
from (select uid, dt,
             date_sub(dt, row_number() over (partition by uid order by dt)) gid
      from ulogin
      where status=1
) t1
group by uid, gid
having countlogin >= 7;

2、编写sql语句实现每班前三名,分数一样并列,同时求出前三名按名次排序的分差

-- 数据。sid class score
1 1901 90
2 1901 90
3 1901 83
4 1901 60
5 1902 66
6 1902 23
7 1902 99
8 1902 67
9 1902 87


-- 待求结果数据如下:
class  score  rank  lagscore
1901   90    1    0
1901   90    1    0
1901   83    2    -7
1901   60    3    -23
1902   99    1    0
1902   87    2    -12
1902   67    3    -20


-- 建表语句
create table stu(
sno int,
class string,
score int
)row format delimited fields terminated by ' ';


-- 加载数据
load data local inpath '/home/hadoop/data/stu.dat' into table stu;


-- 求解思路:
-- 1、上排名函数,分数一样并列,所以用dense_rank
select sno, class, score,
       dense_rank() over (partition by class order by score desc) as rank
from stu;

-- 2、将上一行数据下移,相减即得到分数差
select sno, class, score,
       dense_rank() over (partition by class order by score desc) as rank,
       score - lag(score) over (partition by class order by score desc) lagscore
from stu;

-- 3、处理 NULL
select class, score,
       dense_rank() over (partition by class order by score desc) as rank,
       nvl(score - lag(score) over (partition by class order by score desc), 0) lagscore
from stu;

with tmp as (
    select sno, class, score,
           dense_rank() over (partition by class order by score desc) as rank
    from stu
)
select class, score, rank,
       nvl(score - lag(score) over (partition by class order by score desc), 0) lagscore
from tmp
where rank<=3;

3、行 <=> 列

行转列

-- 数据。id1 id2 flag
a b 2
a b 1
a b 3
c d 6
c d 8
c d 8

-- 编写sql实现如下结果
id1 id2  flag
 a   b   2|1|3
 c   d   6|8
 
-- 创建表 & 加载数据
create table rowline2(
id1 string,
id2 string,
flag int
) row format delimited fields terminated by ' ';

load data local inpath '/root/data/data2.dat' into table rowline2;

-- 第一步 将元素聚拢
-- 下面的函数, 去重
select id1, id2, collect_set(flag) flag 
from rowline2 
group by id1, id2;

-- 下面的不去重
select id1, id2, collect_list(flag) flag 
from rowline2 
group by id1, id2;

-- 下面的不去重且排序
select id1, id2, sort_array(collect_set(flag)) flag 
from rowline2
group by id1, id2;

-- 第二步 将元素连接在一起
select id1, id2, concat_ws("|", collect_set(flag)) flag
from rowline2
group by id1, id2;

-- 这里报错,CONCAT_WS must be "string or array<string>"。
-- 加一个类型转换即可cast (flag as string)
select id1, id2, concat_ws("|", collect_set(cast (flag as string))) flag
from rowline2
group by id1, id2;


-- -------------------------------下面忽略
-- 创建表 rowline3
create table rowline3 
as select id1, id2, concat_ws("|", collect_set(cast (flag as string))) flag
from rowline2
group by id1, id2;

-- 第一步:将复杂的数据展开
select explode(split(flag, "\\|")) flat from rowline3;

-- 第二步:lateral view 后与其他字段关联
select id1, id2, newflag
from rowline3 lateral view explode(split(flag, "\\|")) t1 as newflag;

-- lateralView: LATERAL VIEW udtf(expression) tableAlias AS
-- columnAlias (',' columnAlias)*
-- fromClause: FROM baseTable (lateralView)*

列转行

-- 数据:id course
1 java
1 hadoop
1 hive
1 hbase
2 java
2 hive
2 spark
2 flink
3 java
3 hadoop
3 hive
3 kafka

-- 建表加载数据
create table rowline1(
id string,
course string
)row format delimited fields terminated by ' ';

load data local inpath '/root/data/data1.dat' into table rowline1;

-- 编写sql,得到结果如下(1表示选修,0表示未选修)
id   java  hadoop   hive  hbase  spark  flink  kafka
1      1      1      1      1      0      0      0
2      1      0      1      0      1      1      0
3      1      1      1      0      0      0      1
 
-- 使用case when;group by + sum
select id,
       sum(case when course="java" then 1 else 0 end) as java,
       sum(case when course="hadoop" then 1 else 0 end) as hadoop,
       sum(case when course="hive" then 1 else 0 end) as hive,
       sum(case when course="hbase" then 1 else 0 end) as hbase,
       sum(case when course="spark" then 1 else 0 end) as spark,
       sum(case when course="flink" then 1 else 0 end) as flink,
       sum(case when course="kafka" then 1 else 0 end) as kafka
from rowline1
group by id;

小结:三类典型问题, 行列互转/ TopN+行函数/ 连续值求解

第 3 节 自定义函数

当 Hive 提供的内置函数无法满足实际的业务处理需要时,可以考虑使用用户自定义函数进行扩展。用户自定义函数分为以下三类:

  • UDF(User Defined Function)。用户自定义函数,一进一出; 类似于 current_Date, power, lower
  • UDAF(User Defined Aggregation Function)。用户自定义聚集函数,多进一出;类似于:count, max, min, sum, avg
  • UDTF(User Defined Table-Generating Functions)。用户自定义表生成函数,一进多出;类似于:explode

UDF开发:

  • 继承org.apache.hadoop.hive.ql.exec.UDF
  • 需要实现evaluate函数;evaluate函数支持重载
  • UDF必须要有返回类型,可以返回null,但是返回类型不能为void

UDF开发步骤

  • 创建maven java 工程,添加依赖
  • 开发java类继承UDF,实现evaluate 方法
  • 将项目打包上传服务器
  • 添加开发的jar包
  • 设置函数与自定义函数关联
  • 使用自定义函数

需求:扩展系统 nvl 函数功能:

nvl(x, y): x==null => 返回y
nvl(x, y): x==null or x=="" or x=="   " =>返回y

1、创建maven java 工程,添加依赖

    <!-- pom.xml 文件 -->
    <dependencies>
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>2.3.7</version>
        </dependency>
    </dependencies>

    <!--maven打包插件 -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin </artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>

                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2、开发java类继承UDF,实现evaluate 方法

package com.lagou.hive.udf;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;

/**
 * @author CH
 * @date 2020/12/28 17:07
 */
public class nvl extends UDF {

    public Text evaluate(final Text x, final Text y) {

        if (null == x || 0 == x.toString().trim().length()) {
            return y;
        }

        return x;
    }
}

3、将项目打包上传服务器

4、添加开发的jar包(在Hive命令行中)

add jar /home/hadoop/hiveudf.jar;

5、创建临时函数。指定类名一定要完整的路径,即包名加类名

create temporary function mynvl as "com.lagou.hive.udf.nvl";

6、执行查询

7、退出Hive命令行,再进入Hive命令行。执行步骤6的测试,发现函数失效。
备注:创建临时函数每次进入Hive命令行时,都必须执行以下语句,很不方便:

add jar /home/hadoop/hiveudf.jar;
create temporary function mynvl as "cn.lagou.hive.udf.nvl";

可创建永久函数:

1、将jar上传HDFS

hdfs dfs -mkdir -p jar/

hdfs dfs -put hiveudf.jar jar/

2、在Hive命令行中创建永久函数

create function mynvl1 as 'com.lagou.hive.udf.nvl' using jar
'hdfs:/user/root/jar/nvl.jar';

-- 查询所有的函数,发现 mynvl1 在列表中
show functions;

3、退出Hive,再进入,执行测试

-- 基本功能还有
select mynvl(comm, 0) from mydb.emp;

-- 测试扩充的功能
select mynvl("", "OK");
select mynvl(" ", "OK");

4、删除永久函数,并检查

drop function mynvl1;
show functions;

第八部分 HQL操作之--DML命令

数据操纵语言DML(Data Manipulation Language),DML主要有三种形式:插入(INSERT)、删除(DELETE)、更新(UPDATE)。

事务(transaction)是一组单元化操作,这些操作要么都执行,要么都不执行,是一个不可分割的工作单元。

事务具有的四个要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这四个基本要素通常称为ACID特性。

  • 原子性。一个事务是一个不可再分割的工作单位,事务中的所有操作要么都发生,要么都不发生。
  • 一致性。事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
  • 隔离性。在并发环境中,并发的事务是相互隔离的,一个事务的执行不能被其他事务干扰。即不同的事务并发操纵相同的数据时,每个事务都有各自完整的数据空间,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性。事务一旦提交,它对数据库中数据的改变就应该是永久性的。

第 1 节 Hive 事务

Hive从0.14版本开始支持事务 和 行级更新,但缺省是不支持的,需要一些附加的配置。要想支持行级insert、update、delete,需要配置Hive支持事务。

Hive事务的限制:

  • Hive提供行级别的ACID语义
  • BEGIN、COMMIT、ROLLBACK 暂时不支持,所有操作自动提交
  • 目前只支持 ORC 的文件格式
  • 默认事务是关闭的,需要设置开启
  • 要是使用事务特性,表必须是分桶的
  • 只能使用内部表
  • 如果一个表用于ACID写入(INSERT、UPDATE、DELETE),必须在表中设置表属性 : "transactional=true"
  • 必须使用事务管理器 org.apache.hadoop.hive.ql.lockmgr.DbTxnManager
  • 目前支持快照级别的隔离。就是当一次数据查询时,会提供一个数据一致性的快照
  • LOAD DATA语句目前在事务表中暂时不支持

HDFS是不支持文件的修改;并且当有数据追加到文件,HDFS不对读数据的用户提供一致性的。为了在HDFS上支持数据的更新:

  • 表和分区的数据都被存在基本文件中(base files)
  • 新的记录和更新,删除都存在增量文件中(delta files)
  • 一个事务操作创建一系列的增量文件
  • 在读取的时候,将基础文件和修改,删除合并,最后返回给查询

第 2 节 Hive 事务操作示例

-- 这些参数也可以设置在hive-site.xml中
SET hive.support.concurrency = true;
-- Hive 0.x and 1.x only
SET hive.enforce.bucketing = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
SET hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;

-- 创建表用于更新。满足条件:内部表、ORC格式、分桶、设置表属性
create table zxz_data(
    name string,
    nid int,
    phone string,
    ntime date)
clustered by(nid) into 5 buckets
stored as orc
tblproperties('transactional'='true');

-- 创建临时表,用于向分桶表插入数据
create table temp1(
    name string,
    nid int,
    phone string,
    ntime date)
row format delimited
fields terminated by ",";

-- 数据
name1,1,010-83596208,2020-01-01
name2,2,027-63277201,2020-01-02
name3,3,010-83596208,2020-01-03
name4,4,010-83596208,2020-01-04
name5,5,010-83596208,2020-01-05

-- 向临时表加载数据;向事务表中加载数据
load data local inpath '/home/hadoop/data/zxz_data.txt' overwrite into table temp1;
insert into table zxz_data select * from temp1;

-- 检查数据和文件
select * from zxz_data;
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;

-- DML 操作
delete from zxz_data where nid = 3;

dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;

insert into zxz_data values ("name3", 3, "010-83596208", current_date);  -- 不支持

insert into zxz_data values ("name3", 3, "010-83596208", "2020-06-01");  -- 执行

insert into zxz_data select "name3", 3, "010-83596208", current_date;

dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;

insert into zxz_data values
        ("name6", 6, "010-83596208", "2020-06-02"),
        ("name7", 7, "010-83596208", "2020-06-03"),
        ("name8", 9, "010-83596208", "2020-06-05"),
        ("name9", 8, "010-83596208", "2020-06-06");

dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;

-- name后拼接00
update zxz_data set name=concat(name, "00") where nid>3;

dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;

-- 分桶字段不能修改,下面的语句不能执行
-- Updating values of bucketing columns is not supported
update zxz_data set nid = nid + 1;

小结: Hive支持行事务(允许对数据进行修改), 但是条件严格; 不建议在生产环境中使用

第九部分 元数据管理与存储

第 1 节 Metastore

在Hive的具体使用中,首先面临的问题便是如何定义表结构信息,跟结构化的数据映射成功。所谓的映射指的是一种对应关系。在Hive中需要描述清楚表跟文件之间的映射关系、列和字段之间的关系等等信息。这些描述映射关系的数据的称之为Hive的元数据。该数据十分重要,因为只有通过查询它才可以确定用户编写sql和最终操作文件之间的关系。

Metadata即元数据。元数据包含用Hive创建的database、table、表的字段等元信息。元数据存储在关系型数据库中。如hive内置的Derby、第三方如MySQL等。

Metastore即元数据服务,是Hive用来管理库表元数据的一个服务。有了它上层的服务不用再跟裸的文件数据打交道,而是可以基于结构化的库表信息构建计算框架。

通过metastore服务将Hive的元数据暴露出去,而不是需要通过对Hive元数据库mysql的访问才能拿到Hive的元数据信息;metastore服务实际上就是一种thrift服务,通过它用户可以获取到Hive元数据,并且通过thrift获取元数据的方式,屏蔽了数据库访问需要驱动,url,用户名,密码等细节。

metastore三种配置方式

1、内嵌模式(默认)

内嵌模式使用的是内嵌的Derby数据库来存储元数据,也不需要额外起Metastore服务。数据库和Metastore服务都嵌入在主Hive Server进程中。这个是默认的,配置简单,但是一次只能一个客户端连接,适用于用来实验,不适用于生产环境。

优点:配置简单,解压hive安装包 bin/hive 启动即可使用;

缺点:不同路径启动hive,每一个hive拥有一套自己的元数据,无法共享。

2、本地模式

本地模式采用外部数据库来存储元数据,目前支持的数据库有:MySQL、Postgres、Oracle、MS SQL Server。教学中实际采用的是MySQL。

本地模式不需要单独起metastore服务,用的是跟Hive在同一个进程里的metastore服务。也就是说当启动一个hive 服务时,其内部会启动一个metastore服务。Hive根据 hive.metastore.uris 参数值来判断,如果为空,则为本地模式。

缺点:每启动一次hive服务,都内置启动了一个metastore;在hive-site.xml中暴露的数据库的连接信息;

优点:配置较简单,本地模式下hive的配置中指定mysql的相关信息即可。

3. 远程模式

远程模式下,需要单独起metastore服务,然后每个客户端都在配置文件里配置连接到该metastore服务。远程模式的metastore服务和hive运行在不同的进程里。在生产环境中,建议用远程模式来配置Hive Metastore。

在这种模式下,其他依赖hive的软件都可以通过Metastore访问Hive。此时需要配置hive.metastore.uris 参数来指定 metastore 服务运行的机器ip和端口,并且需要单独手动启动metastore服务。metastore服务可以配置多个节点上,避免单节点故障导致整个集群的hive client不可用。同时hive client配置多个metastore地址,会自动选择可用节点。

metastore内嵌模式配置

安装步骤(linux121)

1、下载软件解压缩
2、设置环境变量,并使之生效
3、初始化数据库。
      schematool -dbType derby -initSchema
4、进入hive命令行
5、再打开一个hive命令行,发现无法进入

metastore远程模式配置
配置规划:

配置步骤:
配置步骤:**
1、将 linux123 的 hive 安装文件拷贝到 linux121、linux122
2、在linux121、linux123上分别启动 metastore 服务

# 启动 metastore 服务
nohup hive --service metastore &

# 查询9083端口(metastore服务占用的端口)
lsof -i:9083

# 安装lsof
yum install lsof

3、修改 linux122 上hive-site.xml。删除配置文件中:MySQL的配置、连接数据库的用户名、口令等信息;增加连接metastore的配置:

<!-- hive metastore 服务地址, 给多个值,即为设置多个客户机 -->
<property>
    <name>hive.metastore.uris</name>
    <value>thrift://linux121:9083,thrift://linux123:9083</value>
</property>

4、启动hive。此时client端无需实例化hive的metastore,启动速度会加快。

# 分别在linux121、linux123上执行以下命令,查看连接情况
lsof -i:9083

5、高可用测试。关闭已连接的metastore服务,发现hive连到另一个节点的服务上,仍然能够正常使用。

第 2 节 HiveServer2

HiveServer2是一个服务端接口,使远程客户端可以执行对Hive的查询并返回结果。目前基于Thrift RPC的实现是HiveServer的改进版本,并支持多客户端并发和身份验证,启动hiveServer2服务后,就可以使用jdbc、odbc、thrift 的方式连接。

Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。

HiveServer2(HS2)是一种允许客户端对Hive执行查询的服务。HiveServer2是HiveServer1的后续 版本。HS2支持多客户端并发和身份验证,旨在为JDBC、ODBC等开放API客户端提供更好的支持。

HS2包括基于Thrift的Hive服务(TCP或HTTP)和用于Web UI 的Jetty Web服务器。

HiveServer2作用:

  • 为Hive提供了一种允许客户端远程访问的服务
  • 基于thrift协议,支持跨平台,跨编程语言对Hive访问
  • 允许远程访问Hive

HiveServer2配置

配置规划:

配置步骤:
1、修改集群上的 core-site.xml,增加以下内容:

<!-- HiveServer2 连不上10000;hadoop为安装用户 -->
<!-- root用户可以代理所有主机上的所有用户 -->
    <property>  
        <name>hadoop.proxyuser.root.hosts</name>  
        <value>*</value>
    </property>
    <property>  
        <name>hadoop.proxyuser.root.groups</name>  
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hadoop.hosts</name>
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hadoop.groups</name>
        <value>*</value>
    </property>

2、修改 集群上的 hdfs-site.xml,增加以下内容:

<!-- HiveServer2 连不上10000;启用 webhdfs 服务 -->
    <property> 
        <name>dfs.webhdfs.enabled</name> 
        <value>true</value> 
    </property>

3、启动linux123上的 HiveServer2 服务

# 启动 hiveserver2 服务
nohup hiveserver2 &

# 检查 hiveserver2 端口
lsof -i:10000

# 从2.0开始,HiveServer2提供了WebUI
# 还可以使用浏览器检查hiveserver2的启动情况。http://linux123:10002/

4、启动 linux122 节点上的 beeline
Beeline是从 Hive 0.11版本引入的,是 Hive 新的命令行客户端工具。

Hive客户端工具后续将使用Beeline 替代 Hive 命令行工具 ,并且后续版本也会废弃掉 Hive 客户端工具。

!connect jdbc:hive2://linux123:10000

用户名密码不用输入 直接回车

use mydb;
show tables;
select * from emp;
create table tabtest1 (c1 int, c2 string);

还可以连接数据库, 但是这里需要输入用户名密码
!connect jdbc:mysql://linux123:3306
!help
!quit

第 3 节 HCatalog 

HCatalog 提供了一个统一的元数据服务,允许不同的工具如 Pig、MapReduce 等通过 HCatalog 直接访问存储在 HDFS 上的底层文件。HCatalog是用来访问Metastore的Hive子项目,它的存在给了整个Hadoop生态环境一个统一的定义。

HCatalog 使用了 Hive 的元数据存储,这样就使得像 MapReduce 这样的第三方应用可以直接从 Hive 的数据仓库中读写数据。同时,HCatalog 还支持用户在MapReduce 程序中只读取需要的表分区和字段,而不需要读取整个表,即提供一种逻辑上的视图来读取数据,而不仅仅是从物理文件的维度。

HCatalog 提供了一个称为 hcat 的命令行工具。这个工具和 Hive 的命令行工具类似,两者最大的不同就是 hcat 只接受不会产生 MapReduce 任务的命令。

# 进入 hcat 所在目录。$HIVE_HOME/hcatalog/bin
cd $HIVE_HOME/hcatalog/bin

# 执行命令,创建表
./hcat -e "create table default.test1(id string, name string, age int)"

# 长命令可写入文件,使用 -f 选项执行文件
./hcat -f createtable.txt

# 查看元数据
./hcat -e "use mydb; show tables"

# 查看表结构
./hcat -e "desc mydb.emp"

# 删除表
./hcat -e "drop table default.test1"

第 4 节 数据存储格式

Hive支持的存储数的格式主要有:TEXTFILE(默认格式) 、SEQUENCEFILE、RCFILE、ORCFILE、PARQUET。

  • textfile为默认格式,建表时没有指定文件格式,则使用TEXTFILE,导入数据时会直接把数据文件拷贝到hdfs上不进行处理;
  • SEQUENCEFILE,RCFILE,ORCFILE, PARQUET格式的表不能直接从本地文件导入数据,数据要先导入到textfile格式的表中, 然后再从表中用insert导入sequencefile、rcfile、orcfile表中。

行存储与列存储

行式存储下一张表的数据都是放在一起的,但列式存储下数据被分开保存了。

行式存储:
优点:数据被保存在一起,insert和update更加容易
缺点:选择(selection)时即使只涉及某几列,所有数据也都会被读取

列式存储:
优点:查询时只有涉及到的列会被读取,效率高
缺点:选中的列要重新组装,insert/update比较麻烦

TEXTFILE、SEQUENCEFILE 的存储格式是基于行b的;
RCFILE, ORCPARQUET 是基于式存储的。


TextFile

Hive默认的数据存储格式,数据不做压缩,磁盘开销大,数据解析开销大。 可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。

create table if not exists uaction_text(
    userid string,
    itemid string,
    behaviortype int,
    geohash string,
    itemcategory string,
    time string
)
row format delimited fields terminated by ','
stored as textfile;

load data local inpath '/home/hadoop/data/uaction.dat'
overwrite into table uaction_text;

SEQUENCEFILE

SequenceFile是Hadoop API提供的一种二进制文件格式,其具有使用方便、可分割、可压缩的特点。 SequenceFile支持三种压缩选择:none,record,block。Record压缩率低,一般建议使用BLOCK压缩。

RCFile

RCFile全称Record Columnar File,列式记录文件,是一种类似于SequenceFile的键值对数据文件。RCFile结合列存储和行存储的优缺点,是基于行列混合存储的RCFile。

RCFile遵循的“先水平划分,再垂直划分”的设计理念。先将数据按行水平划分为行组,这样一行的数据就可以保证存储在同一个集群节点;然后在对行进行垂直划分。


一张表可以包含多个HDFS block

在每个block中,RCFile以行组为单位存储其中的数据

row group又由三个部分组成
        用于在block中分隔两个row group的16字节的标志区
        存储row group元数据信息的header
        实际数据区,表中的实际数据以列为单位进行存储

ORCFile

ORC File,它的全名是Optimized Row Columnar (ORC) file,其实就是对RCFile做了一些优化,在hive 0.11中引入的存储格式。这种文件格式可以提供一种高效的方法来存储Hive数据。它的设计目标是来克服Hive其他格式的缺陷。运用ORC File可以提高Hive的读、写以及处理数据的性能。ORC文件结构由三部分组成:

文件脚注(file footer/ 图中最下紫色部分 ):包含了文件中 stripe 的列表,每个stripe行数,以及每个列的数据类型。还包括每个列的最大、最小值、行计数、求和等信息

postscript(图中最下白色部分):压缩参数和压缩大小相关信息

条带(stripe):ORC文件存储数据的地方。在默认情况下,一个stripe的大小为250MB, 条带又由以下组成: 

  • Index Data:一个轻量级的index,默认是每隔1W行做一个索引。包括该条带的一些统计信息,以及数据在stripe中的位置索引信息
  • Rows Data:存放实际的数据。先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个stream来存储
  • Stripe Footer:存放stripe的元数据信息

ORC在每个文件中提供了3个级别的索引:文件级、条带级、行组级。

借助ORC提供的索引信息能加快数据查找和读取效率,规避大部分不满足条件的查询条件的文件和数据块。

使用ORC可以避免磁盘和网络IO的浪费,提升程序效率,提升整个集群的工作负载。

事务表必须使用ORC格式.

create table if not exists uaction_orc(
    userid string,
    itemid string,
    behaviortype int,
    geohash string,
    itemcategory string,
    time string
)
stored as orc;

insert overwrite table uaction_orc select * from uaction_text;

Parquet

Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容Hadoop生态圈中大多数计算框架(Mapreduce、Spark等),被多种查询引擎支持(Hive、Impala、Drill等),与语言和平台无关的。

Parquet文件是以二进制方式存储的,不能直接读取的,文件中包括实际数据和元数据,Parquet格式文件是自解析的。


Row group:

  • 写入数据时的最大缓存单元
  • MR任务的最小并发单元
  • 一般大小在50MB-1GB之间

Column chunk:

  • 存储当前Row group内的某一列数据
  • 最小的IO并发单元

Page:

  • 压缩、读数据的最小单元
  • 获得单条数据时最小的读取数据单元
  • 大小一般在8KB-1MB之间,越大压缩效率越高

Footer:

  • 数据Schema信息
  • 每个Row group的元信息:偏移量、大小
  • 每个Column chunk的元信息:每个列的编码格式、首页偏移量、首索引页偏移量、个数、大小等信息
create table if not exists uaction_parquet(
userid string,
itemid string,
behaviortype int,
geohash string,
itemcategory string,
time string)
stored as parquet;

insert overwrite table uaction_parquet select * from uaction_text;

文件存储格式对比测试

说明:
1、给 linux123 分配合适的资源。2core;2G内存

2、适当减小文件的数据量(现有数据约800W,根据自己的实际选择处理100-300W条数据均可)

# 检查文件行数
wc -l uaction.dat

#
head -n 1000000 uaction.dat > uaction1.dat
tail -n 1000000 uaction.dat > uaction2.dat

文件压缩比

hdfs dfs -ls /user/hive/warehouse/mydb.db/ua*

ORC > Parquet > text

执行查询速度

SELECT COUNT(*) FROM uaction_text;
SELECT COUNT(*) FROM uaction_orc;
SELECT COUNT(*) FROM uaction_parquet;

-- text : 14.446
-- orc: 0.15
-- parquet : 0.146

orc 与 parquet类似 >> txt

在生产环境中,Hive表的数据格式使用最多的有三种:TextFile、ORCFile、Parquet。

  • TextFile文件更多的是作为跳板来使用(即方便将数据转为其他格式)
  • 有update、delete和事务性操作的需求,通常选择ORCFile
  • 没有事务性要求,希望支持Impala、Spark等多种查询引擎,建议选择Parquet

猜你喜欢

转载自blog.csdn.net/chengh1993/article/details/111837721