hive的3种数据存储格式

r囧r小猫 2022-03-16 06:12 546阅读 0赞

hive有textFile,SequenceFile,RCFile三种文件格式。

其中textfile为默认格式,建表时不指定默认为这个格式,导入数据时会直接把数据文件拷贝到hdfs上不进行处理。

SequenceFile,RCFile格式的表不能直接从本地文件导入数据,数据要先导入到textfile格式的表中,然后再从textfile表中用insert导入到SequenceFile,RCFile表中。

写道

create table zone0000tf(ra int, dec int, mag int) row format delimited fields terminated by ‘|’;
create table zone0000rc(ra int, dec int, mag int) row format delimited fields terminated by ‘|’ stored as rcfile;

load data local inpath ‘/home/cq/usnoa/zone0000.asc ‘ into table zone0000tf;
insert overwrite table zone0000rc select * from zone0000tf;(begin a job)

File Format








































 

TextFile

SequenceFIle

RCFFile

Data type

Text Only

Text/Binary

Text/Binary

Internal Storage Order

Row-based

Row-based

Column-based

Compression

File Based

Block Based

Block Based

Splitable

YES

YES

YES

Splitable After Compression

No

YES

YES

2549683.html_type_1_webview_1

源数据放在test1表中,大小 26413896039 Byte。

创建sequencefile 压缩表test2,使用insert overwrite table test2 select …语句将test1数据导入 test2 ,设置配置项:

set hive.exec.compress.output=true;
set mapred.output.compress=true;
set mapred.output.compression.codec=com.hadoop.compression.lzo.LzoCodec;
SET io.seqfile.compression.type=BLOCK;
set io.compression.codecs=com.hadoop.compression.lzo.LzoCodec;

导入耗时:98.528s。另压缩类型使用默认的record,耗时为418.936s。

创建rcfile 表test3 ,同样方式导入test3。

set hive.exec.compress.output=true;
set mapred.output.compress=true;
set mapred.output.compression.codec=com.hadoop.compression.lzo.LzoCodec;
set io.compression.codecs=com.hadoop.compression.lzo.LzoCodec;

导入耗时 253.876s。

以下为其他统计数据对比:

1347875031_7425.jpg






































rows 类型 合并耗时 文件数 总数据大小 count(1) 基于domain、referer求点击的top100
238610458 原始数据 1134 26413896039 66.297s
238610458 seq 98.528(block) 418.936(record) 1134 32252973826 41.578 394.949s(读入数据:32,253,519,280,读入行数:238610458)
238610458 rcfile 253.876 s 15 3765481781 29.318 286.588s(读入数据:1,358,993,读入行数:238610458

因为原始数据中均是小文件,所以合并后文件数大量减少,但是hive实现的seqfile 处理竟然还是原来的数目。rcfile 使用lzo 压缩效果明显,7倍的压缩比率。查询数据中读入数据因为这里这涉及小部分数据,所以rcfile的表读入数据仅是seqfile的4%.而读入行数一致。

SequeceFile是Hadoop API提供的一种二进制文件支持。这种二进制文件直接将对序列化到文件中。一般对小文件可以使用这种文件合并,即将文件名作为key,文件内容作为value序列化到大文件中。这种文件格式有以下好处
1)支持压缩,且可定制为基于Record或Block压缩(Block级压缩性能较优)
2)本地化任务支持:因为文件可以被切分,因此MapReduce任务时数据的本地化情况应该是非常好的。
3)难度低:因为是Hadoop框架提供的API,业务逻辑侧的修改比较简单。
坏处是需要一个合并文件的过程,且合并后的文件将不方便查看。

SequenceFile 是一个由二进制序列化过的key/value的字节流组成的文本存储文件,它可以在map/reduce过程中的input/output 的format时被使用。在map/reduce过程中,map处理文件的临时输出就是使用SequenceFile处理过的。
SequenceFile分别提供了读、写、排序的操作类。
SequenceFile的操作中有三种处理方式:
1) 不压缩数据直接存储。 //enum.NONE
2) 压缩value值不压缩key值存储的存储方式。//enum.RECORD
3)key/value值都压缩的方式存储。//enum.BLOCK

工作中用到了RcFile来存储和读取RcFile格式的文件,记录下。
RcFile是FaceBook开发的一个集行存储和列存储的优点于一身,压缩比更高,读取列更快,它在MapReduce环境中大规模数据处理中扮演着重要的角色。
读取操作:

