10亿数据量的系统性能优化设计令人叹为观止。其实说起来很简单。比如有一个十亿级数据量的庞大数据文件,可能达到TB级别。这个时候文件真的太大了。这时HDFS客户端会把它拆分成很多块,一个块是128MB。你可以把这个HDFS客户端理解为一个云盘系统、日志采集系统等,比如有人上传一个1TB的大文件到网盘,或者上传一个1TB的大日志文件。然后HDFS客户端将block一个一个的上传到第一个DataNode,第一个DataNode将block复制一份,复制一份发送给第二个DataNode,然后第二个DataNode再将block的一个副本发送给第三个DataNodeDataNodes。所以你会发现一个块有3个副本,分布在三台机器上。如果任何一台机器出现故障,数据都不会丢失。然后将一个TB级别的大文件拆解成N多个MB级别的小文件存储在很多机器上。这不是分布式存储吗?今天要讨论的问题是当HDFS客户端上传一个TB级别的大文件时,它是如何上传的?如果用更原始的方式上传,我们大概可以想到下图中的样子。其实很简单。无非就是不断地使用输入流从本地磁盘文件中读取数据,然后读取一点,立即通过网络的输出流写入到DataNode中。参考上图,一个文件的输入流至多是一个FileInputStream,一个DataNode的输出流至多是一个Socket返回的OutputStream,然后在中间找一个内存小的byte[]数组执行流比较。从本地文件中读取一些数据,并向DataNode发送一些数据。但如果是这样的话,性能会极低。网络通讯要注意适当的频率。每次批量发送,都要读取大量数据,通过网络通信发送一批数据。如果不能读到一点点数据,就马上有网络通信,把这一点点数据发出去。如果按照上面原来的方法,网络通信效率会极低,上传大文件的性能也很差。相当于你可能刚刚读取了几百字节的数据,马上写入网络,比如几百毫秒,然后读取下一批几百字节的数据,然后写入网络等待几百毫秒,这个性能很差,在工业规模的大型分布式系统中几乎是无法忍受的。如何优化Hadoop中大文件上传的性能?让我们看看下面的图片。需要为本地TB级磁盘文件创建一个输入流,然后在读取数据后立即写入HDFS提供的FSDataOutputStream输出流。这个FSDataOutputStream输出流在做什么?他会天真地通过网络传输立即将数据写入DataNode吗?答案当然是否定的!如果你这样做,它会和以前一样!1.Chunkbuffer机制首先,数据会被写入到一个chunkbuffer数组中。这个块是一个512字节的数据片段。你可以这样理解。那么这个缓冲数组可以容纳多个chunk大小的数据在里面缓冲。就这个buffer,首先让client可以快速写入数据,没必要为了几百个字节就进行一次网络传输吧?2.数据包数据包机制接下来,当chunkbuffer数组满时,chunkbuffer数组会被一个一个的切割成chunk,一个chunk就是一个数据段。那么多个chunk会一次性直接写入到另一个内存缓冲区数据结构中,这就是Packet数据包。一个Packet数据包旨在容纳127个块,其大小约为64mb。因此,大量的chunk会被源源不断的写入到Packet数据包的内存缓冲区中。通过Packet数据包机制的设计,可以在内存中容纳大量的数据,进一步避免频繁的网络传输影响性能。3.内存队列异步发送机制当一个Packet被chunk填满后,会将该Packet放入一个内存队列中进行排队,然后一个DataStreamer线程会不断的获取队列中的Packet数据包并通过网络进行传输写一个数据包直接发给DataNode。如果一个Block默认是128mb,那么一个Block默认会对应两个Packet数据包,每个Packet数据包是64MB。也就是说,DataNode发送完两个Packet数据包后,会发送一个通知,说一个Block的数据已经发送完毕,然后DataNode就知道自己收到了一个Block,其中包含了别人发送的两个Packet数据包。总结:ok,看了上图和hadoop采用的大文件上传机制,是不是觉得设计得很巧妙呢?工业级的大型分布式系统不会采用特别简单的代码和模式,所以性能会很低。并发优化、网络IO优化、内存优化、磁盘读写优化的架构设计和生产计划有很多。所以大家观察上图,hdfs客户端可以快速读取TB级大文件的数据,然后快速将hdfs的输出流交给写入内存,基于内存中的chunkbuffer机制,打包数据包机制,内存队列的异步发送机制,绝对不会造成网络传输卡顿,拖慢大文件的上传速度。相反,通过上述几种机制,可以大大提高TB级大文件的上传性能。
