目录
5.1 数据完整性
checksum: 校验和,当数据第一次进入系统时计算校验和,后面再计算一次,如果前后不一致,就认为数据损坏
CRC-32: 32位循环冗余校验,任何数据都可以计算得到一个32位的整数校验和,checkSumFileSystem是用这个的。
CRC-32C: HDFS用于校验和计算的是这个
5.1.1 hdfs数据完整性
对所有数据 写入的时候计算 校验和, 读取的时候验证。
默认是每512个字节就计算一个4个字节的检验和,可以配置 dfs.bytes-per-checksum来配置这个大小
数据写入时: 客户端把数据和检验和发送至管线中,管线的最后一个dn负责验证校验和,如果不对,则抛一个异常。
数据读取时:客户端会计算校验和,并且和存在DN中的作比较,如果校验成功,客户端还会告知NN.
每个DN自己校验:有个进程叫DataBlockScaner,会定期做校验。
损坏数据处理: NN知道这个副本有误之后,就将该副本标记为已损坏,就不再让客户端请求到这个副本,同时可能会把该副本复制到另一个DN。 后面,NN会安排一个新的DN来保存一个新的副本,最终达到副本数量。
使用 open()的时候,可以不做校验和处理
可以使用 fs -checksum 来检查文件的校验和 。 distcp也有类似的功能
5.1.2 LocalFileSystem
LocalFileSystem来执行客户端的校验和计算和验证。
写入fileName的文件时:会在文件块和校验和的文件夹里放一个 .fileName.src的隐藏文件,文件块默认512K,不过可以配置,src文件也保存了文件块的大小。在读文件时,LocalFileSystem会检查检验和,如果不对,则抛异常。
计算校验和开销很小。不过也可以使用RawLocalFileSystem来读,就不做校验和的检查了。
5.1.3 CheckSumFileSystem
这个CheckSumFileSystem继承于FileSystem。 可以这样来使用:
FileSystem c=new CheckSumFileSystem(rawFs).
如果检查到错误,LocalFileSystem会把出错的文件以及校验和放到一个bad目录中
5.2 压缩
压缩的两大好处: 减少存储空间,加速文件在网络和磁盘的传输
要权衡压缩算法的时间和空间: 压缩快的,空间就节省得少。
gzip在空间/时间上 要优于其他算法。
最快的是 snappy 和 lz4,但是他们的空间就多。
切分:是否支持搜索到任意位置,并且往下读取数据
适合mapreduce的: 可切分的算法,只有bzip2。
5.2.1 Codec
1 可以对输入/输出流进行 压缩/解压
2 通过compressionCodeFactory推断CompressCodec
读取压缩文件时,需要通过文件的后缀来识别压缩格式,如 .gz, 就用Gzipcode来读取。
3 原生类库
为了提高性能,最好用原生的类库来 压缩/解压 。
4 CodecPool
可以使用压缩池,减少创建这些对象的开销
5.2.2 压缩和输入分片
在考虑如何压缩将有MapReduce处理的数据时,理解这些压缩格式是否支持切分(splitting)非常重要。
gzip压缩的文件是不支持切分的,因为它使用DEFLATE算法压缩,把数据存储在连续的存储块中,但是没有标识每个块的起始位置,所以读取无法从数据流的任意位置前进到下一个块的起始位置。
MapReduce知道gzip不可切分,通过名称来看。所以他就不切分gzip,所以一个map任务就把所有的数据都计算了。而且,如果作业的粒度越大,执行的时间会更长。
LZO压缩,LZO本来也不支持切分,但是可以使用索引工具来构建切分点索引,就可以实现文件的切分特性
索引效率从高到低排序:
1 使用容器文件格式(5.4.1) AVRO数据文件(12.3), OrcFile(5.4.3) parquet (13.2)这些文件都支持压缩和切分
2 使用支持切分的压缩格式,bzip2(非常慢),或者通过索引实现切分,LZO
3 先将文件切分成块,并且使用任意的压缩格式压缩,需要确保压缩后的块的大小近似于hdfs的块大小
4 不压缩直接存储
5.2.3 在MapReduce中使用压缩
前面也讲到,通过CompressinCodeFactory来判断CompressionCodec时,如果输入文件是压缩的,那么会根据扩展名来解压
也可以压缩Mapreduce作业的输出。这些可以配置,也可以使用FileOutputFormat来配置。
也可以对Map作业的输出进行压缩,这样做可以提升性能
5.3 序列化
序列化是指把结构化的对象转换为字节流以便在网络上传输或者写入磁盘的过程。
反序列化就是反过来。
hadoop中,系统中的多个节点的进程通信是通过RPC来实现的。
RPC协议把消息序列化成二进制流后发送到远程节点。
hadoop使用自己的序列化格式,Writable,紧凑/速度快。hadoop也支持其他序列化框架,avro(12章)
5.3.1 Writable接口
一个整数占了4个字节
5.3.2 Writable类
writable类对所有的jiava基本类型都提供了封装,char类除外
Text是针对UTF-8序列的Writable类,一般认为他等价于string,其最大值是2GB.但是其实两者是有差别的。
Text的api没有String多,可以通过toString来转化为string.
5.3.3 实现定制的writable集合
这个就不写了,
后面还提到了实现定制的 comparator
5.3.4 序列化框架
也可以不用Wriable来实现序列化。
Java 原生的 Serialization不满足列出的序列化格式标准:精简,快速,可扩展性,支持互操作。
hadoop的writable的可扩展性不性。
序列化IDL:不通过代码来定义类型,而是使用“接口定义语言”,以不依赖具体语言的方式进行声明。由此,系统能够为其他语言生成类型,这种形式可以提高互操作能力。
apache Trift 和 Protocol Buffers。
AVRO是基于IDL的序列化框架,非常适合hadoop的大规模数据处理(12章会讲)
5.4 基于文件的数据结构
对基于mapreduce的数据处理,将每个二进制数据大对象(blob)单独放在各自的文件中不能实现可扩展性。所以,hadoop为此开发了很多更高层次的容器。
5.4.1 关于sequenceFile
非常适合日志文件,好长好长。。。
5.4.2 关于MapFile
它是排过序的sequenceFile,他有索引,所以可以按键去查找。
索引是一个sequenceFile,主数据也是一个sequenceFile。
索引可以加载到内存,所以可以快速查找。
MapFile的变种:SetFile ArrayFile BloomMapFile
5.4.3 其他文件格式和面向列的格式
顺序文件和map文件是hadoop最早的文件,实际上由更好的二进制文件格式。
Avro数据文件类似顺序文件,是面向行的,紧凑可切分的。而且它还是可移植的,可以跨越不同的编程语言。
Avro被hadoop生态系统的各个组件广泛支持。
面向列的存储可以使查询跳过那些不必访问的列。减少开销,提高速度。
不过也由弊端:
1 如果一行里由很多字段,还是面向行比较快。
2 由于必须缓存行的切片,因此需要更大的内存来读写
3 顺序文件也有好处,可以一直读取到writer失败后的最后同步点,这个也是flume使用行存储的原因。
hadoop第一个面向列的文件格式是:Hive的RcFile。 不过已经被ORCFile和parquet取代
Parquet是基于google Dremel的通用的列存储文件格式,被hadoop组件广泛支持。
Avro也有一个面向列的文件格式,叫trevni