每个chunkserver使用checksumming(校验和)来检测存储数据的损坏.GFS分布式环境往往有数百台机器数千个硬盘,磁盘故障是一个很普通的事情,这往往会导致在读取或者写入时数据的存坏或者丢失.我们能够从其他chunk replicas中恢复这些损坏的数据.但是如果通过跨chunkserver的方式去检测这些损坏的数据,是不现实的.此外,副本数据有些出入或许是正常的:GFS mutation的语义,特别是上述讨论过的原子record append,并不保证replicas完全一致(逐字节).因此,每个chunkserver必须独自来校验自己的副本的数据完整性,使用checksum.
一个chunk(数据块文件)被拆分成64KB的block,每个block都有一个相应的32位checksum.像其他metadata一样,checksums被保存在内存中,通过日志持久存储,和用户实际数据隔离.
对于reads,chunkserver在返回任何数据给请求者(可能是client,也可能是其他chunkserver)之前,会使用checksum校验读取数据所在block,因此chunkserver不会把异常数据交付给其他机器(client或者其他chunkserver).如果block和记录的checksum不匹配,chunkserver将会返回一个错误给client,并且会把这个不匹配信息报告master.收到响应后,请求者(client or other chunkserver)将会从其他的replicas读取,同时master也会从其他chunk replicas复制这个chunk(有可能在其他chunkserver上clone,而非一定在出错的replicas机器上复制).当一个有效的replicas被替换,master会指示chunkserver删除这个mismatch的replicas.
Checksumming有多方面的原因会对read性能有些影响,因为我们的read会分布在多个blocks上,我们需要额外的读取一些相对较小的数据进行校验.Client code通过对齐block边界来更进一步降低开支.此外,checksum查找和比较无需任何IO,并且checksum的计算经常和其他I/O操作同时进行.
Checksum计算做了较大的优化,针对chunk尾部追加的write(而不是覆盖现有数据的write),因为它们(append write)占据了我们了工作量.我们之需要增量的更新最后一个block部分的checksum,(增加block或者block内容变更才会导致checksum的生成和计算),为append操作填充数据的block计算出一个新的checksum.即使最后一个checksum block已经损坏了,我们现在无法检测它,这个新的checksum值不会匹配现存的数据,这个损坏的数据将会在下一次block被读取时才会校验.
相反,如果write是覆盖(overwrite)现有的chunk区间,我们必须读取然后校验覆盖区间内第一个和最后一个block,然后执行write,最后计算和记录这个新的checksums.如果我们在覆盖数据之前,不去校验第一个和最后一个blocks,那么新的checksums可能会隐藏没有被覆盖区域的损坏的数据.
在空闲时期,chunservers能够扫描和校验那写不活跃的chunks的内容.这就允许我们检测chunks中损坏数据,在它读取操作较少的时候.一旦发现损坏数据,master能就会新建一个没有损坏数据的replicas和删除损坏数据的replicas(通过复制),以防止这些非活跃但是损坏的chunk replicas愚弄master认为它有足够多有效的replicas.
Diagnostic Tools(故障诊断工具):
详细的诊断日志对发现问题/调试/性能分析有一定的帮助,它之需要较小的开支.没有日志,对于了解偶然的/不能重现的机器件交互,是很困难.GFS服务器产生诊断日志,记录了许多重要的事件(例如chunkserver的上下线)和所有的RPC请求和响应.这些诊断日志能够在不影响系统正确性前提下,随意的删除.不过,我们会尽量存储这些日志只要空间允许.
PRC日志包括在网络上传输的请求和响应细节,除去读取或者写入的文件数据外.通过匹配和比较不同机器上的PRC请求和响应,我们能够重建整个交互历史过程,以诊断问题.这个logs也能用来各种负载测试和性能分析.
诊断日志的性能影响是微小的(相对收益而言),引文这些日志顺序的写入,而且是异步的.最近的一些事件保存在内存中,可以持续的在线监控.
Measurements:
这在一章节,我们主要展示几个micro-benchmarks来描述GFS架构和实现上的瓶颈.还有一些来自google实际的分布式的数据.
Micro-benchmarks:
我们测量GFS分布式环境的性能:一个master,2个master replicas,16个chunkservers,16个clients.注意这个配置设置是用来测试,通常分布式具有数百个chunkserver和clients.所有的机器都是1.4G双核处理器,2GB内存,2个80 5400rpm硬盘,100M全双工网卡,链接到HP 2524交换机上.全部19台server机器链接到一个交换机上,16个client机器链接到另外一个交换机.这2个交换机之间通过1Gbps链路链接.
Read:
N个client同时从文件系统中读取.每个client从320GB的集合中,读取随即选取的4MB 区域.重复256次,最终每个client读取了1GB数据.chunkserver总共只有32GB内存,所以我们期望10%的命中率在linux buffer cache中.
图3展示了N个客户端整体的读取速率,以及理论上限。整体的上限在2个交换机直接1GB链路上,为125M/S,或者客户端在100M网卡饱和的情况下为12.5M/s(平均每个客户端)。当只有一个客户端读取时,观测到其读取速率为10M/s,为每个客户端上限的80%。16个客户端整体的读取速率可达94M/s,为125M上限的75%。由80%降低到75%,是由于reader的个数增加,所以可能是多个readers同时从同一个chunkservers读取数据造成。
Writes:
N个client同时向N个不同的files中进行写操作,每个client以1M为写入单元,共向一个新文件中写入1GB的数据。其总体写入速率和它的理论上限如图。理论上限为64M/s,因为我们需要把每个字节写入到16个中3个chunkservers上,每个都有12.5M的输入链接。每个client的写入速率为6.3M/s,大约为写入上限的一半,主要原因是我们的网络协议栈。它与我们push data 到chunk replicas所使用的流水线模式(pipleline),不能很好的交互。replicas直接传播数据的延迟,降低了整体的写入速率。写入操作比预期的要慢,事实上,这不是一个主要的问题,即使某个client的延迟增加,在大量client的情况下,也将不会太大的影响整体的写入带宽。
Record appends:
图三展示了record append的性能,N个clients同时向一个文件append数据.性能受到了存储最后一个chunk文件的chunkserver的带宽的限制,和客户端的数量没有关系.单个客户端6.0M/S,在16个客户端是降低到4.8M/S,主要归因与网络阻塞和传输速度的不同.
我们的应用多数情况下是并发的生成多个这样的文件.换句话说,N个clients向M个共享的文件中同时append,N和M都是上百的数字.因此,chunkserver的网络阻塞在实践中不是一个严重的问题.
Experiences(经验):
在构建和部署GFS过程中,我们经历了一些问题,一些操作和技术上的.
起初,GFS被设想为一个为生产系统服务的后台文件系统.随着时间迁移,它发展为包括研究和研发的任务.开始时极少的功能支持例如权限和配额,但是现在全面支持了这些.当生产系统是严格控制的,但是用户曾有时不是如此.需要更多的基础设置(功能)来保持用户之间的隔离(互相干扰).
我们一些最大的问题就是硬盘和linux相关的.许多硬盘生成它们支持IDE协议的一定的版本范围的linux驱动,但是实际上它们只支持最新的(驱动版本).尽管这些协议版本比较接近,驱动也能工作,但是偶尔的不匹配会导致驱动和内核disagree about the drive's state.这会导致内核悄无声息的丢失数据.这个问题会促使我们使用checksum来检测数据的损坏,同时我们也修改内核来处理这种协议上的不匹配.
早期我们出现过一些问题,在linux2.2内核上,fsync()的性能开支.它的开支与文件的大小而不是被修改部分的大小有关.对于我们的大尺寸operation log特别是在实现checkpoiting之前是一个问题.我们在这个问题上徘徊了很久,最后迁移到了linux2.4.
另外一个linux问题是单一reader-writer锁,某个地址空间的线程从disk载入(reader lock,磁盘IO)或者在mmap()调用修改地址空间时,它(线程)必须先hold一个reader-writer lock.我们发现在系统负载很低的时候,也有超时情况出现.我们费劲周折来查找系统平静和硬件问题.最终,我们发现这个single lock,在磁盘线程交换以前的映射数据到磁盘时,会阻塞primary network线程映射新的数据到内存.因为我们主要受限与网络接口,而不是内存复制的带宽(难以理解,什么意思???),我们使用pread()替换mmap(),用额外的copy解决这个问题.
虽然偶尔的问题,linux代码还是帮助了我们理解探索和理解系统的行为.在合适的时机,我们会改进内核和共享我们的这些改动给开源组织.
Related work(相关工作):
和其他大型分布式文件系统一样,GFS提供了一个与位置无关的namespace,它能够让数据根据负载均衡或者容错的需要,透明的迁移.因为磁盘是相对的便宜以及replication比传统的RAID(阵列)方式简单.GFS目前只使用了replication方式进行数据冗余.
CONCLUSION:GFS展示了使用普通的商用硬件提供了大规模数据处理的能力.
GFS论文整理(一):[http://shift-alt-ctrl.iteye.com/blog/1842245]
GFS论文整理(二):[http://shift-alt-ctrl.iteye.com/blog/1842286]
GFS论文整理(三):[http://shift-alt-ctrl.iteye.com/blog/1842509]