hive编程指南阅读笔记
1.基础操作
1.1.启动hive
HIVE_HOME
$ bin/hive
1.2.1.本地模式:用户的job在同一个JVM实例中执行所有的任务;
1.2.2.分布式和伪分布式:
设置自己的数据仓库目录:
set hive.metastore.warehouse.dir = /user/myname/hive/warehouse
最好将这个命令放置在$HOME/.hiverc文件中,每次启动都会执行这个文件.
1.2.3.JDBC连接元数据
2.数据类型和文件格式
2.1.基本数据类型
数据类型 | 长度 | 例子 |
---|---|---|
TINYINT | 1byte有符号整数 | 20 |
SMALLINT | 2byte有符号整数 | 20 |
INT | 4byte有符号整数 | 20 |
BIGINT | 8byte有符号整数 | 20 |
BOOLEAN | 布尔类型,true或false | true |
FLOAT | 单精度浮点数 | 3.14159 |
DOUBLE | 双精度浮点数 | |
STRING | 字符序列,可以指定字符集(''或"") | |
TIMESTAMP | 整数,浮点数或者字符串 | |
BINNARY | 字节数组 |
hadoop和hive强调优化磁盘的读和写的性能,而限制列的值的长度相对来说并不重要.
2.2.集合数据类型
数据类型 | 描述 | 示例 |
---|---|---|
STRUCT | 可以通过"点"符号访问元素内容.如,SRTUCT{first STRING,last STRING},那么第1个元素可以通过 字段名.first来引用; | struct{'John','Doe'} |
MAP | Map是键值对元组集合,使用数组表示法可以访问元素. 字段名['last']获取最后一个元素 | map('first','JOIN','last','Doe') |
ARRAY | 数组是一组具有相同类型和名称的变量的集合.每个数组元素编号从0开始.访问:数组名[1]; | Array('John','Doe') |
Hive中没有键的概念.但是,用户可以对表建立索引.
CREATE TABLE employee (
name STRING,
salary FLOAT,
subordinates ARRAY<STRING>,
deductions MAPstreet:STRING,city:STRING,state:STRING,zip:INT
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\001'
COLLECTION ITEMS TERMINATED BY '\002'
MAP KEYS TERMINATED BY '\003'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;
#也可以用','.'\t'制表符作为字段分隔符
#SRTUCT可以混合多种不同的数据类型,但是STRUCT一旦声明好结构,其位置就不可以再改变;
2.3.文本文件数据编码
HIVE中默认的记录和字段分隔符
分隔符 | 描述 |
---|---|
\n | 对于文本来说,每行都是一条记录,因此换行符可以分割记录 |
^A | 用于分割字段(列),在CREATE TABLE 语句中可以使用八进制编码\001表示 |
^B | 用于分割ARRAY或STRUCT中元素,或用于MAP中键值对之间的分割.在CREATE TABLE中可使用\002表示 |
^C | 用于MAP中键值对之间的分割.在CREATE TABLE语句中可以使用八进制编码\003表示. |
2.4.读时模式
传统数据库:写时模式(schema on write),即在数据写入时对模式进行检查;
hive:读时模式(schema on read);
3.HiveQL:数据定义
3.1.Hive中的数据库
hive.metastore.warehouse.dir指定数据所在目录
默认配置:/user/hive/warehouse
修改默认位置
hive> create database financials
> location '/my/preferred/directory';
添加描述信息
hive> create database financials
> comment 'Holds all financial tables';
hive> describe database financials;
#显示数据库的描述信息和所在位置;
hive> create database financials
> with dbproperties('creator'='Math Moneybags','date'='2017-04-12'
hive> describe database extended financials;
#该语句可以显示更多数据库信息;
hive>set hive.cli.print.current.db=true
#在hive客户端显示当前数据库名称,如下
hive(finacials)> use default;
hive(default)>set hive.cli.print.current.db=false;
hive> ...
hive> drop database if exists financials casecade;
#hive不允许用户删除一个包含表的数据库的.要么先删除数据库中的表,然后再删除数据.要么加关键字casecade;
3.2.修改数据库
hive> alter database financials set dbproperties('edit-by'='Joe Dba');
3.3.创建表
create table if not exists mydb.employees(
name string comment 'Employee name',
salary float comment 'Employee salary',
subordinates array<string> comment 'name of subordinates',
deductions map<string,float> commment '...',
address struct<street:string,city:string,state:string,zip:int> comment 'home address'
)
comment 'description of the table'
tblproperties('creator'='lgr','create_at'='2017-3-2 10:00:00')
location '/user/hive/warehouse/mydb.db/employees';
#如果用户使用了if not exists,而且这个这个已经存在的表和create table语句 后面指定的模式是不同的.hive会忽略掉这个差异.
#hive会自动增加两个属性:last_modified_by,last_modified_time;
hive> show tblproperties table_name;#列举该表的属性信息
拷贝一张已经存在的表的模式(无数据)
create table if not exists mydb.employees2
like mydb.employees;
#可接受可选的location属性
hive> show tables;#显示当前数据库下的表;
hive> show tables in mydb;#显示mydb数据库中的表;
hive> show tables 'empl.*';
#注:in database_name语句和对表名使用正则表达式两个功能尚不支持;
hive> describe extened mydb.employees;#查看当前表详细结构 信息;
hive> describe formatted mydb.employees;#比extend输出更多详细信息;
3.4.管理表(内部表)
Hive对管理表(或多或少的)控制着数据的生命周期.
当删除一个管理表时,hive也会删除这个表中数据;
管理表不方便和其它工作共享数据.
3.5.外部表
create external table if not exists stocks(
exchange string,
symbol string,
ymd string,
price_open float
)
row format delimited fields terminated by ','
loocation '/data/stocks';
#关键字ecternal,location(告诉hive数据位于哪个目录);
#外部表:删除该表并不会删掉数据,不过描述表的元数据信息会被删除掉;
hive> describe extended tablename;
#可在末尾看出管理表(内部表)还是外部表;
#...tableType:MANAGED_TABLE):管理表(内部表);
#...tableType:EXTERNAL_TABLE):外部表;
#表结构复制
create external table if not exists mydb.emmployees3
like mydb.employees
location 'path/to/data';
3.5.分区表/管理表
分区表:性能优势(优化查询性能),分层存储;
创建分区表
create table employee(
name string,
salary float,
subordinates array<string>,
deductions map<string,float>,
address struct<street:string,city:string,state:string,zip:int>
)
partition by (country string,state string);
#分区表目录
#.../employees/country=CA/state=AB
#.../employees/country=CA/state=BC
#...
#.../employees/country=CA/state=AL
#...
如果表中的数据以及分区个数都很大的话,还行没有where条件的语句会触发巨大的MapReduce任务.高度建议的安全措施是将Hive设置为"strict(严格)"模式.
hive> set hive.mapred.mode=strict;
hive> show partitions employees;#查看表中存在的所有分区;
#...
#country=CA/state=AB
#country=CA/state=BC
#country=US/state=AL
#...
#查看特定分区键的分区
hive> show partition emploees partition(country='US');
通过载入数据方式创建分区
load data local inpath '${env:HOME}/californai-employees'
into table employees
partition (country='US',state='CA');
3.5.1.外部分区表
创建log日志分区表
create external table if not exists log_message(
hms int,
severity string,
server string,
process_id int,
message string
)
paratitioned by (year int,month int,day int)
row format delimited fields terminated by '\t';
#添加分区
hive> alter table log_message add partition(year=2012,month=1,day=2);
将旧数据存储到廉价设备(如像Amazon S3),较新数据保存到HDFS中
#1.将分区下的数据拷贝到S3中.例如,用户可以使用hadoop distcp命令
hadoop distcp /data/log_message/2011/12/02 s3n://ourbucket/logs/2011/12/02
#2.修改表,将分区路径指向S3路径:
alter table log_message partition (year=2011,month=12,day=2)
set location 's3n://ourbucket/logs/2011/12/02';
#3.使用hadoop fs -rmr命令删除HDFS中的分区
hadoop fs -rmr /data/log_message/2011/12/02
3.5.2.自定义表的存储格式
支持存储文件类型
textfile/sequencefile/rcfile
#自定义
row format serde
stored as inputformat ... outputformat ...
clustered by ... into ... buckets...;#分桶表数据存储;
#后面可以接一个可选的stored by ... 子句,优化某些特定类型的查询;
3.6.删除表
drop table if exists employees;
1.管理表(内部表):表的元数据信息和表数据都会被删除;
用户可开启hadoop回收站功能(默认关闭).数据则会被转移到用户分布式系统根目录的.Trash目录下,也就是HDFS的/user/$USER/.Trash目录.配置fs.trash.internal;(单位是分钟 1440=24h);
3.7.修改表
Alter table:修改元数据,不会修改数据本身.可用于修改表模式中出现的错误,改变分区路径等.
1表重命名
alter table log_messages rename to logmsgs;
2.增加,修改和删除表分区
#添加表分区
alter table log_messages add if not exists
partition (year=2011, month=1, day=1)
location '/logs/2011/01/01';
#修改某个分区路径
alter table log_messages partition(year = 2011,month=12, day=2)
set location 's3n://ourbucket/logs/2011/01/02';
#删除某个分区
alter table log_messages drop if exists partition (year=2011, month=12, day=2);
3.修改列信息
alter table log_messages
change column hms hours_minutes_seconds int
comment 'The hours,minutes,and seconds part of the timestamp'
after severity;#将字段移到severity之后;
#first : 将字段移动到第一个位置;
4.增加列
用户可以在分区字段之前增加新的字段到已有的字段之后;
alter table log_messages add columns(
app_name string comment 'application name',
session_id long comment 'the current session_id'
);
如果新增的字段中有某个或多个字段位置是错误的,需要使用下面语句逐一将字段调整到正确的位置
alter colume tableName change colume after fieldName;
5.删除或替换列
alter table log_messsages replace columns(
hoours_mins_sec int comment 'hour,minute,seconds from timestamp',
severity string comment 'the message severity'
message string comment 'the rest of the message'
)
#这个语句实际上 重命名了之前的hms字段,并且从之前的表定义的模式中移除了字段server和process_id.因为是alter语句,所以只有表的元数据信息改变了.
6.修改表属性
用户可以增加附加的表属性或者修改已经 存在的属性,但是无法删除 属性
alter table log_messages set tblproperties(
'note' = 'the process id is no longer captured;'
)
7.修改存储属性
alter table log_messages
partition(year=2016,month=2,day=1)
set fileformat sequencefile;
#下面例子演示使用一个名为com.example.JSONSerDe的Java类来处理记录使用JSON编码的文件:
alter table table_using_JSON_storage
set serde 'com.example.JSONSerDe'
with serdeproperties(
'prop1'='value1',
'prop1'='value2'
);
#修改存储属性
alter table stocks
clustered by (exchange,symbol)
stored by (symbol) #可选
into 48 buckets;
8.众多的修改表语句
为各种操作添加执行钩子;
alter table ... touch语句用户 触发这些钩子
alter table log_messages touch
partition(year=2016,month=1,day=2);
#应用场景,当表中存储的文件哎Hive之外被修改,就会触发钩子的执行.如:某个脚本往分区2012/01/01中写入了新的日志信息,可以在Hive CLI中进行下面的调用:
hive -e 'alter table log_message touch partition(year=2016,month=2,day=2');
#如果表或者分区不存在,这个语句不会创建表或分区.这种情况需要使用合适的创建策略
alter table ... archive partition
#上面语句会将这个分区的文件打成一个Hadoop压缩(HAR)文件.
alter table log_messages archive
partition(year=2016,month=2,day=1);
#unarchive 反向操作
#下面语句可以防止分区被删除或被查询
alter table log_messages
partition(year=2016,month=2,day=2) enable no_drop;
alter table log_messages
partition(year=2016,month=2,day=1) enable offline;
#disable 反向操作
4.HiveQL:数据操作
1.向管理表中装载数据
load data local inpath '${env:HOME}/california-employees'
overwrite into table employees
partition (country='US',state='CA');
#通常情况下指定的路径应该是一个目录,而不是单独的文件.
#overwrite:会将目标文件中之前存在的数据删除.如果没有overwrite关键字,则不会删除之前的数据;
#inpath子句中使用的文件路径不能包含任何文件夹;
#hive并不会验证装在的数据和表的模式是否匹配.然而,会验证文件格式是否和表结构定义的一致.
2.通过查询语句向表中插入数据
insert [overwirte/into] table employees
partition (country='US',state='OR')
select * from staged_employees se
where se.cnty='US' and se.st='OR';
#如果没有overwrite或者使用into,hive会追加的方式写入数据,不会覆盖之前已经存在的内容;
#下面例子显示了如何为3个州创建表employees分区:
from staged_employees se
insert overwrite table employees
partition(country='US',state='OR')
select * where se.cnty='US' and se.st='OR'
insert overwrite table employees
partition(country='US',state='CA')
select * where se.cnty='US' and se.st='CA'
insert overwrite table employees
partition(country='US',state='IL')
select * where se.cnty='US' and se.st='IL'
3.动态分区插入
insert overwrite table employees
partition (country,state)
select ...,se.cnty,se.st
from staged_employees se;
混合使用静态分区和动态分区
insert overwirte table employees
partition(country='US',state)
select ...,se.cnty,se.st
from staged_employees se
where se.cnty = 'US';
#静态分区键必须出现在动态分区键之前!
动态分区属性
属性名称 | 缺省值 | 描述 |
---|---|---|
hive.exec.dynamic.partition | false | 设置为true,表示开启动态分区功能 |
hive.exec.dynamic.mode | strict | 设置为nonstrict,表示允许所有分区都是动态的 |
hive.exec.max.dynamic.partitions.pernode | 100 | 每个mapper和reducer可以创建的最大动态分区个数.如果某个mapper或reducer尝试创建大于这个值的分区的话则会抛出一个致命的错误信息 |
hive.exec.max.dynamic.partitions | +1000 | 一个动态分区创建语句可以创建的最大动态分区个数.如果超过这个值则会抛出一个致命错误 |
hive.exec.max.created.files | 100000 | 全局可以创建的最大文件个数.有一个Hadoop计数器会跟踪记录创建了多少个文件,如果超过这个值则会抛出一个致命的错误信息. |
4.单个查询语句中创建表并加载数据
create table ca_employees
as select name,salary,address
from employees
where se.state='CA';
#使用场景:从一个大的宽表中选取部分需要的数据集.
#此功能不能用于外部表
5.导出数据
如果数据文件格式 一致
hadoop fs -cp source_path target_path
insert...directory
insert overwirte local directory '/tmp/ca_employees'
select name,salary,address
from employees
where se.state = 'CA';
#一个或多个文件将会被写入到/tmp/ca_employees,具体个数取决于调用的reducer个数.
用户也可以通过如下方式指定多个输出文件夹目录:
from staged_employees se
insert overwrite directory '/tmp/or_employees'
select * where se.cty='US' and se.st='OR'
insert overwrite directory '/tmp/or_employees'
select * where se.cty='US' and se.st='CA'
insert overwrite directory '/tmp/or_employees'
select * where se.cty='US' and se.st='IL'
对于定制输出的数据是有一些限制 的(除非自己写一个定制的outputformat);
表的字段分隔符是需要考量的.如,如果使用的是默认的^A分隔符,而用户又经常导出数据的话,那么可能使用逗号或制表键作为分隔符会更合适;
另一种 变通的方式是定义一个"临时 表",这个表的存储方式配置成期望的输出格式(例如,使用制表键作为字段分隔符)..然后再从这个临时表中查询数据,并使用insert overwrite directory将查询结果写入到这个表中.
5.HiveQL:查询
1.select...from
Array取值查询
#假设subordinates是一个Array类型
hive> select name,subordinates[0] from employees;
#注意,引用一个不存在的元素 将会返回null.同时,提取出的string数据类型值将不再加引号!
引用MAP类型中的元素查询
hive> select name,deuction['state taxes'] from employees;
引用Struct类型中的元素查询
hive> select name,address.city from employees;
1.1.使用正则表达式指定列
从stocks中查询出symbol列和所有以price作为前缀的列名
hive> select symbol,'price.*' from stocks;
1.2.使用列值进行计算
hive> select upper(name),salary,deductions["Federal Taxes"],
> round(salary*(1-deuctions["Federal Taxes"])) from employees;
1.3.使用函数
数学函数
返回值类型 | 样式 | 描述 |
---|---|---|
bigint | round(double d) | |
double | round(double d,int n) | 返回d的保留n位小数的double近似值 |
bigint | floor(double d) | 返回<=d的最大bigint型值 |
bigint | ceil(double e)/ceiling(double d) | 返回>=d的最小bigint型值 |
double | rand()/rand(int seed) | 返回一个double随机数,整数seed是随机因子 |
double | exp(double d) | 返回e的d幂次方 |
double | ln(double d) | |
double | log10(double d)/log2(double d) | |
double | log(doule base,double d) | 以base为底d的对数. |
double | pow(double d,double p)/power(d,p) | 计算d的p次幂 |
double | sqrt(double d) | 计算d的平方根 |
string | bin(double i) | 计算二进制值i的string类型值 |
string | hex(bigint i)/hex(string str)/hex(binary b) | 十六进制 |
string | unhex(string i) | hex的逆方法 |
string | conv(bigint/string num,int from,int to) | 将bignt或string类型的num从from进制转换为to进制 |
double | abs(double i) | 绝对值 |
int | pmod(int i1,int i2) | i1对i2取模 |
需要注意的是函数floor,round和ceil输入的是double,返回的是bigint类型.
聚合函数
返回值类型 | 样式 | 描述 |
---|---|---|
bigint | count(*)/count(expr)/count(distinct expr) | |
double | sum(col)/sum(distinct col) | |
double | avg(col)/sum(distinct col) | |
double | min(col)/max(col) | |
double | varince(col),val_pop(col) | 一组数方差 |
double | var_samp(col) | 样本方差 |
double | stddev_pop(col) | 标准偏差 |
double | stddev_samp(col) | 标准样本偏差 |
double | convar_pop(col1,col2) | 协方差 |
double | convar_samp(col1,col2) | 样本协方差 |
double | corr(col1,col2) | 两组数值的相关系数 |
double | percentile(bigint int_expr,p) | int_expr是在p([0,1])处的对应的百分比.p是double型数值. |
array<double> | percentile(bigint int_expr,array(p1,...)) | |
double | percentile_approx(double col,p[,NB]) | NB是用于估值的直方图中仓库数量(默认是10000) |
array<double> | percentile_approx(double col,array(p1[,p2...])[,NB]) | |
array<struct{'x','y'}> | histogram_numeric(col,NB) | 返回NB数量的直方图仓库数组,x是中心,y是仓库的高 |
array | collect_set(col) | 返回集合col元素排重后的数组 |
通常可以设置属性hive.map.aggr值为true来提高聚合的性能
hive> set hive.map.aggr = true;
#关于distinct的bug:
hive> select count(distinct symbol) from stocks;
0
#结果为什么是0呢?当使用count(distict col)而同时col是分区列时存在这个bug.
hive> select count(distinct ymd),count(distinct volumn) from stocks;
12100 26110
#案例来说,一个查询语句中不允许使用多于一个的函数(distinct..)表达式,但上面语句实际是可以执行的;
1.4.表生成函数
可以将单列扩展成多列或多行.
hive> select explode(subordinates) as sub from employees;
#subordinates字段内容为空时,不会产生新的记录.不为空则会产生一行新纪录;
hive> select parse_url_tuple(url,'host','path','query') as (host,path,query) from url_table;
表生成函数
返回值类型 | 样式 | 描述 |
---|---|---|
N行结果 | explode(ARRAY array)/explode(MAP map) | |
数组类型 | explode(ARRAY<TYPE>,a) | explode()会生成一行记录包含这个元素 |
结果插入表 | inline(ARRAY<STRUCT[,STRUCT]>) | 将结构体数组提取出来并插入到表中 |
tuple | json_tuple(STRING jsonStr,p1,p2,..,pn) | 对输入的JSON字符串进行处理 |
tuple | parse_url_tuple(url,partname1,partname2,...) | HOST,PATH,QUERY,REF,PROTOCOL,AUTHORITY |
N行结果 | stack(int n,col1,...,colM) | 把M列转换成N行,每行有M/N个字段,n必须是常数 |
1.5.其它内置函数
这些函数用于处理字符串,Map,数组,JSON和时间戳
返回类型:BINARY binary(STRING s) 输入值转换成二进制
cast(<expr> as <type>);如:cast('1' as bigint);
STRING concat(STRING s1,STRING s2,...)
STRING concat_ws(STRING separator,STRING s1,STRING s2,...)使用指定分隔符进行拼接;
p111页
1.6.嵌套select语句
from(
select upper(name),salary,deductions["Federal Taxes"] as fed_taxes,
round(salary*(1-deductions["Federal Taxes"])) as salary_minus_fed_taxes
from employees
) e
select e.name,e.salary_minus_fed_taxes
where e.salary_minus_fed_taxes > 7000;
1.7.case...when..then
与if条件语句类似,用于处理单个列的查询结果
select name,salary,
case
when salary < 50000.0 then 'low'
when salary >= 50000.0 and salary < 70000.0 then 'middle'
else 'high'
end as bracket from employees;
1.8.什么情况下可以避免进行MapReduce
set hive.exec.mode.local.auto = true;
#最好将set.hive.exec.mode.local.auto = true;设置到$HOME/.hiverc配置文件中.
2.where语句
不能在where语句中使用列别名
2.1.谓词操作符
=,<=>,==,<>,!=,<,<=,>,>=,a [not] between b and c,a is null,a is not nll,
a [not] like b,a relike b,a regexp b,
2.2.关于浮点数比较
select name,salary
from employees
where salary > cast(0.2 as float);
#第三种方案:即钱相关的都避免使用浮点数.
2.3.LIKE和RLIKE
RLIKE是LIKE的扩展,支持正则
select name, address.street
from employees
where address.street rlike '.*(Chicago|Ontario).*';
Mary Smith 100 Ontario St.
Todd Jones 200 Chicago Ave.
3.GROUP By语句
group by ...
having ... : 如果不是having过滤条件,那么需要嵌套select子查询
4.JOIN语句
1INNER JOIN
join...on...:Hive中的ON语句之后不能接<=,OR这种语句;
2.JOIN优化
当对3个或更多表进行JOIN连接时,如果每个ON子句都使用相同的连接键的话,那么只会产生一个MapReduce job;
用户需要保证连续查询中表的大小从左到右是依次增加的.
用户还可以利用"标记"机制显式的告之查询优化器那张表是大表:
select /*streamtable(s)*/s.ymd,s.symbol,s.price_close,d.dividend
from stocks s join dividends d on s.ymd=d.ymd and s.symbol=d.symbol
where s.symbol='APPL;\
#这样,Hive将会 尝试将表stocks作为驱动表,即使其在查询中不是位于最后面的.
3.LFET OUTER JOIN
4.OUTER JOIN
5.RIGHT OUTER JOIN
6.FULL OUTER JOIN
7.LEFT SEMI-JOIN
左半开连接会返回左表的记录,前提是其记录对于右边表满足ON语句中的判定条件.
#Hive中不支持的查询
select s.ymd,s.symbol,s.price_close
from stocks s
where s.ymd,s.symbol in
(select d.ymd,d.symbol from dividends d);
#Hive支持的LEFT SEMI JOIN语法
select s.ymd,s.symbol,s.price_close
from stocks s
left semi join dividends s
on s.ymd=d.ymd and s.symbol=d.symbol;
#注意:Hive不支持右半开连接(right semi join)
#semi join通常比innner join更搞笑,因为对于左表中一条指定的记录,在右边中一旦找到匹配的记录,Hive会立即停止扫描.
8.笛卡尔积JOIN
如下:
select * from stocks join dividends;
下面查询很多数据库会被优化成内连接(INNER JOIN),但在Hive中并没有此优化
select * from stocks join dividends
where stocks.symbol = dividends.symbol and stock.symbol = 'APPL';
9.map-side JOIN
如果只有一张表是小表,那么可以在最大的表通过mapper的时候将小表完全放到内存中.Hive可以在map端执行连接过程(称为map-side JOIN).
两种方式:
方式一:
select /*+MAPJOIN(d)*/ s.ymd,s.symbol,s.price_close,d.dividend
from stocks s join fividends d on s.symd = d.ymd and s.symbol = d.symbol
where s.symbol = 'APPL';
方式二:仅Hive v0.7版本之后
hive> set hive.auto.convert.join = true;
hive> select /*+MAPJOIN(d)*/ s.ymd,s.symbol,s.price_close,d.dividend
> from stocks s join fividends d on s.symd = d.ymd and s.symbol = d.symbol
> where s.symbol = 'APPL';
#用户也可以配置能够使用这个优化的小表的大小,如下属性是默认的(单位是字节):
hive.mapjoin.smalltable.filesize = 25000000
#自动启动优化,可以将这个一个(或两个)属性设置在$HOME/.hiverc文件中;
如果表中的数据是分桶的,那么对于大表,在特定的情况下同样可以使用这个优化.
表中的数据必须是按照ON语句中的键进行分桶的,而且其中一张表的分桶的个数必须是另一张表分桶个数的若干倍.当满足这些条件时,那么Hive可以在map阶段按照分桶数据进行分桶连接.因此这种情况,不需要先获取列表中所有的内容,之后才去和另一个表中每个分桶进行匹配连接.
这个优化没有默认开启,需设置参数:
set hive.optimize.bucketmapJOIN=true;
如果所涉及的分桶表都具有相同的分桶数,而且都是按照连接键或桶的键进行排序,那么Hive可以执行一个 更快的分类-合并连接(sort-merge JOIN).设置如下:
set hive.input.format=org.apache.hadoop.hive.sql.io.BucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin=true;
set hive.optimize.bucketmapjion.sortedmerge=true;
5.ORDER BY和SORT BY
ORDER BY
会对查询结果集执行一个全局排序.也就是说会有一个所有的数据都通过一个reducer进行处理的过程.对于大数据集,这个过程可能会消耗太过漫长的时间来执行.
SORT BY
只会在每个reducer中对数据进行排序,也就是执行一个局部排序过程.可以保证每个reducer输出数据都是有序的(局部有序).这样可以提高后面进行的全局排序效率.
DESC 降序
ASC 升序
#可以知道,当reducer数>1时,输出结果排序就大不一样.
因为ORDER BY操作可能会导致运行时间过长,如果属性hive.mapred.mode=strict的话,那么Hive要求这样的语句必须加LIMIT语句进行限制.默认情况下这个属性是nonstrict,也就不会有这样的限制.
6.含有SORT BY的DISTRIBBUTE BY
DISTRIBUTE BY控制map输出在rducer中如何划分的.
通常用户不需担心这个特性.但,若使用了Streaming特性以及一些状态为UDAF的查询时例外.还有,在另外一个场景下,使用这些语句实有用的.
默认情况下,MapReduce计算框架会依据map输入的键计算响应的哈希值,然后按照得到的哈希值将键值对均匀分发到多个reducer中去.但不幸的是,这也就意味着我们使用sort by时,不同reducer的输出内容会有明显重叠,至少对于排序顺序而言是这样,即使每个reducer的输出数据是有序的.
假如我们希望具有相同股票交易码的数据在一起处理,那么我们可以使用DISTRIBUTE BY来保证具有相同股票交易码的记录会分发到同一个reducer中进行处理,然后使用SORT BY来按照我们的期望对数据进行排序.如下:
hive>SELECT s.ymd,s.symbol,s.price_close
>FROM stocks s
>DISTRIBUTE BY s.symbol
>SORT BY s.symbol ASC,s.ymd ASC;
#DISTRIBUTE BY个GROUP BY控制着reducer是如何接受一行行数据进行处理这方面是类似的.
#而SORT BY则控制着reducer内的数据是如何进行排序的.
#需要注意的是,Hive要求DISTRIBUTE BY语句要写在ORDER BY语句之前.
7.CLUSTER BY
在上面例子中,s.symbol列被用在 了DISRTIBUTE BY语句中,而s.symbol列和s.ymb位于SORT BY语句中.如果这2个语句中涉及到的列完全相同,而且采用的是升级排序方式,也就是默认的排序方式(也就是默认的排序方式),那么在这种情况下,CLUSTER BY就等价于前面的2个语句
SELECT s.ymd,s.symbol,s.price_close
FROM stocks s
CLUSTER BY s.symbol;
#使用DISTRIBUTE BY ... SORT BY语句或其简化版的CLUSTER BY语句会剥夺SORT BY的并行性,然而这样可以实现输出文件的数据是全局排序的.
8.类型转换
语法
cast(value AS TYPE)
类型转换BINARY值
SELECT (2.0*cast(cast(b as string) as double)) from src;
#b字段类型是BINARY
9.抽样检查
Hive可以通过分桶对表进行分桶抽样检查
rand()函数抽样
SELECT * from numbers TABLESAMPLE(BUCKET 3 OUT OF 10 ON rand()) s;
按照指定 列分桶
SELECT * FROM numbers TABLESAMPLE(BUCKET 5 OUT OF 10 ON number) s;
#分桶语句这种分母表示的是数据会被散列的桶的个数;
#分子表示将会选择桶的个数;
1.数据块抽样
基于行数,按照输入路径下的数据块百分比进行的抽样
hive> SELECT * FROM numbersflat TABLESAMPLE(0.1 PERCENT) s;
#注:这种抽样方式不一定适用所有的文件格式.另,这种抽样的最小抽样单元是一个HDFS数据块.因此,如果表的数据大小小于普通的块大小128MB的话,那么将会返回所有行.
基于百分比的抽样方式提供了一个变量,用于控制基于数据块的调优的种子信息.
hive.sample.seednumber
0
2.分桶表的输入裁剪
如果TABLESAMPLE语句中指定的列和CLUSTERD BY语句中指定的列相同,那么TABLESAMPLE查询就只会扫描涉及到哈斯分区下的数据
hive> CREATE TABLE numbers_bucketed(number int) CLUSTERED BY (number) INTO 3 BUCKETS;
hive> set hive.enforce.bucketing=true;
hive> dfs -ls /user/hive/warehouse/mydb.db/numbers_bucketed;
/user/hive/warehouse/mydb.db/numbers_bucketed/000000_0
/user/hive/warehouse/mydb.db/numbers_bucketed/000001_0
/user/hive/warehouse/mydb.db/numbers_bucketed/000002_0
hive> dfs -cat /user/hive/warehouse/mydb.db/numbers_bucketed/000001_0;
1
7
10
4
hive> SELECT * FROM numbers_bucketed TABLESAMPLE (BUCKET 2 OUT OF 3 ON NUMBER) s;
10.UNION ALL
6.HiveQL:视图
Hive目前暂不支持物化视图.从逻辑上讲,Hive先执行这个视图,然后使用这个结果进行余下后续的查询.
1.使用视图来降低查询复杂度
CREATE VIEW shorter_join AS
SELECT * FROM people JOIN cart
ON (cart.people_id = people.id) WHERE firstname = 'john';
SELECT lastname FROM shorter_join WHERE id = 3;
2.使用视图来限制基于条件过滤的数据
hive> CREATE TABLE employee (firstname string, lastname string,ssn string,
> password string,department string);
hive> CREATE VIEW techops_employee AS
> SELECT firstname,lastnname,ssn FROM employee WHERE department='techops';
3.动态分区中的视图和map类型
将其整行作为一个map处理,而不是一列固定的列.
CREATE VIEW orders(state,city,part) AS
SELECT cols["state"],cols["city"],cols["part"]
FROM dynamictable
WHERE cols["type"] = "request";
CREATE VIEW shipments(time,part) AS
SELECT cols["time"],cols["part"]
FROM dynamictable
WHERE cols["type"] = "response"
4.视图零零碎碎相关事项
视图实际上是对其所使用到的表和列的一个查询语句固化过程.
CREATE VIEW IF NOT EXISTS shipments(time,part)
COMMENT 'Time and parts for shipments.'
TBLPROPERTIES ('creator' = 'me')
AS SELECT ... ;
CREATE TABLE shipements2
LIKE shipments;
DROP VIEW IF EXISTS shipments;
ALTER VIEW shipments SET TBLPORPERTIES('created_at'='some_timestamp');
7.HiveQL:索引
1.创建索引
外部表和视图
CREATE INDEX employees_index
ON TABLE employees(country)
AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler'
WITH DEFERRED REBUGILD
IDXPROPERTIES ('creator = me','created_at' = 'some_time')
IN TABLE employees_index_table
PARTITIONED BY (country,name)
COMMENT 'Employees indexed by country and name';
如果省略PARTITIONED BY语句,那么索引将会包含原始表的所有分区.
AS ... 语句指定了索引处理器.
如果需要索引处理器在一张新表中保存索引数据,则使用IN...TABLE子句.
1.1.Bitmap索引
bitmap索引普遍应用于排重后值较少的列.
CREATE INDEX employees_index
ON TABLE employees(country)
AS 'BITMAP' #v0.8.0新增的内置bitmap索引处理器.
WITH DEFREEED REBUILD
IDXPROPERTIES ('creator' = 'me','created-at' = 'some_time')
IN TABLE employees_index_table
PARTITIONED BY (country,name)
COMMENT 'Employees indexed by country and name.'
2.重建索引
重建索引操作时原子性的.
如果用户指定了DEFERRED REBUILD,那么新索引将呈现空白状态.在任何时候都可以进行第一次索引创建或使用ALTER IDNEX对索引进行重建.
ALTER INDEX emplyees_index
ON TABLE employees
PARTITION (country = 'US')
REBUILD;
#如果省略掉PARTITION,那么将会对所有分区进行重建索引.
#还没有一个内置的机制能够在底层的表或者某个特定的分区改变时,自动触发重建索引.但是,如果用户具有一个工作流来更新表分区中的数据的话,那么用户可能已经在某处使用到了ALTER TABLE ... TOUCH PARTITION(...)功能,同样的,在这个工作流中也可以对对应的索引执行重建索引语句ALTER INDEX ... REBUILD.
3.显示索引
SHOW [FORMATTED] INDEX/INDEXES ON employees;
4.删除索引
如果有索引表的话,删除一个索引将会删除这个索引表.
DROP INDEX IF EXISTS employees_index ON TABLE employees;
5.实现一个定制化的索引处理器
完整例子:
https://cwiki.apache.org/confluence/display/Hive/IndexDev#space-menu-link-content
源代码例子:
org.apache.hadoop.hive.sql.index.compact.CompactIndexHandler
创建好索引后,实现索引处理器的Java代码也要做一些初始验证和为索引表(如果用到)定义模式的过程.同时还要实现重建索引处理逻辑,其会读取需要创建索引的表,然后写索引存储(例如索引表).当索引被删除后,处理器还需要清理掉所有为索引所用到的非表存储.如果需要,还要依赖Hive去删除索引表.处理器必须能够参与到优化查询中.
8.模式设计
1.按天划分的表
分区表
ALTER TABLE supply add PATITION (day = 20110102);
ALTER TABLE supply add PATITION (day = 20110103);
2.关于分区
创建很多的分区确实可以优化一些查询,但是同时可能会对其他一些重要的查询不利;
一个理想的分区方案不应该导致产生太多的分区和文件件目录,并且每个目录下的文件应该足够的大,应该是文件系统中块大小的若干倍.按时间范围进行分区是一个好的策略就是按照不同的时间粒度来确定合适大小的数据积累量.而且随着时间的推移,分区数量的增长是"均匀的".但同时要考虑where子句选择较小粒度的范围情况.
另一种方案是使用两个级别的分区并且使用不同的维度.如:第一个分区按时间划分,二级分区按州名划分:
hive> CREATE TABLE weblogs ( url string, time long, city string)
> PARTITION BY (day int, state string);
hive> SELECT * FROM weblogs WHERE day = 20110202;
#但由于州的数据可能分布不均匀,map task处理数据会出现不均.
#如果不能够找到好的,大小相对合适的分区方式的话,那么可以考虑使用分桶存储;
#MapReduce会将一个任务(job)转换成多个任务(task).默认情况下,每个task都是一个新的JVM实例,都需要开启和销毁的开销.对于小文件,每一个文件都会对应一个task.JVM开启和销毁的时间会比实际处理数据的时间消耗要长.
3.唯一键和标准化
Hive没有主键或基于序列秘钥生成的自增键的概念,如果可以,应避免对非标准化数据进行连接(JOIN)操作.
避免标准化的主要原因是为了最小化磁盘寻道,比如那些通常需要外键关键的请况.非标准化数据 允许被扫描或写入到大的,连续的磁盘存储区域,从而优化磁盘驱动器的I/O性能.
4.同一份数据多种处理
Hive本省提供了一个独特的语法,可以从一个数据源产生多个数据聚合,而无需每次聚合都要重新扫描一次.
#执行效率低的方法
hive> INSERT OVERWRITE TABLE sales
> SELECT * FROM history WHERE action = 'purchased';
hive> INSERT OVERWRITE TABLE credits
> SELECT * FROM history WHERE action = 'returned';
#高效率语法,只需扫描一次history表
hive> FROM history
> INSERT OVERWRITE sales SELECT * WHERE action = 'purchased'
> INSERT OVERWRITE credits SELECT * WHERE action = 'returned';
5.对于每个表的分区
对中间表(临时表)进行分区,避免后面的数据内容覆盖之前的临时表数据;
$ hive -hiveconf dt = 2011-01-01
hive> INSERT OVERWRITE table distinct_ip_in_logs
> PARTITION ( hit_date = ${dt}) #这里对表进行分区
> SELECT distinct(ip) as ip from weblogs
> WHERE hit_date = '${hiveconf:dt}';
hive> CREATE TABLE state_city_for_day (state string,city string)
> PARTITION BY (hit_date string);
hive> INSERT OVERWRITE table state_city_for_day PAARTITION(${hiveconf:df})
> SELECT distinct(state,city) FROM distinct_ip_in_logs
> JOIN geodate ON (distinct_ip_in_logs.ip=geodate.ip)
> WHERE (hit_date = '${hiveconf:dt}');
#这种方法缺点是用户需要管理中间表并删除旧分区.
6.分桶表数据存储
分区可以提供一个隔离数据和优化查询的便利方式.但要确定合适的划分大小这个疑虑.
字段值会根据用户指定的值进行哈希分发到桶中.
假如用户数要比桶数多得多,那么每个桶内就会包含多个用户记录.
hive> CREATE TABLE weblog (user_id INT,url STRING, source_ip STRING)
> PARTITION BY (dt STRING)
> CLUSTERED BY (user_id) INTO 96 BUCKETS;
INSERT...TABLE语句正确地填充表.需要设置属性强制Hive为目标表的分桶初始化过程设置一个正确的reducer个数
hive> SET hive.enforce.bucketing = true;
hive> FROM raw_logs
> INSERT OVERWRITE TABLE weblog
> PARTITION (dt = '2009-02-25')
> SELECT user_id, url, source_ip WHERE dt = '2009-02-25';
#如果没有使用hive.enforce.bucketing,那么需要设置分桶个数相匹配的reducer个数,如:set mapred.reduce.tasks = 96,然后在SLELECT语句后添加CLUSTER BY语句.
警告:
对于所有表的元数据,指定分桶并不能保证表可以正确地填充.用户可以根据前面的实例哎确保是否正确地填充了表.
分桶优点:
1.桶的数量固定,没有数据波动.
2.对于抽样很合适.
3.利于执行高效的map-side JOIN.
7.为表增加列
hive> ALTER TABLE weblogs ADD COLUMNS (user_id string);
hive> LOAD DATA LOCAL INPATH 'log2.txt' into weblogs partition(20110102);
#这种方式无法在已有的字段的开始或者中间增加新字段;
8.使用列存储表
Hive通常使用行式存储,不过Hive提供一个列式SerDe来以混合列式格式存储信息.
1.重复数据
一列的数据多是重复数据
2.多列
表具有非常多的字段
查询通常只会使用一个字段或者很少的一组字段,基于列式存储将会使分析表数据执行的更快.
9.(几乎)总是使用压缩
需注意压缩和解压缩都会消耗CPU资源.
9.调优
1.使用EXPLAIN
2.EXPLAIN EXTENDED
3.限制调整
LIMIT在很多情况下,还是需要执行整个查询语句,然后返回部分结果.这通常是浪费的.应尽可能避免,Hive有一个配置属性可以开启,可以对数据源进行抽样:
<property>
<name>hive.limit.optimize.enable</name>
<value>true</value>
<description>....</description>
</property>
<property>
<name>hive.limit.row.max.size/name>
<value>100000</value>
<description>....</description>
</property>
<property>
<name>hive.limit.optimize.limit.file</name>
<value>10</value>
<description>....</description>
</property>
#这个功能的一个缺点就是,有可能输入有用的数据用眼不会被处理到.如像任意一个需要reduce步骤的查询,JOIN和GROUP BY操作,以及聚合函数的大多数调用等等,将会产生很不同的结果.
4.JOIN优化
要清楚要个表的最大的,将最大的表放置在JOIN的最右边.或者直接使用/*streamtable(table_name)*/语句指出
5.本地模式
对输入数据量非常小的时候.
本地模式设置步骤
hive> set oldjobtracker = ${hiveconf:mapred.job.tracker};
hive> set mapred.job.tracker = local;
hive> set mapred.tmp.dir = /home/edward/tmp;
hive> SELECT * FROM people WHERE firstname = bob;
...
hive> set mapred.job.tracker = ${oldjobtracker};
hive.exec.mode.local.auto=true
#可设置上面属性值,让Hive在适当时候自动启动这个优化.
#可配置到$HOME/.hiverc中
#也可配置到$HIVE_HOME/conf/hive-site.xml文件中
<property>
<name>hive.exec.mode.local.auto</name>
<value>true</value>
<description>....</description>
</property>
6.并行执行
Hive会将一个查询转化成一个或者多个阶段.这样的阶段可以是MapReduce阶段,抽样阶段,合并阶段,limit阶段,或者其它阶段,默认情况下,Hive一次只会执行一个阶段.不过,某个特定的job可能包含众多的阶段,而这些阶段并非完全相互依赖,也就是说这些阶段可以并行执行,那么可以使整个job的执行时间缩短.
<property>
<name>hive.exec.parallel</name>
<value>true</value>
<description>....</description>
</property>
7.严格模式
<property>
<name>hive.mapred.mode</name>
<value>strict</value>
<description>....</description>
</property>
设置上面属性可以禁止3中类型的查询:
1.分区表
对应分区表,除非where语句中含有分区字段过滤条件来限制数据范围,否则不允许执行.也就是用户不允许扫描所有分区.
2.ORDER BY语句
使用ORDER BY语句的查询,必须使用LIMIT语句,因为ORDER BY为了执行排序过程会将所有的结果数据分发到同一个reducer中进行处理,强制要求用户增加这个LIMIT语句可以防止reducer额外执行很长的时间.
3.限制笛卡尔积查询
JOIN语句后需要跟上ON条件.
而不能JOIN后直接跟上WHERE条件(与普通的关系型数据库不一样).
8.调整mapper和reducer个数
Hive是按照输入量大小来确定reducer个数的.可通过dfs -count来计算输入量大小(和linux的du -s命令类似).
[root ~ ] $ hadoop dfs -count /user/mediaa/fracture/ins/* | tail -4
hive.exec.reducers.bytes.per.reducer 默认值是1GB
hive> set hive.exec.reducers.bytes.per.reducer = 750000000;
#设置上面值可更改reducer个数
mapred.reduce.tasks #设置reducer个数
#在性能测试的时候,要考虑map和reduce任务的启动和调度的时间这些影响因子,特别是job比较小的时候.
#控制资源利用情况,设置最大reducer数非常重要
hive.exec.reducers.max = N;
#计算公式:
#(集群总Reduce槽位个数*1.5)/(执行中的查询平均个数);1.5是经验倍数.
#一个Hadoop可以提供的map和reduce资源个数(也称为插槽)是固定的.
9.JVM重用
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避难小文件的场景或task特别多的场景,这类场景大多数执行时间都很短.
JVM重用可以使用JVM实例在同一个job中重新使用N次.N的值可以在Hadoop的mapred-site.xml文件中进行设置($HADOOP_HOME/conf);
<property>
<name>mapred.job.reuse.jvm.num.tasks</name>
<value>10</value>
<description>....</description>
</property>
#缺点:开启JVM重用将会一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放.如果某个不平衡的job中有某几个reduce task执行的时间比其它reduce task消耗的时间多得多的话,保留的插槽就会一直空闲着却无法被其它的job使用,知道所有的task都结束才会释放.
10.索引
索引可以用来加快含GROUP BY语句的查询的计算速度.
v0.8.0版本后增加了bitmap索引实现.一般在指定的列排重后的值比较小时进行使用.
11.动态分区调整
Hive可以通过配置限制 动态分区插入允许创建的分区数在1000个左右.将这个值设置更大以便这些查询执行.虽然太多的分区并不好.
<property>
<name>hive.exec.dynamic.partition.mode</name>
<value>strict</value>
<description>....</description>
</property>
#当开启严格模式的时候,必须保证至少有一个分区是静态的.
#配置下面属性来限制查询可以创建的最大动态分区个数
<property>
<name>hive.exec.max.dynamic.partitions</name>
<value>300000</value>
<description>....</description>
</property>
<property>
<name>hive.exec.max.dynamic.partitions.pernode</name>
<value>10000</value>
<description>....</description>
</property>
#下面配置是控制DataNode上一次可以打开的文件个数,这个参数必须设置在DataNode的$HADOOP_HOME/conf/hdfs-site.xml配置文件中.在v0.20.0版本中,该属性默认值是256,太小了.这个值会影响最大的线程数和资源数,因此也不宜设置为极大值.更改该属性值需重启DataNode才能生效.
<property>
<name>dfs.datanode.max.xcievers</name>
<value>8192</value>
</property>
12.推测执行
推测执行是Hadoop中的一个功能,其可以触发执行一些重复的任务(task).尽管这样会因对重复的数据进行计算而导致消耗更多的计算资源,不过这个功能的目的是通过加快获取单个task的结果以及进行侦测将执行慢的TaskTracker加入到黑名单的方式来提高整体的执行效率.
在$HADOOP_HOME/conf/mapred-site.xml文件中如下2个配置:
#分别配置map和reduce
<property>
<name>mapred.map.tasks.sepculative.execution</name>
<value>true</value>
<description>....</description>
</property>
<property>
<name>mapred.reduce.tasks.sepculative.execution</name>
<value>true</value>
<description>....</description>
</property>
#Hive本身也提供了配置项来控制reduce-side的推测执行:
<property>
<name>hive.mapred.reduce.tasks.sepculative.execution</name>
<value>true</value>
<description>....</description>
</property>
#如果用户对于运行时的偏差非常敏感的话,可以将这些功能关闭掉.
#如果用户因为输入数据量很大而需要执行长时间的map或reduce task的话,那么启动推测执行#造成的浪费是非常巨大的.
13.单个MapReduce中多个GROUP BY
另一种特别的优化试图将查询中的多个GROUP BY操作组装到单个MapReduce任务中.如果想启动这个优化,需要一组常用的GROUP BY键:
<property>
<name>hive.multigroupby.singlemr</name>
<value>false</value>
<description>....</description>
</property>
14.虚拟列
Hive提供了两种虚拟列:
一种用于将要进行划分的输入文件名.
另一种用于文件中块内偏移量.
hive> set hive.exec.roeoffset = true;
hive> SLECT INPUT_FILE_NAME, BLOCK_OFFSET_INSIDE_FILE,line FROM hive_text
> WHERE line LIKE '%hive%' LIMIT 2;
第三种虚拟列提供了文件的行偏移量
<property>
<name>hive.exec.rowoffset</name>
<value>true</value>
<description>....</description>
</property>
hive> SLECT INPUT_FILE_NAME, BLOCK_OFFSET_INSIDE_FILE,
> ROW_OFFSET_INSIDE_BLOCK
> FROM hive_text WHERE line LIKE '%hive%' LIMIT 2;
10.其他文件格式和压缩方法
Hadoop的job通常是I/O密集型而不是CPU密集型,这样,压缩可以提高性能.
如果job是CPU密集型的话,那么压缩会降低执行性能.
因为压缩和解压缩会增加额外的CPU开销.
1.确实能够安装编码器
Hive可通过set命令查看Hive配置文件或Hadoop配置文件中配置的值.(io.compression.codec);
# hive -e "set io.compression.codecs"
2.选择一种压缩编/解码器
Hive内置GZIP,BZip2压缩方案,新增Snappy压缩.
如果Hadoop版本不支持,自行增加相关库即可.
另外还有一种常用的压缩方案,LZO压缩.
性能比较:
BZip2:压缩率最高,但同时需要消耗最多的CPU开销.GZip:是压缩率和压缩/解压缩速度上的下一个选择.如果磁盘空间利用率和I/O开销都需要考虑的话,这2中压缩方案是有吸引力的.
LZO和Snappy压缩率相比前面的2中要小,但是压缩/解压缩速度更快,特别是解压缩过程.如果相对于磁盘空间和I/O开销,频繁的读取数据所需的解压缩速度更重要的话,那么这两个是不错的选择.
另一个考虑因素是压缩格式的文件是否是可分割的.MapReduce需要将大的输入文件分割成多个划分(通常一个文件块对应一个划分,64MB的倍数).但是Gzip和Snappy将这些边界信息掩盖了.不过,BZip1和LZO提供了快(BLOCK)级别压缩,也就是每个块中都含有完整的记录信息.
需要注意的是,如果有一个不可分割的文件特别的大,那么就会出现一个单独的task来读取整个文件,进行处理.
3.开启中间压缩
<property>
<name>hive.exec.compress.intermediate</name>
<value>true</value>
<description>....</description>
</property>
#对于Hadoop job来说控制中间数据压缩属性是:mapred.compress.map.output
#Hadoop默认编/解码器是DefaultCodec,可修改属性mapred.map.output.compression.codec值来修改编.解码器.可在mapred-site.xml或hive-site.xml文件中配置.
#SnappyCodec是一个比较好的中间文件压缩编/解码器
<property>
<name>mapred.map.output.compression.codec</name>
<value>org.apache.io.compress.SnappyCodec</value>
<description>....</description>
</property>
4.最终输出结果压缩
<property>
<name>hive.exec.compress.output</name>
<value>true</value>
<description>....</description>
</property>
#对于其他Hadoop任务,开启最终输出结果压缩功能属性是mapred.output.compress
#编解码器设置,GZip压缩的文件对于后面的MapReduce job而言是不可分割的.
<property>
<name>mapred.map.output.compression.codec</name>
<value>org.apache.io.compress.GzipCodec</value>
<description>....</description>
</property>
5.sequence file存储格式
CREATE TABLE a_sequence_file_table STORED AS SEQUENCEFILE;
<property>
<name>mapred.map.output.compression.type</name>
<value>BLOCK</value>
<description>....</description>
</property>
6.使用压缩实践
CLI中通过set命令设置的属性在同一个会话中会一直生效.
7.存档分区
Hadoop中有一种存储格式名为HAR,也就是Hadoop Archive的简写.HAR文件查询效率不高,也并非是压缩的,因此不会节约存储空间.可以减轻NameNode的压力.
8.压缩:包扎
11.开发
UDF,序列化/反序列化器,输入或输出文件格式或者其它一些增强功能.
1.修改Log4J属性
$ bin/hive -hiveconf hive.root.logger = DEBUG,console
2.连接Java调试器到Hive
$ bin/hibe --help --debug
3.从源码编译Hive
12.函数
1.发现函数
hive>SHOW FUNCTIONS;
#查看函数信息
hive> describe function concat; #cocat:函数名
hive> describe function extended concat;
2.调用函数
SELECT concat(col1,col2) AS x FROM table;
3.标准函数
round()/floor()/abs()/ucase()/reverse()/concat()
#UDF函数同样可以返回复杂的对象,例如array,map或struct;
4.聚合函数
sum()/min()/max()/avg()/year()
5.表生成函数
表生成函数接受零个或多个输入,然后产生多列或多行输出.
如array函数,将一列转换成一个数组输出:
hive> SELECT array(1,2,3) FROM dual;
[1,2,3]
explode()函数以array类型数据作为输入,然后对数组中的数据进行迭代,返回多行结果,一行一个数组元素值:
hive> SELECT explode(array(1,2,3)) AS element FROM src;
1
2
3
但,Hive只允许表生成函数以特定的方式使用.例如,一个 显著的限制就是,我们无法从表中产生其它的列.
explode函数错误使用方式:
hive> SELECT name, explode(subordinates) FROM employees; #会报错
不过,Hive提供了一个LATERAL VIEW功能实现这种查询
hive> SELECT name, sub
> FROM employees
> LATERAL VIEW explode(subordinates) subView AS sub;
#通过LATERAL VIEW可以将explode这个UDTF得到的行转列的结果集在一起提供服务.使用LATERAL VIEW需要指定视图别名和生成的新列的别名,对于 本例分别是subView和sub.
7.一个通过日期计算其星座的UDF
继承UDF实现evaluate()函数.
编译,打JAR包,并加入到类路径下,载通过CREATE FUNCTIN语句定义好使用这个Java类函数
hive> ADD JAR /full/path/to/zodiac.jar;
hive> CREATE TEMPORARY FUNCTION zodiac
> AS 'org.apache.hadoop.hive.contrib.udf.example.UDFZodiacSign';
# TEMPORARY,当前会话中声明的函数只会在当前会话有效.
# 如果频繁使用,可以将语句增加到$HOME/.hiverc文件中去.
#DROP TEMPORARY FUNCTION IF EXISTS zodiac;
7.UDF与GenericUDF
GenericUDF是更为复杂的抽象概念,支持更好的null值处理,同时可以处理一些标准的UDF无法支持的编程操作.
8.不变函数
用户可以将自己的函数永久的加入 到Hive中,不过这需要对Hive的Java文件进行简单的修改,然后重新编译Hive.
对于HIve源代码,需对ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java
对FunctionRegistry.java类进行一行代码修改.然后按照Hive源码分支的编译方式,对Hive源码重新编译即可.只需替换hive-exec-*.jar这个JAR文件即可(*表示版本号)
...
registerUDF("parse_url",UDFParseUrl.class,false);
registerGenericUDF("nvl",GenericUDFNvl.class);
...
9.用户自定义聚合函数
10.用户自定义表生成函数
10.1.可以产生多行数据的UDTF
继承的是GenericUDTF接口
10.2.可以产生具有多个字段的单行数据的UDTF
hive> SELECT parse_url_tuple(weblogs.url,'HOST','PATH')
> AS (host, path) FROM weblogs;
10.3.可以模拟复杂数据 类型的UDTF
15.Hive的Thrift服务
1.启动Thrift Server
$ cd $HIVE_HOME
$ bin/hive --service hiveserve & Starting Hive Thrift Server
#检查HiveServer是否启动成功
$ netstat -nl | grep 10000
网友评论