Java代码 复制代码 收藏代码spinner.gif

  1. job信息:
  2. Job job = new Job();
  3. job.setJarByClass(类.class);
  4. //设定输入文件为RcFile格式
  5. job.setInputFormatClass(RCFileInputFormat.class);
  6. //普通输出
  7. job.setOutputFormatClass(TextOutputFormat.class);
  8. //设置输入路径
  9. RCFileInputFormat.addInputPath(job, new Path(srcpath));
  10. //MultipleInputs.addInputPath(job, new Path(srcpath), RCFileInputFormat.class);
  11. // 输出
  12. TextOutputFormat.setOutputPath(job, new Path(respath));
  13. // 输出key格式
  14. job.setOutputKeyClass(Text.class);
  15. //输出value格式
  16. job.setOutputValueClass(NullWritable.class);
  17. //设置mapper类
  18. job.setMapperClass(ReadTestMapper.class);
  19. //这里没设置reduce,reduce的操作就是读Text类型文件,因为mapper已经给转换了。
  20. code = (job.waitForCompletion(true)) ? 0 : 1;
  21. // mapper 类
  22. pulic class ReadTestMapper extends Mapper {
  23. @Override
  24. protected void map(LongWritable key, BytesRefArrayWritable value, Context context) throws IOException, InterruptedException {
  25. // TODO Auto-generated method stub
  26. Text txt = new Text();
  27. //因为RcFile行存储和列存储,所以每次进来的一行数据,Value是个列簇,遍历,输出。
  28. StringBuffer sb = new StringBuffer();
  29. for (int i = 0; i < value.size(); i++) {
  30. BytesRefWritable v = value.get(i);
  31. txt.set(v.getData(), v.getStart(), v.getLength());
  32. if(i==value.size()-1){
  33. sb.append(txt.toString());
  34. }else{
  35. sb.append(txt.toString()+”\t”);
  36. }
  37. }
  38. context.write(new Text(sb.toString()),NullWritable.get());
  39. }
  40. }

    job信息:
    Job job = new Job();

    1. job.setJarByClass(类.class);

    //设定输入文件为RcFile格式

    1. job.setInputFormatClass(RCFileInputFormat.class);

    //普通输出

    1. job.setOutputFormatClass(TextOutputFormat.class);

    //设置输入路径

    1. RCFileInputFormat.addInputPath(job, new Path(srcpath));
    2. //MultipleInputs.addInputPath(job, new Path(srcpath), RCFileInputFormat.class);

    // 输出

    1. TextOutputFormat.setOutputPath(job, new Path(respath));
    2. // 输出key格式
    3. job.setOutputKeyClass(Text.class);

    //输出value格式

    1. job.setOutputValueClass(NullWritable.class);

    //设置mapper类

    1. job.setMapperClass(ReadTestMapper.class);

    //这里没设置reduce,reduce的操作就是读Text类型文件,因为mapper已经给转换了。

    1. code = (job.waitForCompletion(true)) ? 0 : 1;
  1. // mapper 类
  2. pulic class ReadTestMapper extends Mapper<LongWritable, BytesRefArrayWritable, Text, NullWritable> {
  3. @Override
  4. protected void map(LongWritable key, BytesRefArrayWritable value, Context context) throws IOException, InterruptedException {
  5. // TODO Auto-generated method stub
  6. Text txt = new Text();
  7. //因为RcFile行存储和列存储,所以每次进来的一行数据,Value是个列簇,遍历,输出。
  8. StringBuffer sb = new StringBuffer();
  9. for (int i = 0; i < value.size(); i++) {
  10. BytesRefWritable v = value.get(i);
  11. txt.set(v.getData(), v.getStart(), v.getLength());
  12. if(i==value.size()-1){
  13. sb.append(txt.toString());
  14. }else{
  15. sb.append(txt.toString()+"\t");
  16. }
  17. }
  18. context.write(new Text(sb.toString()),NullWritable.get());
  19. }
  20. }

输出压缩为RcFile格式:

Java代码 复制代码 收藏代码spinner.gif

  1. job信息:
  2. Job job = new Job();
  3. Configuration conf = job.getConfiguration();
  4. //设置每行的列簇数
  5. RCFileOutputFormat.setColumnNumber(conf, 4);
  6. job.setJarByClass(类.class);
  7. FileInputFormat.setInputPaths(job, new Path(srcpath));
  8. RCFileOutputFormat.setOutputPath(job, new Path(respath));
  9. job.setInputFormatClass(TextInputFormat.class);
  10. job.setOutputFormatClass(RCFileOutputFormat.class);
  11. job.setMapOutputKeyClass(LongWritable.class);
  12. job.setMapOutputValueClass(BytesRefArrayWritable.class);
  13. job.setMapperClass(OutPutTestMapper.class);
  14. conf.set(“date”, line.getOptionValue(DATE));
  15. //设置压缩参数
  16. conf.setBoolean(“mapred.output.compress”, true);
  17. conf.set(“mapred.output.compression.codec”, “org.apache.hadoop.io.compress.GzipCodec”);
  18. code = (job.waitForCompletion(true)) ? 0 : 1;
  19. mapper类:
  20. public class OutPutTestMapper extends Mapper {
  21. @Override
  22. public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  23. String line = value.toString();
  24. String day = context.getConfiguration().get(“date”);
  25. if (!line.equals(“”)) {
  26. String[] lines = line.split(“ “, -1);
  27. if (lines.length > 3) {
  28. String time_temp = lines[1];
  29. String times = timeStampDate(time_temp);
  30. String d = times.substring(0, 10);
  31. if (day.equals(d)) {
  32. byte[][] record = {lines[0].getBytes(“UTF-8”), lines[1].getBytes(“UTF-8”),lines[2].getBytes(“UTF-8”), lines[3].getBytes(“UTF-8”)};
  33. BytesRefArrayWritable bytes = new BytesRefArrayWritable(record.length);
  34. for (int i = 0; i < record.length; i++) {
  35. BytesRefWritable cu = new BytesRefWritable(record[i], 0, record[i].length);
  36. bytes.set(i, cu);
  37. }
  38. context.write(key, bytes);
  39. }
  40. }
  41. }
  42. }

SequenceFile提供了若干Writer的构造静态获取。
//SequenceFile.createWriter();

SequenceFile.Reader使用了桥接模式,可以读取SequenceFile.Writer中的任何方式的压缩数据。

三种不同的压缩方式是共用一个数据头,流方式的读取会先读取头字节去判断是哪种方式的压缩,然后根据压缩方式去解压缩并反序列化字节流数据,得到可识别的数据。

流的存储头字节格式:
Header:
*字节头”SEQ”, 后跟一个字节表示版本”SEQ4”,”SEQ6”.//这里有点忘了 不记得是怎么处理的了,回头补上做详细解释
*keyClass name
*valueClass name
*compression boolean型的存储标示压缩值是否转变为keys/values值了
*blockcompression boolean型的存储标示是否全压缩的方式转变为keys/values值了
*compressor 压缩处理的类型,比如我用Gzip压缩的Hadoop提供的是GzipCodec什么的..
*元数据 这个大家可看可不看的

所有的String类型的写操作被封装为Hadoop的IO API,Text类型writeString()搞定。

未压缩的和只压缩values值的方式的字节流头部是类似的:
*Header
*RecordLength记录长度
*key Length key值长度
*key 值
*是否压缩标志 boolean
*values
剩下的大家可看可不看的,并非这个类中主要的。

根据自身涉及到的数据分布和使用需求,对HIVE上的三类文件格式做了如下测试,指导HIVE的文件格式选型。测试存在环境、数据分布、测试偏重点的不同,本测试只供参考,不作为大家选型决策的绝对指导。

HIVE的三种文件格式:TEXTFILE、SEQUENCEFILE、RCFILE中,TEXTFILE和SEQUENCEFILE的存储格式都是基于行存储的,RCFILE是基于行列混合的思想,先按行把数据划分成N个row group,在row group中对每个列分别进行存储。

基于HDFS的行存储具备快速数据加载和动态负载的高适应能力,因为行存储保证了相同记录的所有域都在同一个集群节点。但是它不太满足快速的查询响应时间的要求,因为当查询仅仅针对所有列中的少数几列时,它就不能跳过不需要的列,直接定位到所需列;同时在存储空间利用上,它也存在一些瓶颈,由于数据表中包含不同类型,不同数据值的列,行存储不易获得一个较高的压缩比。RCFILE是基于SEQUENCEFILE实现的列存储格式。除了满足快速数据加载和动态负载高适应的需求外,也解决了SEQUENCEFILE的一些瓶颈。

接下来就针对压缩比、数据加载、查询响应角度对HIVE的三种文件格式进行比较,主要比较对象为SEQUENCEFILE和RCFILE,因为TEXTFILE在压缩后不能发挥MapReduce的并行处理能力,所以此文件格式不会被我们采用。


压缩比

测试数据为1月11号产品表的当前数据,利用此数据保存到三张表,分别采用三种文件格式,压缩方式统一为gzip

存储格式 文件大小

TEXTFILE 21.4GB

SEQUENCEFILE 22.3GB

RCFILE 18.0GB

