Hadoop权威指南第四章学习笔记(超详细)

                                                    第四章  Hadoop的I/O操作

4.1 数据的完整性
当数据量达到hadoop处理的极限时,可能会破坏数据的完整性。
检测数据完整性的常见措施:
匹配校验和(第一次引入系统时计算的校验和 是否等于 通过不可靠通道时计算出来的校验和,常用的校验方式是循环冗余校验),匹配就认为未损坏,反之则损坏

4.1.1 HDFS的数据完整性
HDFS对写入的数据都计算校验和,在读取的时候会验证校验和。(存储校验和的额外开销为1%)datanode在收到其他客户端的数据或者复制数据时会存储数据及其校验和,此外还会保存一份验证校验和的日志(写数据的客户端将其数据和校验和发送到datanode的管线,管线中的最后一个datanode负责验证校验和)。
客户端从datanode取数据时也会验证校验和,与他们在datanode中存储的校验和比较。
除此之外,每个datanode还会运行一个DataBlockScanner的后台线程,定期验证datanode中数据的校验和,这是防止物理存储破坏影响数据完整性的有效措施(扫描之后会产生一份扫描报告)。
当数据块损坏之后可以通过他的复本来修复。修复过程:
(1)检测到错误时,向datanode报告损坏的数据块和正在尝试读操作的datanode,再抛出CheckSumException异常;
(2)namenode将这个数据块标记为已损坏;
(3)安排这个数据块的一个复本复制到另一个数据块;
(4)删除损坏数据块
防止自动校验和修复:open方法传递false参数,放弃自动校验,使用命令解释器时,带-get或者-ignoreCrc参数或者使用copyToLocal命令

4.1.2 LocalFileSystem
Hadoop的LocalFileSystem执行客户端的校验和验证。在写文件的时候,文件系统会创建一个.crc文件,包含该文件的元数据信息。即使文件块发生了变化,也可以正确的读回数据。在读取文件的时候验证校验和,如果校验和不匹配,那么就抛出异常。
校验和的计算和验证需要的开销很小的,底层文件系统自身支持校验和计算,那么可以禁用。
针对读操作禁用校验和实例:
Configuration conf = new Configuration();
FileSystem fs = new RawLocalFileSystem();
fs.initialize(null,conf);

4.1.3 CheckSumFileSystem
LocalFileSystem通过它来完成任务完成任务,简化了向其它无校验和系统加入校验和的功能。 FileSystem fs = new CheckSumFileSystem();底层文件系统可以通过CheckSumFileSystem的getCkeckSumFileSystem得到CheckSumFileSystem实例,通过getCheckSumFile得到任意文件的校验和和文件的路径。

4.2 压缩
压缩文件的好处:较少存储文件需要的容量,加快文件在网络上的传输。所有的压缩和解压缩的算法都需要权衡事件/空间,通常压缩和解压缩更快那么就只能节省更少的空间。
gzip -1 file 优化压缩速度
gzip -9 file 优化压缩空间
bzip的压缩能力强于gzip,但是压缩速度慢一点,LZO,LZ4,Snappy均优化压缩速度,其速度比gzip快一个数量级。
4.2.1 codec
codec实现了一种压缩-解压缩的算法。Hadoop中,一个对CompressionCodec接口的实现代表一个codec。例如:GzipCodec包装了gzip的压缩和解压缩的算法。Hadoop实现的codec如下:
压缩格式 HadoopCompressionCodec
DEFLATE org.apache.apache.hadoop.io.compress.DefaultCodec
gzip org.apache.apache.hadoop.io.compress.GzipCodec
bzip2 org.apache.apache.hadoop.io.compress.BZip2Codec
LZO com.hadoop.compression.lzo.LzopCodec
LZ4 org.apache.apache.hadoop.io.compress.Lz4Codec
Snappy org.apache.apache.hadoop.io.compress.SnappyCodec
(1)通过CompressionCodec对数据流进行压缩和解压缩
CompressionOutputStream createOutputStream(OutputStream out)
CompressionInputStream createInputStream(InputStream in)
范例4.1-用API来压缩从标准输入中读取的数据并将其写入到标准输出

public class StreamCompressor{
    public static void main(String[] args) {
        String codecClassName = args[0];
        Class<?> codecClass = Class.forName(codecClassName);
        Configuration conf = new Configuration();
        CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf);

        CompressionOutputStram out = codec.createOutputStream(System.out);
        IOUtils.copyBytes(System.in, out, 4096, false);
        out.finish();
    }
}
执行命令:%echo "Text" | hadoop StreamCompressor org.apache.hadoop.io.compress.GzipCodec\ | gunzip Text
(2)通过CompressionCodecFactory推断CompressionCodec

范例4.2-根据文件扩展名选取codec解压缩文件

