1、为了HDFS中的保证数据完整性,Hadoop使用Checksum的方法,每io.bytes.per.checksum个字节计算一个CRC-32的CheckSum。默认是512字节,生成4字节的checksum,所以在空间开销上
2、Hadoop/HDFS支持压缩,当数据规模很大的时候,不仅可以节省空间,还可以减少网络I/O等的消耗。
3、在支持的压缩算法中,压缩比:bzip2 > gzip > lzo,速度:lzo > gzip > bzip2。
解码器是使用上述压缩算法的接口,
gzip 对应的codec是 org.apache.hadoop.io.compress.GzipCodec
bzip2 对应的codec是 org.apache.hadoop.io.compress.BZip2Codec
用法:
(1)直接用Codec.createOutputStream()
或者
(2)CompressionCodecFactory.getCodec(Path p),Path的后缀决定了工厂类返回的Codec。
4、在Hadoop中的配置支持的Codec:
io.compression.codecs 决定了采用哪种压缩。
使用Native代码可以大幅提升Hadoop中压缩/解压缩的性能!
见:http://wiki.apache.org/hadoop/NativeHadoop
然而,bzip和lzo都是不支持split的,所以用在hdfs中会非常影响性能,至于bzip2是可以用的。因此,一般都会用SequenceFile(它支持压缩)。
5、在Map/Reduce任务中设置压缩
设置Reduce结果的压缩:
Configuration conf = new COnfiguration(); conf.setBoolean("mapred.output.compress", true); conf.setClass("mapred.output.compression.codec", GzipCodec.class, CompressionCodec.class);
设置map阶段输出为压缩
mapred.compress.map.output -> true mapred.map.output.compression.codec -> GzipCodec.class
但是这些设置压缩的API已经被废除,建议用SequenceFile?
6、序列化,如果需要自定义输出格式,需要自己实现Writable接口,能对性能有很大提升。
7、SequenceFile:实际就是Key-Value对的文件存储格式。
Key是任意的Writable
Value是任意的Writable
我们可以实现将许多小文件转化为SequenceFile,以方便Map/Reduce处理。
实际上,现在Hadoop处理时,都会将数据转为SequenceFile格式,无论是性能还是压缩上的考量。
8、写SequrenceFile
要写SQ文件,关键的是要获得SequrenceFile.Write,重载的方法很多,看一个最基本的:
Writer createWriter(FileSystem fs, Configuration conf, Path name, Class keyClass, Class valClass)
具体key-value写时用append()方法。
fs不解释了; conf是配置,默认空也行; path是存放的位置; keyClass是key的Class, valClass是value的Class。
import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.SequenceFile; import org.apache.hadoop.io.Text; public class SeqWrite { private static final String[] data = { "a,b,c,d,e,f,g", "h,i,j,k,l,m,n", "o,p,q,r,s,t", "u,v,w,x,y,z", "0,1,2,3,4", "5,6,7,8,9," }; // Write an Sequrence File public static void main(String[] args) throws Exception { // Configuration Configuration conf = new Configuration(); // HDFS File Sytem FileSystem fs = FileSystem.get(URI.create("hdfs://localhost:9000"), conf); // Seq File Path Path path = new Path("test.seq"); // Open Seq Writer and write all key-values SequenceFile.Writer writer = null; IntWritable key = new IntWritable(); Text value = new Text(); try { writer = SequenceFile.createWriter(fs, conf, path, key.getClass(), value.getClass()); for (int i = 0; i < 10000; i++) { key.set(i); value.set(SeqWrite.data[i % SeqWrite.data.length]); writer.append(key, value); } } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeStream(writer); } } }
9、读SequenceFile
与写不太相同,直接new SequenceFile.Reader()就可以了,然后反复调用next()方法。
import java.io.IOException; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.SequenceFile; import org.apache.hadoop.io.Writable; import org.apache.hadoop.util.ReflectionUtils; public class SeqRead { public static void main(String[] args) throws Exception { // Configuration Configuration conf = new Configuration(); // HDFS File Sytem FileSystem fs = FileSystem.get(URI.create("hdfs://localhost:9000"), conf); // Seq File Path Path path = new Path("test.seq"); // Read SequenceFile SequenceFile.Reader reader = null; try { // Get Reader reader = new SequenceFile.Reader(fs, path, conf); // Get Key/Value Class Writable key = (Writable) ReflectionUtils.newInstance( reader.getKeyClass(), conf); Writable value = (Writable) ReflectionUtils.newInstance( reader.getValueClass(), conf); // Read each key/value while (reader.next(key, value)) { System.out.println(key + "\t" + value); } } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeStream(reader); } } }
结果(部分):
9989 5,6,7,8,9, 9990 a,b,c,d,e,f,g 9991 h,i,j,k,l,m,n 9992 o,p,q,r,s,t 9993 u,v,w,x,y,z 9994 0,1,2,3,4 9995 5,6,7,8,9, 9996 a,b,c,d,e,f,g 9997 h,i,j,k,l,m,n 9998 o,p,q,r,s,t 9999 u,v,w,x,y,z
至此,我们已经可以读、写SequenceFile了。
10、通过命令行读取seq文件。
其实我们刚才的Read,在Hadoop的fs命令中已经实现啦!
./bin/hadoop fs -text test.seq |head #结果(部分) 0 a,b,c,d,e,f,g 1 h,i,j,k,l,m,n 2 o,p,q,r,s,t 3 u,v,w,x,y,z 4 0,1,2,3,4 5 5,6,7,8,9, 6 a,b,c,d,e,f,g 7 h,i,j,k,l,m,n 8 o,p,q,r,s,t 9 u,v,w,x,y,z
11、SequenceFile提供了SequenceFile.Sorter,用于自定义排序?
12、SequenceFile的压缩。
写:压缩分为Record和Block两种,前者只压缩Value,后者key、value都压缩。
强烈提醒:如果再Seq文件上用压缩,必须安装Hadoop-Native(0.20.203后,默认都配置好了)。
需要注意的是,请使用bin/hadoop来运行!
./bin/hadoop SeqWrite2
import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.SequenceFile; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.compress.GzipCodec; //An Compressed version to wrie Seq File public class SeqWrite2 { private static final String[] data = { "a,b,c,d,e,f,g", "h,i,j,k,l,m,n", "o,p,q,r,s,t", "u,v,w,x,y,z", "0,1,2,3,4", "5,6,7,8,9," }; // Write an Sequrence File public static void main(String[] args) throws Exception { // Configuration Configuration conf = new Configuration(); // HDFS File Sytem FileSystem fs = FileSystem.get(URI.create("hdfs://localhost:9000"), conf); // Seq File Path Path path = new Path("test2.seq"); // Open Seq Writer and write all key-values SequenceFile.Writer writer = null; IntWritable key = new IntWritable(); Text value = new Text(); try { // Get writer with compress writer = SequenceFile.createWriter(fs, conf, path, key.getClass(), value.getClass(), SequenceFile.CompressionType.RECORD, new GzipCodec()); for (int i = 0; i < 10000; i++) { key.set(i); value.set(SeqWrite2.data[i % SeqWrite2.data.length]); writer.append(key, value); } } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeStream(writer); } } }
压缩存储完毕后,是435.68,原来是317.34,比原来还大?
可能是由于value太小了。
此外,还可以Block策略而非Record来压缩。
读取压缩,和原来读取一样,无需设置压缩。
13、MapFile,允许通过key直接访问的SequenceFile,可以理解为java.util.Map。
14、MapFile的用法和SequenceFile基本一致,MapFile就是一个sort的SequenceFile。
注意读MapFile时支持直接用Key来读取。