Hive基础
- Hive 概念
- Hive优缺点
- Hive名词概念
- hive底层执行流程
- hive 与RDBMS传统关系型数据库对比
- hive基本数据类型(原子数据类型)
- hive的集合数据类型
- 类型转换
- DDL语言
- 内部表
- 外部表
- 内部表和外部表互转
- hive常用操作
- 分区表
- 常用内置函数
- 开窗函数(窗口函数)
- RANK
- 常用函数
- 日期函数
- unix_timestamp 返回当前时间或者指定时间的时间戳
- from_unixtime 将时间戳转换为日期模式
- current_date和current_timestamp;
- to_date 将日期+时间格式的数据抽取出日期
- year/month/day/hour/minute/second 获取对应时间的年、月、日、小时、分、秒
- weekofyear/dayofmonth 一年中的第几周 或者是一个月中的第几天
- months_between 两个日期之间的月份
- add_months 日期加减月
- datediff 两个日期相差天数
- date_add 日期加天数(负数实现减)
- date_sub 日期减天数(负数实现加)
- last_day 当月最后一天
- date_format(); 格式化日期
- 常用取整函数
- 字符串函数
- 判断是否空值
- json 解析函数
- 自定义函数
- 存储
- 压缩
Hive 概念
hive是用于解决海量结构化日志的数据统计工具,是基于hadoop的一个数仓工具,可以将结构化数据文件映射成一张表,并提供类SQL查询。
hive本质:将HSQL转化为MR程序。
1、hive数据源存储在本地HDFS
2、hive分析数据底层的实现是MR
3、执行程序在Yarn
Hive优缺点
优点
1、采用类SQL语法,简单,容易上手,提供了快速开发能力。
2、避免了写MR程序,减少开发人员学习成本
3、HIVE执行延迟比较高,常用于数据分析等对实时要求不高的场合。
4、Hive优势在处理大数据,对小规模数据没有优势,
5、Hive支持用户自定义函数,用户可以根据自己的需求实现自己的函数。
缺点
1、Hive sql表达能力有限:1、迭代算法无法实现2、数据挖掘方面不擅长,高效率的算法因为mr数据处理流程的限制无法实现
2、Hive效率低:1、hive自动生成的MR作业,通常情况下不够智能(需要优化)2、Hive调优比较困难,粒度比较粗
Hive名词概念
1、用户接口client:CLLI(Command-line-interface)、JDBC/ODBC(jdbc访问hive),WEBUI(浏览器访问Hive)
2、元数据metastore:包括表名、表所属数据库(默认default)、表拥有者、列/分区字段,表类型(是否分区表),表数据所在目录等。元数据默认存储在自带的derby数据库,推荐用mysql
3、驱动器driver:包括
编译器 compiler:解析器、逻辑计划生成器、解析器 SQL Parser:将sql转化为抽象语法树AST TREE。这一步一般都用第三方工具库完成,比如antlr;对AST进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误。
1、逻辑计划生成器(Physical Plan):将AST编译生成逻辑执行计划。
2、优化器Query Optimizer:对逻辑计划进行优化
3、执行器Execution:对逻辑计划转换为可执行的物理计划。
4、查询分析器semanticAnalyzer:包含了对查询的所有优化操作以及查询分析
hive底层执行流程
1、client与hiveserver2建立连接,创建会话
2、client提交DDL或者查询,转给driver处理
3、driver内部通过parser对语句进行解析,校验语法是否正确,生成AST tree
4、然后通过compiler编译器生成逻辑计划:
1、semanticAnalyzer遍历AST TREE,进一步进行语义分析。然后和hive
metastore通信获取元数据schema信息,抽象成query block。 2、逻辑生成器physical plan 会遍历query
block,翻译成operatortree,这是未优化的逻辑计划。
5、optimizer优化器会对逻辑计划operatorTree进行优化,例如谓词下推,常量值替换,列裁剪等,得到优化后的逻辑计划。
6、semanticAnalyzer对逻辑计划进行处理,通过task compiler生成物理计划taskTree
7、task compiler会对物理计划进行优化,然后根据底层不同引擎进行提交。
hive 与RDBMS传统关系型数据库对比
查询语言
数据库采取了SQL查询,hive采取了类SQL 即HSQL
数据更新
hive是针对数据仓库设计,数据仓库是读多写少,因此hive不建议对数据的改写,所有数据都是加载的时候确定好的。但是数据的数据通常都是经常修改的,经常用update和insert into
执行延迟
hive查询表的时候因为没有索引,所以需要扫描表,所以延迟比较高。除此之外,因为hive是用MR框架去计算,所以延迟很高。数据库在处理小数据延迟很低,但是在大数据量上比不过并行计算的hive。
数据规模
hive是针对大数据,rdbms是针对小数据
hive基本数据类型(原子数据类型)
tinyint 相当java 的byte,长度是1byte有符号整数,例如20
smalint java的short,长度是2byte的有符号整数,例如20
int java的int,4byte的有符号整数,例如20
bigint java的long,8byte的有符号整数,例如20
boolean java的boolean,java的布尔类型, 例如true或者false
float 相当于java的float,单精度浮点,3.1415
double 相当于java的double,双精度浮点,3.1415
string java的string,
timestamp 时间类型
binary 字节数组
hive的集合数据类型
Create table complex(
tickdata0 array<string>,
tickdata MAP<String, string>,
tickdata1 struct<
name: string,
sex: string,
age: int>
STRUCT 描述:例如某列是xx列内容是{"name":"chen","sex":"nan","age":"18"} 可以通过xx.name获取name列
MAP描述:键-值集合,例如xx列内容是MAP{"name":"chen","sex":"nan","age":"18"} 可以通过xx['name']获取name
ARRAY描述:值集合,例如xx列内容是["chen","adasd","aqweqw","aqwe"] 可以通过xx[0]获取chen
类型转换
hive的基本数据类型(原子数据类型)
hive基础数据类型是可以进行隐性转换的,类似java的转换,byte小的可以转成大的,byte大的转小的要用cast。例如某表达式使用int类型,tinyint类型就会转换成int类型。但是int类型转换成tinyint类要用cast转换,例如cast(‘xx’ as int)
隐性转换规则
1、任何整数类型可以转换成一个byte范围更广的整数类型,例如tinyint转换成int
2、所有整数类型、float、string 类型都能转换成double
3、TINYINT、SMALLINT、INT都可以转换为FLOAT。
4、boolean不能转换为任何类型
DDL语言
1、创建数据库(数据存在默认位置) create database database_name
2、创建数据库,指定数据存放在HDFS某个位置 create database if not exists database_name location 'hdfs/dir/xxx.db’
3、删除空数据库: drop database database_name
4、数据库不为空,要用cascade 强制删除命令 drop database db_name cascade
5、创建表
create 【external】 table 【if not exists】 table_name(
col_1 string comment"字段1注释"
,col_2 string ccomment"字段2注释"
) comment "表注释"
partition by(c0_1) --分区表
clustered by (co_1,co_2) --分桶表
sorted by (co_1 asc) --对分桶表的字段排序
row format delimit fields terminated by ',' --列分隔符
collection items terminated by '_', --map struct 和array的分隔符
may keys terminated by '_' --map中的key和value分隔符
line terminated by '\n' --行分隔符
localtion ---数据存储地点
TBLPROPERTIES (property_name=property_value, ...) --表的一些属性
stored as file --以文件形式存储
导入数据 入hive表
load data local inpath ‘xx/xxx/xx/xx’
展示表信息
desc tablename
内部表
默认创建的(没有external)表都是内部表,也叫管理表。这种表的数据存储位置由hive配置项管理hive.metastore.warehouse.dir管理,例如默认是/usr/hive/warehouse。
当我们删除一个内部表的时候,hive也会删除这个表中的数据。
创建普通内部表
create
create table if not exists xxx(
id int , name string
)
row format delimited fields terminated by '\t'
stored as texfile
location '/usr/hive/warehouse/xx'
create table as
create table if not exists xxx as select id ,name from x
create table like (只创建结构)
create table if not exists xxx like student
外部表
外部表,hive只管理外部表的元数据,不管理外部表的数据文件。因此删除外部表的时候,只是删除元数据,不删除数据文件。
#外部表使用场景
每天收集的日志信息或者文件流入HDFS,然后建立外部表读取文件,然后在外部表的基础上做分析或者用外部表做增量处理,数据流入内部表。
外部表创建
create external table xx (
id int, name string
)
row format delimited fields terminated by '\t'
内部表和外部表互转
查看表类型
desc formatted student;
Table Type: MANAGED_TABLE (内部表)
内部表转外部表
alter table student set tblproperties('EXTERNAL' = 'TRUE')
desc formatted student;
Table Type: EXTERNAL_TABLE
外部表转内部表
alter table student set tblproperties('EXTERNAL'='TRUE')
desc formatted student;
Table Type: MANAGED_TABLE (内部表)
hive常用操作
重命名表
alter table table_old_name rename to table_new_name;
增加/修改/替换列信息
1、修改列名、数据类型、列注释、列所在顺序(first在第一列,after在aftercol后一列):
alter table table_name change old_col_name new_col_name column_type comment_col first/after col
例如:
after table test change col1 col2 STRING '修改为信的注释' after col3
2、增加和替换列:ADD COLUMNS允许用户在当前列的末尾,分区列之前添加新的列,REPLACE COLUMNS允许用户更新列,更新的过程是先删除当前所有的列,然后在加入新的列。注:只有在使用native的SerDE时才可以这么做。
alter table table_name add|replace COLUMN(col_name data_type [comment],...)
例如:
alter table tesvt add column (col_new STRING comment '添加新列')
alter table test replace column(eid INT,empid INT ,ename String,name String )
表中载入数据
1、load data [local] inpath 'xx/x/x/xx/x'[overwrite] into table student [partition(col = xxx)]
load data:表示加载数据
load data:表示加载数据
inpath:表示加载数据的路径
inpath:表示加载数据的路径
into table:表示加载到哪张表
student:表示具体的表
partition:表示上传到指定分区
2、insert into table table_name value ('1','23231'),('2','xxx')
3、insert into/overwrite table table_name【partition (col = xx)】 select * from xx
4、create table if not exists table_name as select * from xx
导入导出数据
1、导入数据
import table table_name from 'xx/x//x//x/xx'
2、查询结果导出本地
insert overwrite local directory 'xx/xx/xx' select * from table_name;
2、查询结果结构化导出本地
insert overwrite local directory 'xx/xx/xx' row format delimited fields terminated by '\t' select * from table_name
3、查询结果导出HDFS
insert overwrite directory 'xx/xx/xx' select * from table_name;
4、Hadoop 命令导出本地表文件
dfs -get //user/hive/warehouse/student/student.txt /opt/module/datas/export/student3.txt;
5、Hive shell 导出本地查询结果
hive -e 'select * from table_name;' > /x/xx/xxx/xx.txt
6、export 导表到HDFS
export table default.studnet to 'usr/xxxx/xx/xx/'
truncate
内部表删除数据和元数据,外部表删除元数据不能删除数据
基础查询
略
分区 (distribute by)
Distribute By: 在有些情况下,我们需要控制某个特定行应该到哪个reducer,通常是为了进行后续的聚集操作distribute by 子句可以做这件事。distribute by类似MR中partition(自定义分区),进行分区,结合sort by使用
对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
例如:先按照部门编号分区,再按照员工编号降序排序。
set mapreduce.job.reduces=3;
insert overwrite local directory '/opt/module/hive/datas/distribute-result' select * from emp distribute by deptno sort by empno desc;
distribute by的分区规则是根据分区字段的hash码与reduce的个数进行模除后,余数相同的分到一个区。
Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前
cluster by
当disribute by 和sort by 相同时候,可以用cluster by 。但是cluster by 排序只能是升序,不能指定为降序。
select * from emp cluster by deptno;=select * from emp distribute by deptno sort by deptno;
分区表
分区表就是对应HDFS上文件系统的一个文件夹,该文件夹就是该分区所有的数据文件。hive中分区就是HDFS该表数据所在的文件夹在的一个子目录。把一个大的数据集分割成多个小的数据集。查询的时候通过where指定分区,查询效率会提升很多。
创建分区表
create table if not exists table_name (
id INT, name STRING)
partitioned by (day STRING)
row format delimited fields terminated by '\t'
加载数据到分区表某分区
load data inpath 'xx/xx/xxx/xx.log' into table table_name partition(partition_col = 'xx')
查询分区表数据
select * from A where dt = '20220728'
分区表增加/删除分区
alter table table_name add/drop partition(partition_col = 'xx')(多个分区就空格再接partition(partition_col = 'xx'))
查看分区数
show partitions table_name
查看分区表结构
desc formatted table_name
二级分区
二级分区建表
create table if not exists table_name (
id INT, name STRING)
partitioned by (day STRING,hour STRING)
row format delimited fields terminated by '\t'
加载数据和查询数据
参考单分区表
动态分区
开启参数设置后,会根据设定好的分区字段自动取值分区。
开启动态分区
hive.exec.dynamic.partition = true ;
设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)
hive.exec.dynamic.partition.mode=nonstrict
在所有执行MR的机器上设置最大动态分区数,默认1000
hive.exec.max.dynamic.partitions = 1000;
在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。
hive.exec.max.dynamic.partitions.pernode=100
整个MR Job中,最大可以创建多少个HDFS文件。默认100000
hive.exec.max.created.files=100000
当有空分区生成时,是否抛出异常。一般不需要设置。默认false
hive.error.on.empty.partition=false
分桶表
分桶表是让某一列数据,让这一列数据按照哈希取模的方式进行随机、均匀的发送到各个同文件中。
抽样查询
tablesample(bucket x out of y on col_name)
将某列分成y个桶,然后选第x个桶
select * from stu_buck tablesample(bucket 1 out of 4 on id);
随机抽取20个
select * from table_name order by rand() limit 20 ;
常用内置函数
NVL
判断字段是否为null,为null则用另一个值代替。
select comm, nvl(comm,mgr) from emp;
case when … then … else … end
判断字段逻辑
um(case sex when '男' then 1 else 0 end) male_count
行转列
concat(col/string,col/string) 将两个字符串或者字段连接。例如concat(id,'-',name)
concat_ws(separator, str1, str2,...) separator 是分隔符,用分隔符将str1,str2...连接起来。str1,str2...也可以是array<string>
concat_set(col) 将某一列的值去重,然后汇总为一个array<string/int/xx>类型
孙悟空 白羊座 A
大海 射手座 A
宋宋 白羊座 B
猪八戒 白羊座 A
凤姐 射手座 A
苍老师 白羊座 B
||
射手座,A 大海|凤姐
白羊座,A 孙悟空|猪八戒
白羊座,B 宋宋|苍老师
SELECT t1.c_b , CONCAT_WS("|",collect_set(t1.name))
FROM (
SELECT NAME ,CONCAT_WS(',',constellation,blood_type) c_b
FROM person_info
)t1
GROUP BY t1.c_b
列转行
explode(col) :将hive中复杂的array<string>或者map分成多行。
lateral view:用于和split, explode等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。
例如:
[atguigu@hadoop102 datas]$ vi movie_info.txt
《疑犯追踪》 悬疑,动作,科幻,剧情
《Lie to me》 悬疑,警匪,动作,心理,剧情
《战狼2》 战争,动作,灾难
||
《疑犯追踪》 悬疑
《疑犯追踪》 动作
《疑犯追踪》 科幻
《疑犯追踪》 剧情
《Lie to me》 悬疑
《Lie to me》 警匪
《Lie to me》 动作
《Lie to me》 心理
《Lie to me》 剧情
《战狼2》 战争
《战狼2》 动作
《战狼2》 灾难
select movie,category_name
from movie_info
lateral view explode (split(category,',')) movie_info_tmp as category_name;
开窗函数(窗口函数)
over():指定分析函数的数据窗口大小,这个数据窗口大小可能随着行的变化而变化。
current row : 当前行
n preceding :往前n行
n following: 往后n行
unbounded : 起点
unbounded preceding : 表示 从前面的起点
unbounded following : 标识 到后面的终点
LAG(col,n,default_value) 往前第n行数据
LEAD(col,n,default_value) 往后第n行数据
NTILE(n):把有序窗口的行发送到指定的数据组中,各个组有编号,从1开始。NTILE 返回此行所属的组的编号 。 (分成n组,组号是从1到..n,返回编号)
rows:根据排序后的行数处理 ,例如rows between 100 preceding and 200 表示取 前100行和后100行
range:根据排序后的值 处理,例如 partition by name order ny orderdate range between 100 preceding and 200 表示按orderdate排序后,取大于且等于orderdate-100到小于等于orderdate+200的数。
聚合函数sum,max,min + over()
select name,orderate,cost,
sum(cost) over(order by orderdate), -- 从第一行的cost累加到当前行的cost
sum(cost) over(partition by name) -- 按name 分组,然后组内全部行sum
sum(cost) over(partition by name order by orderdate), -- 按name分组,组内按 orderdate排序,然后组内全部sum
sum(cost) over(partition by name order ny orderdate rows between unbounded preceding and current row ) -- 按name分组,组内按 orderdate排序,然后取组内第0行(起点)到当前行的cost进行sum相加
sum(cost) over(partition by name order ny orderdate rows between current row and undounded following ) -- 按name分组,组内按 orderdate排序,然后取当前行到最后一行的cost进行sum相加
sum(cost) over(partition by name order ny orderdate range between 100 and current row ) -- 按name分组,组内按 orderdate排序,然后取组内小于当前行orderdate的cost和大于orderdate-100行的cost进行sum相加
sum(cost) over(partition by name order ny orderdate rows between 1 preceding and current row ) -- 按name分组,组内按 orderdate排序,然后取当前行的cost和前一行的cost进行sum相加
sum(cost) over(partition by name order ny orderdate rows between 1 preceding and 1 following ) -- 按name分组,组内按 orderdate排序,然后取当前行的cost和前一行的cost以及后一行的cost进行sum相加
from business;
查看顾客上次购买时间
select name, orderdate, cost,
lag(orderdate,1,'1900-01-01') over (partition by name order by orderdate) as time2
from business;
查询前20%时间的订单信息
select * from(
select name, orderdate, cost,
ntile(5) over(order by orderdate)as nt
from business
)a
where nt=1;
RANK
rank() 排序的时候会重复,总数不会变
DENSE_RANK() 排序的时候会重复,总数会变少
row_number() 排序不会重复
rank() over(partition by subject order by score desc) rp,
dense_rank() over(partition by subject order by score desc) drp,
row_number() over(partition by subject order by score desc) rmp
score rk dr rn
84 1 1 1
84 1 1 2
78 3 2 3
68 4 3 4
常用函数
日期函数
unix_timestamp 返回当前时间或者指定时间的时间戳
select unix_timestamp;
select unix_timestamp('2022-12-18','yyyy-MM-dd');
from_unixtime 将时间戳转换为日期模式
select from_unixtime(1603843200)
select from_unixtime(unix_timestamp('2022-12-18:12:13 : asd','yyyy-MM-dd HH:mm:ss'))
current_date和current_timestamp;
current_date 当前日期 yyyy-MM-dd
current_timestamp 当前日期+时间 yyyy-MM-dd HH:mm:ss
to_date 将日期+时间格式的数据抽取出日期
select to_date('2022-08-02 12:12:12')
year/month/day/hour/minute/second 获取对应时间的年、月、日、小时、分、秒
select year/month/day/hour/minute/second(current_date);
select year/month/day/hour/minute/second(current_timestamp);
weekofyear/dayofmonth 一年中的第几周 或者是一个月中的第几天
select weekofyear/dayofmonth(current_date)
months_between 两个日期之间的月份
select months_between('2022-04-01','2022-10-28');
add_months 日期加减月
select add_months(current_date,-3);
datediff 两个日期相差天数
select datediff('2020-11-04','2020-10-28');
date_add 日期加天数(负数实现减)
select date_add(current_date,-3);
date_sub 日期减天数(负数实现加)
select date_sub(current_date,3);
last_day 当月最后一天
select last_day(current_date);
date_format(); 格式化日期
select date_format(current_date,'yyyydMM/dd HH:mm:ss');
常用取整函数
round 四舍五入
select round(3.54);
ceil 向上取整 只要有小数就加1
select ceil(3.14);
select ceil(3.54);
floor 向下取整,不计算小数,只取整数部分
select floor(3.14);
select floor(3.54)
字符串函数
upper/lower 大小写
select upper/lower('xx')
length 长度
select length ('xx')
trim 前后去空格
select trim(' aasd ')
lpad/rpad 向左/右补齐,通过指定字符串补到指定长度
select lpad/rpad('aasd',10,'c')
regexp_replace 使用正则表达式匹配目标字符串,匹配成功后进行替换
SELECT regexp_replace('2020/10/25', '/', '-');
substr() 截取字符串
substr(string str, int start ) 截取 str字符串 坐标start 后的字符串(下坐标是0开始)
substr(string str, int start, int len) 截取 str字符串 坐标start 后的字符串,截取长度为len
concat 拼接字符串
concat('a','-','b')
判断是否空值
nvl(val1,val2) 判断是否空值空
val1字段非空null,则返回val1,如果为空,则返回val2
coalesce(expr,val1,val2,…valN)
判断expr是否为空,如果第一个为空,则返回val1,第二个为空返回val2…第N个空值,返回valN
json 解析函数
json_tupe 解析json
select json_tupe(json,'id','name') as (id,name) from json_test ;
get_json_object()
get_json_object 函数第一个写json对象变量,第二个参数用$表示json变量表示,用.或[]读取对象或者数组
select get_json_object(content,'$.姓名') as name
from json_Test;
自定义函数
UDF: 一进一出(一个参数进来,一个结果返回)
UDAF:多进一出
UDTF:一进多出
UDF流程 (粗略)
1、创建maven项目
2、导入依赖
3、继承UDF类 GenericUDF
4、重写三个方法:1、initialize 初始化,一般判断参数,UDF参数限定为1个 2、执行计算evaluate,这里写计算逻辑。3、返回执行计划顺序getDisplayString,一般是空值“”
5、打包编译
6、添加到hive类路径
7、创建临时函数 create temporary function my_len as “com.atguigu.udf.MyUDF”;
8、测试
具体代码如下:
public class MyUDF extends GenericUDF {
@Override
public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {
if (objectInspectors.length != 1){
throw new UDFArgumentException("参数个数不为1");
}
return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
}
@Override
public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {
String input = deferredObjects[0].get().toString();
if (input ==null){
return 0;
}
return input.length();
}
@Override
public String getDisplayString(String[] strings) {
return "";
}
}
存储
HIVE存储格式:textfile,sequencefile,orc,parquet.
但总的来说,分两类,一类是行存储,一类是列存储,但是他们的逻辑板展示形式是一样的(结果展示一样,底层存储不一样)。
逻辑表:
a b c
a1 b1 c1
a2 b2 c2
a3 b3 c4
行存储逻辑:
a1b1c1 a2b2c2 a3b3c3
列存储逻辑
a1 a2 a3 a4… b1b2b3b4… c1c2c3c4
行存储
使用行存储的一般是texfile和sequencefile 。
行存储特点:
写入数据的时候,一行一行写入保存,查询满足条件的某一行数据的时候(多字段或者整行),只需要找到一个值即可,其他同行的数据就在隔壁。此时查询速度更快。
因为同行数据都在隔壁,所以insert和update操作更快。
列存储
使用列存储的一般是orc和parquet
列存储特点
把每一个字段的数据聚集存储,在查询少数字段的时候,查询速度快。update和insert会很慢。
textfile
默认格式,数据不压缩。磁盘开销大,数据解析开销大,可以结果Gzip、Bzip2等压缩使用。但是使用Gzip等压缩格式,hive不会对数据进行切分,所以无法对数据进行操作。
ORC格式
每个orc由1个或者多个stripe组成,每个stripe一般为hdfs的一个块大小,每个stripe包含多条信息记录,这些记录单独存储,对应parquet中的row group。
每个strip分三部分,index_Data,row_data,stripe footer.
index_Data:轻量级的index。默认每隔1w数据就做一个索引,记录某行的各字段的offset。
row data : 存的是具体数据,先取部分行,再对行按列进行存储,对每个列都进行了编号,分成多个stream进行存储。
stripe footer: 存储的是stream的类型,长度等信息。
同时每个文件都会有一个file footer,这里存的是stripe的行数,每个column的数据类型信息等。
每个文件尾部由一个post stript文件,记录了整个文件的压缩类型以及file footer的长度信息等。
读取文件的时候,先seek到文件尾端读取post stript,里面解析了file footer的长度,再读取file footer,从里面解析各个stripe的信息,再读取stripe,即从后往前读。
parquet
parque是列式存储,以二进制的方式存储为parquet文件,所以是不能直接读取。 该文件包括该文件的数据和元数据,所以parquet是自解析。
parquet文件有三个概念:
1、行组(row group ):每一行组包括一定的行数,一个hdfs文件中至少存储1个行组,类似orc的stripe。
2、列块(column chunk):在一个行组中的一个列块中,行组中的所有列都是连续存储在这个列块的,一个列块中的值都是相同类型的。不同的列块可能使用不同的算法压缩。
3、页(page)每一个列块划分多个页,一页是最小编码,在同一个列块中的不同页可能使用不同的编码方式。page有三种类型:数据页(存储当前行组中该列的值)、字典页(该值的编码字典)、和索引列(该行组下该列的索引)
通常情况下,存储parquet数据的时候会按照hdfs的block大小设置行组的大小,因为一个mapper能处理任务数据的最小是一个block,所以可以把每一个行组交给一个mapped任务处理,增大任务执行并行度。
压缩
压缩算法:分无法切分和可切分的压缩
无法切分的压缩算法:snappy,Gzip