public class FileDecompressor {
    public static void main(String[] args) {
        String uri = args[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);

        Path inputPath = new Path(uri);
        CompressionCodecFactory factory = new CompressionFactory(conf);
        CompressionCodec codec = factory.getCodec(inputPath);
        if (codec == null) {
            System.err.println("No codec found for "+uri);
            system.exit(1);
        }
        String outputUri = CompressionCodecFactory.removeSuffix(uri, codec.getDefaultExtension());
        InputStream in = null;
        OutputStream out = null;
        try{
            in = codec.createInputStream(fs.open(inputPath));
            out = fs.create(new Path(outputUri));//读取时会自动解压缩
            IOUtils.copyBytes(in, out, conf);
        }finally{
            IOUtils.closeStream(in);
            IOUtils.closeStream(out);
        }
    }
}
//执行命令:%hadoop FileDecompressor file.gz

(3)原生类库
使用原生(native)类库压缩和解压缩可以提高性能。默认情况下应用会根据平台自动搜索原生代码库,但有时调试需要禁用(hadoop.native.lib=false)来保证java代码库的有效性。
(4)CodecPool
应用场景:使用的时袁尚代码库,并且需要大量的压缩和解压缩
范例4.3-使用压缩池对读取自标准输入的数据进行压缩,然后将其写到标准输出

public class PooledStreamCompressor{
    public static void main(String[] args) {
        String codecClassname = args[0];
        Class<?> codecClass = Class.forName(codecClassname);
        Configuration conf = new Configuration();
        CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf);
        Compressor compressor = null;
        try{
            compressor = CodecPool.getCompressor(codec);
            CompressionOutputStream out = codec.createOutputStream(System.out, compressor);
            IOUtils.copyBytes(System.in, out, 4096, false);
            out.finish();
        }finally{
            CodecPool.returnCompressor(compressor);//确保发生异常后也可以将copressor返回池中
        }
    }
}

4.2.2 压缩和输入分片
不支持切分的文件格式的文件是不能从任意位置读取的,当一个文件压缩后作为多个块存储,而每个块作为单独的mapreduce任务,每个mapreduce任务都需要支持从任意位置读取,所以不支持分片的压缩文件格式是会大大降低MapReduce的性能的。
这里写图片描述
4.2.3 在MapReduce中使用压缩
压缩MapReduce作业的输出的配置:
map.output.compress=true
map.ouput.compression.codec=想使用的codec的类名(GzipCodec)
范例4.4-对查找最高气温作业的输出进行压缩

public class  MaxTempratureWithCompression {
    public static void main(String[] args) {
        if (args.length != 2) {
            System.err.println("Usage MaxTempratureWithCompression <input path>"+" <output path>");
            System.exit(-1);//整个程序都退出;System.exit(1)正常退出;System.exit(-1);非正常退出
        }
        Job job = new Job();
        job.setByJarClass(MaxTemprature.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.addOutputPath(job, new Path(args[1]));

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        FileOutputFormat.setCompressOutput(job, true);
        FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);

        job.setMapperClass(MaxTempratureMapper.class);
        job.setCombinerClass(MaxTempratureReducer.class);
        job.setReducerClass(MaxTempratureReducer.class);

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}
//执行命令:%hadoop MaxTempratureWithCompression input/ncdc/sample.txt.gz output

mapred.output.compression.type=RECORD(针对每条记录压缩)/BLOCK(针对每组记录压缩,推荐使用该方式)
在作业中启用map任务输出gzip压缩格式的代码(新API)
Configuration conf = new Configuration();
conf.setBoolean(“mapred.compress.map.output”,true);
conf.setClass(“mapred.map.output.compression.codec”,GzipCodec.class,CompressionCodec.class);
//旧API的实现,和新API的功能相同
// conf.setCompressionMapOutput(true);
// conf.setMapOutputCompressorClass(GzipCodec.class);
Job job = new Job(conf);

4.3 序列化
序列化:将结构化对象转换为字节流以便于在网络上传输或者写到磁盘永久存储的过程。
反序列化:将字节流转换为结构化对象的过程。
Haddop中,多个节点之间的通信通过RPC协议,RPC将消息序列化成二进制的字节流传递到各个节点。RPC序列化的格式如下:紧凑,可扩展,快速,支持互操作。Hadoop使用的是自己的序列化格式Writable,它紧凑,速度块,但不太容易用java外的语言扩展。

4.3.1 Writable接口
该接口中的两个方法
void write(DataOutput out);将其状态写到DataOutput二进制流
void readFields(DataInput in);从DataInput二进制流读取状态(读取和写入的状态是指是否为序列化)
WritableComparable和Comparator
IntWritable implements QritableComparable
WritableComparable extends Writable, Comparable
MapReduce的类型比较接口是RawComparator
public interface RawComparator extends Comparator{
public int compare(byte[] b1, int s1, int l1, byte [] b2, int s2, int l2);//不需要反序列化为对象,直接比较数据流中的记录
}
//得到RawComparator的实例
RowComparator comparator = WritableComparator.get(IntWritable.class);
//比较
IntWritable w1 = new InteWritable(163);
IntWritable w2 = new IntWritable(63);
assertThat(comparator.compare(b1,0,b1.length,b2,0,b2.length),greaterThan(0));

4.3.2 Wriable类
org.apache.hadoop.io包中有许多Writable类可以选择
这里写图片描述
VIntWritable是指可变长度的整形,可表示1~5个字节
如:VItWritable v1 = new VItWritable(127);//v1占4个字节
VItWritable v1 = new VItWritable(128);//v1占5个字节

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值