RCFILE的压缩比优于SEQUENCEFILE。

7098bedfgb8934e802bce_690

![Image 1][]

![Image 1][]

上图是HADOOP的不同压缩方法的一个性能对比图(摘自《pro hadoop》第5章),其中gzip是空间和时间比较折中的压缩方法,后续测试也反应了这一点。


数据加载

7098bedfgb893521c3930_690

![Image 1][]

上图是三类文件格式的数据插入不同压缩类型,不同文件格式的数据加载时间对比图。

1.同一份数据的加载时间为lzo < gzip < bz2和不同压缩方式性能对比图的结果一致。

2.目标表为SEQUENCEFILE的数据加载时间优于TEXTFILE和RCFILE。


查询响应

测试1:

数据表wp_product_target包含62个字段,字段类型包含:BIGINT、DOUBLE、STRING。

执行如下两种SQL查询:

方案一,测试整行记录的查询效率:

select * from wp_product_target where product_id like ‘480523%’;

方案二,测试特定列的查询效率:

select product_id, gmt_create, dw_end_date from wp_product_target where product_id like ‘480523%’;

![Image 1][]

7098bedfgb89357099f93_690

上图反映了RCFILE的查询效率都优于SEQUENCEFILE。

测试2:

数据表wp_product_detail包含8个字段,只取了product_id和description两个字段做测试。product_id的数据占用存储为:447MB,description的数据占用存储为:89697MB。

本测试目的是验证RCFILE的数据读取方式和Lazy解压方式是否有性能优势。数据读取方式只读取元数据和相关的列,节省IO;Lazy解压方式只解压相关的列数据,对不满足where条件的查询数据不进行解压,IO和效率都有优势。

方案一:

insert overwrite local directory ‘/home/dwapp/hugh.wangp’

select product_id, description

from wp_product_detail

where product_id like ‘480523%’;

方案二:

insert overwrite local directory ‘/home/dwapp/hugh.wangp’

select product_id, description

from wp_product_detail

where substr(product_id, 1, 2) = ‘50’;

方案三:

insert overwrite local directory ‘/home/dwapp/hugh.wangp’

select product_id, description

from wp_product_detail_sf;

方案四:

insert overwrite local directory ‘/home/dwapp/hugh.wangp’

select product_id

from wp_product_detail;

前三个方案的查询数据量:800条,700万条,1.8亿条。

7098bedfgb89359a70d7c_690

![Image 1][]

上图反应在大小数据集上,RCFILE的查询效率高于SEQUENCEFILE。

7098bedfgb8935bdcd7ae_690

![Image 1][]

上图反应在特定字段数据读取时,RCFILE的查询效率依然优于SEQUENCEFILE。


总结

1.在压缩比方面,RCFILE有较优的压缩比,因为SEQUENCEFILE是行压缩,行内不同数据值统一压缩,但是RCFILE对每列独立压缩,避免了不同数据值的混合压缩,所以压缩比相对高一些。但是由于对列进行压缩存储,压缩性能比单单对行进行压缩消耗更多。也正体现了压缩的空间和时间的矛盾体。

2.数据加载方面,SEQUENCEFILE优于RCFILE,因为RCFILE的列压缩方式,数据加载压缩时的性能消耗比较大。但是对于数据仓库的一次写入、多次读取,数据的加载性能对整体利用影响没有压缩比和查询响应大。

3.查询响应方面,RCFILE有较优的表现。RCFILE的数据读取方式和Lazy解压方式起到了很大的作用,在读取数据的每个行组时,RCFILE只需读取元数据头部和查询指定的列,而不是行组的全部数据都读到内存中,节省了很多的I/O。虽然读取了元数据头部和查询指定的列,但是不是对这些数据完全解压,Lazy解压技术保证了,只解压满足where条件的数据。

总之,相对于SEQUENCEFILE,RCFILE的压缩比有20%左右的提升,数据加载时间有10%左右的劣势,查询响应时间平均有15%左右的优势,尤其在大数据量的非全字段查询上有30%左右的性能优势。

所以从上面的测试比较可以看出,RCFILE是HIVE最适合的文件存储格式,压缩方式采用GZIP压缩。

业界使用情况,FACEBOOK目前所有HIVE的文件全部采用RCFILE存储,国内的盛大也采用RCFILE存储。

[Image 1]:

发表评论

表情:
评论列表 (有 0 条评论,546人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Hive存储格式 08

    1. Hive的数据存储格式 Hive支持的存储数的格式主要有:TEXTFILE(行式存储) 、SEQUENCEFILE(行式存储)、ORC(列式存储)、PARQUET(