Bigtable探秘 Google公布式数据存储系统
发布时间:2022-04-30 14:14:18 所属栏目:云计算 来源:互联网
导读:Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据。Google 的很多项目使用Bigtable存储数据,包括Web索引、Google Earth、Google Finance。这些应用对Bigtable提出的要求差异非常大,无论
|
5 介绍 Bigtable包括了三个主要的组件:链接到客户程序中的库、一个Master服务器和多个Tablet服务器。针对系统工作负载的变化情 况,BigTable可以动态的向集群中添加(或者删除)Tablet服务器。 Master服务器主要负责以下工作:为Tablet服务器分配Tablets、检 测新加入的或者过期失效的Table服务器、对Tablet服务器进行负载均衡、以及对保存在GFS上的文件进行垃圾收集。除此之外,它还处理对模式的相 关修改操作,例如建立表和列族。 每个Tablet服务器都管理一个Tablet的集合(通常每个服务器有大约数十个至上千个Tablet)。每个Tablet服务器负责处理它 所加载的Tablet的读写操作,以及在Tablets过大时,对其进行分割。 和很多Single-Master类型的分布式存储系统【17.21】类似,客户端读取的数据都不经过Master服务器:客户程序直接和 Tablet服务器通信进行读写操作。由于BigTable的客户程序不必通过Master服务器来获取Tablet的位置信息,因此,大多数客户程序甚 至完全不需要和Master服务器通信。在实际应用中,Master服务器的负载是很轻的。 一个BigTable集群存储了很多表,每个表包含了一个Tablet的集合,而每个Tablet包含了某个范围内的行的所有相关数据。初始状 态下,一个表只有一个Tablet。随着表中数据的增长,它被自动分割成多个Tablet,缺省情况下,每个Tablet的尺寸大约是100MB到 200MB。 5.1 Tablet的位置 我们使用一个三层的、类似B+树[10]的结构存储Tablet的位置信息(如图4)。 Tablet的位置 第一层是一个存储在Chubby中的文件,它包含了Root Tablet的位置信息。Root Tablet包含了一个特殊的METADATA表里所有的Tablet的位置信息。METADATA表的每个Tablet包含了一个用户Tablet的集 合。Root Tablet实际上是METADATA表的第一个Tablet,只不过对它的处理比较特殊 — Root Tablet永远不会被分割 — 这就保证了Tablet的位置信息存储结构不会超过三层。 在METADATA表里面,每个Tablet的位置信息都存放在一个行关键字下面,而这个行关键字是由Tablet所在的表的标识符和Tablet 的最后一行编码而成的。METADATA的每一行都存储了大约1KB的内存数据。在一个大小适中的、容量限制为128MB的METADATA Tablet中,采用这种三层结构的存储模式,可以标识2^34个Tablet的地址(如果每个Tablet存储128MB数据,那么一共可以存储 2^61字节数据)。 客户程序使用的库会缓存Tablet的位置信息。如果客户程序没有缓存某个Tablet的地址信息,或者发现它缓存的地址信息不正确,客户程序就在 树状的存储结构中递归的查询Tablet位置信息;如果客户端缓存是空的,那么寻址算法需要通过三次网络来回通信寻址,这其中包括了一次Chubby读操 作;如果客户端缓存的地址信息过期了,那么寻址算法可能需要最多6次网络来回通信才能更新数据,因为只有在缓存中没有查到数据的时候才能发现数据过期(alex注:其中的三次通信发现缓存过期,另外三次更新缓存数据)(假 设METADATA的Tablet没有被频繁的移动)。尽管Tablet的地址信息是存放在内存里的,对它的操作不必访问GFS文件系统,但是,通常我们 会通过预取Tablet地址来进一步的减少访问的开销:每次需要从METADATA表中读取一个Tablet的元数据的时候,它都会多读取几个 Tablet的元数据。 在METADATA表中还存储了次级信息(alex 注:secondary information),包括每个Tablet的事件日志(例如,什么时候一个服务器开始为该 Tablet提供服务)。这些信息有助于排查错误和性能分析。 5.2 Tablet分配 在任何一个时刻,一个Tablet只能分配给一个Tablet服务器。Master服务器记录了当前有哪些活跃的Tablet服务器、哪些 Tablet分配给了哪些Tablet服务器、哪些Tablet还没有被分配。当一个Tablet还没有被分配、并且刚好有一个Tablet服务器有足够 的空闲空间装载该Tablet时,Master服务器会给这个Tablet服务器发送一个装载请求,把Tablet分配给这个服务器。 BigTable使用Chubby跟踪记录Tablet服务器的状态。当一个Tablet服务器启动时,它在Chubby的一个指定目录下建立一个 有唯一性名字的文件,并且获取该文件的独占锁。Master服务器实时监控着这个目录(服务器目录),因此Master服务器能够知道有新的Tablet 服务器加入了。如果Tablet服务器丢失了Chubby上的独占锁 — 比如由于网络断开导致Tablet服务器和Chubby的会话丢失 — 它就停止对Tablet提供服务。(Chubby提供了一种高效的机制,利用这种机制,Tablet服务器能够在不增加网络负担的情况下知道它是否还持有 锁)。只要文件还存在,Tablet服务器就会试图重新获得对该文件的独占锁;如果文件不存在了,那么Tablet服务器就不能再提供服务了,它会自行退 出(alex注:so it kills itself)。当Tablet服务器终止时(比如,集群的管理系统将运行该Tablet 服务器的主机从集群中移除),它会尝试释放它持有的文件锁,这样一来,Master服务器就能尽快把Tablet分配到其它的Tablet服务器。 Master服务器负责检查一个Tablet服务器是否已经不再为它的Tablet提供服务了,并且要尽快重新分配它加载的Tablet。 Master服务器通过轮询Tablet服务器文件锁的状态来检测何时Tablet服务器不再为Tablet提供服务。如果一个Tablet服务器报告它 丢失了文件锁,或者Master服务器最近几次尝试和它通信都没有得到响应,Master服务器就会尝试获取该Tablet服务器文件的独占锁;如果 Master服务器成功获取了独占锁,那么就说明Chubby是正常运行的,而Tablet服务器要么是宕机了、要么是不能和Chubby通信了,因 此,Master服务器就删除该Tablet服务器在Chubby上的服务器文件以确保它不再给Tablet提供服务。一旦Tablet服务器在 Chubby上的服务器文件被删除了,Master服务器就把之前分配给它的所有的Tablet放入未分配的Tablet集合中。为了确保 Bigtable集群在Master服务器和Chubby之间网络出现故障的时候仍然可以使用,Master服务器在它的Chubby会话过期后主动退 出。但是不管怎样,如同我们前面所描述的,Master服务器的故障不会改变现有Tablet在Tablet服务器上的分配状态。 当集群管理系统启动了一个Master服务器之后,Master服务器首先要了解当前Tablet的分配状态,之后才能够修改分配状态。 Master服务器在启动的时候执行以下步骤:(1)Master服务器从Chubby获取一个唯一的Master锁,用来阻止创建其它的Master服 务器实例;(2)Master服务器扫描Chubby的服务器文件锁存储目录,获取当前正在运行的服务器列表;(3)Master服务器和所有的正在运行 的Tablet表服务器通信,获取每个Tablet服务器上Tablet的分配信息;(4)Master服务器扫描METADATA表获取所有的 Tablet的集合。在扫描的过程中,当Master服务器发现了一个还没有分配的Tablet,Master服务器就将这个Tablet加入未分配的 Tablet集合等待合适的时机分配。 可能会遇到一种复杂的情况:在METADATA表的Tablet还没有被分配之前是不能够扫描它的。因此,在开始扫描之前(步骤4),如果在第三步 的扫描过程中发现Root Tablet还没有分配,Master服务器就把Root Tablet加入到未分配的Tablet集合。这个附加操作确保了Root Tablet会被分配。由于Root Tablet包括了所有METADATA的Tablet的名字,因此Master服务器扫描完Root Tablet以后,就得到了所有的METADATA表的Tablet的名字了。 保存现有Tablet的集合只有在以下事件发生时才会改变:建立了一个新表或者删除了一个旧表、两个Tablet被合并了、或者一个Tablet被 分割成两个小的Tablet。Master服务器可以跟踪记录所有这些事件,因为除了最后一个事件外的两个事件都是由它启动的。Tablet分割事件需要 特殊处理,因为它是由Tablet服务器启动。在分割操作完成之后,Tablet服务器通过在METADATA表中记录新的Tablet的信息来提交这个 操作;当分割操作提交之后,Tablet服务器会通知Master服务器。如果分割操作已提交的信息没有通知到Master服务器(可能两个服务器中有一 个宕机了),Master服务器在要求Tablet服务器装载已经被分割的子表的时候会发现一个新的Tablet。通过对比METADATA表中 Tablet的信息,Tablet服务器会发现Master服务器要求其装载的Tablet并不完整,因此,Tablet服务器会重新向Master服务 器发送通知信息。 5.3 Tablet服务 Tablet服务 如图5所示,Tablet的持久化状态信息保存在GFS上。更新操作提交到REDO日志中(alex注:Updates are committed to a commit log that stores redo records)。在这些更新操作中,最近提交的那些存放在一个排序的缓存中,我们称这个缓存为 memtable;较早的更新存放在一系列SSTable中。为了恢复一个Tablet,Tablet服务器首先从METADATA表中读取它的元数据。 Tablet的元数据包含了组成这个Tablet的SSTable的列表,以及一系列的Redo Point(alex注:a set of redo points),这 些Redo Point指向可能含有该Tablet数据的已提交的日志记录。Tablet服务器把SSTable的索引读进内存,之后通过重复Redo Point之后提交的更新来重建memtable。 当对Tablet服务器进行写操作时,Tablet服务器首先要检查这个操作格式是否正确、操作发起者是否有执行这个操作的权限。权限验证的方法是 通过从一个Chubby文件里读取出来的具有写权限的操作者列表来进行验证(这个文件几乎一定会存放在Chubby客户缓存里)。成功的修改操作会记录在 提交日志里。可以采用批量提交方式(alex注:group commit)来提高包含大量小的修改操作的应用程序的吞吐量【13,16】。当一个写操作提交后,写的内容插入到 memtable里面。 当对Tablet服务器进行读操作时,Tablet服务器会作类似的完整性和权限检查。一个有效的读操作在一个由一系列SSTable和 memtable合并的视图里执行。由于SSTable和memtable是按字典排序的数据结构,因此可以高效生成合并视图。 当进行Tablet的合并和分割时,正在进行的读写操作能够继续进行。 5.4 Compactions (alex注:这个词挺简单,但是在这节里面挺难翻译的。应 该是空间缩减的意思,但是似乎又不能完全概括它在上下文中的意思,干脆,不翻译了) 随着写操作的执行,memtable的大小不断增加。当memtable的尺寸到达一个门限值的时候,这个memtable就会被冻结,然后创建一 个新的memtable;被冻结住memtable会被转换成SSTable,然后写入GFS(alex注:我们称这种Compaction行为为Minor Compaction)。Minor Compaction过程有两个目的:shrink(alex注:shrink是数据库用语,表示空间收缩)Tablet 服务器使用的内存,以及在服务器灾难恢复过程中,减少必须从提交日志里读取的数据量。在Compaction过程中,正在进行的读写操作仍能继续。 每一次Minor Compaction都会创建一个新的SSTable。如果Minor Compaction过程不停滞的持续进行下去,读操作可能需要合并来自多个SSTable的更新;否则,我们通过定期在后台执行Merging Compaction过程合并文件,限制这类文件的数量。Merging Compaction过程读取一些SSTable和memtable的内容,合并成一个新的SSTable。只要Merging Compaction过程完成了,输入的这些SSTable和memtable就可以删除了。 合并所有的SSTable并生成一个新的SSTable的Merging Compaction过程叫作Major Compaction。由非Major Compaction产生的SSTable可能含有特殊的删除条目,这些删除条目能够隐藏在旧的、但是依然有效的SSTable中已经删除的数据(alex注:令人费解啊,原文是SSTables produced by non-major compactions can contain special deletion entries that suppress deleted data in older SSTables that are still live)。而Major Compaction过程生成的SSTable不包含已经删除的信息或数据。Bigtable循环扫描它所有的Tablet,并且定期对它们执行 Major Compaction。Major Compaction机制允许Bigtable回收已经删除的数据占有的资源,并且确保BigTable能及时清除已经删除的数据(alex注:实际是回收资源。数据删除后,它占有的空间并不能马上重复利用;只有空间 回收后才能重复使用),这对存放敏感数据的服务是非常重要。 6 优化 上一章我们描述了Bigtable的实现,我们还需要很多优化工作才能使Bigtable到达用户要求的高性能、高可用性和高可靠性。本章描述 了Bigtable实现的其它部分,为了更好的强调这些优化工作,我们将深入细节。 局部性群组 客户程序可以将多个列族组合成一个局部性群族。对Tablet中的每个局部性群组都会生成一个单独的SSTable。将通常不会一起访问的列族 分割成不同的局部性群组可以提高读取操作的效率。例如,在Webtable表中,网页的元数据(比如语言和Checksum)可以在一个局部性群组中,网 页的内容可以在另外一个群组:当一个应用程序要读取网页的元数据的时候,它没有必要去读取所有的页面内容。 此外,可以以局部性群组为单位设定一些有用的调试参数。比如,可以把一个局部性群组设定为全部存储在内存中。Tablet服务器依照惰性加载的 策略将设定为放入内存的局部性群组的SSTable装载进内存。加载完成之后,访问属于该局部性群组的列族的时候就不必读取硬盘了。这个特性对于需要频繁 访问的小块数据特别有用:在Bigtable内部,我们利用这个特性提高METADATA表中具有位置相关性的列族的访问速度。 压缩 客户程序可以控制一个局部性群组的SSTable是否需要压缩;如果需要压缩,那么以什么格式来压缩。每个SSTable的块(块的大小由局部性群 组的优化参数指定)都使用用户指定的压缩格式来压缩。虽然分块压缩浪费了少量空间(alex注:相比于对整个SSTable进行压缩,分块压缩压缩率较低),但是,我们在只读取SSTable 的一小部分数据的时候就不必解压整个文件了。很多客户程序使用了“两遍”的、可定制的压缩方式。第一遍采用Bentley and McIlroy’s方式[6],这种方式在一个很大的扫描窗口里对常见的长字符串进行压缩;第二遍是采用快速压缩算法,即在一个16KB的小扫描窗口中寻 找重复数据。两个压缩的算法都很快,在现在的机器上,压缩的速率达到100-200MB/s,解压的速率达到400-1000MB/s。 虽然我们在选择压缩算法的时候重点考虑的是速度而不是压缩的空间,但是这种两遍的压缩方式在空间压缩率上的表现也是令人惊叹。比如,在 Webtable的例子里,我们使用这种压缩方式来存储网页内容。在一次测试中,我们在一个压缩的局部性群组中存储了大量的网页。针对实验的目的,我们没 有存储每个文档所有版本的数据,我们仅仅存储了一个版本的数据。该模式的空间压缩比达到了10:1。这比传统的Gzip在压缩HTML页面时3:1或者 4:1的空间压缩比好的多;“两遍”的压缩模式如此高效的原因是由于Webtable的行的存放方式:从同一个主机获取的页面都存在临近的地方。利用这个 特性,Bentley-McIlroy算法可以从来自同一个主机的页面里找到大量的重复内容。不仅仅是Webtable,其它的很多应用程序也通过选择合 适的行名来将相似的数据聚簇在一起,以获取较高的压缩率。当我们在Bigtable中存储同一份数据的多个版本的时候,压缩效率会更高。 通过缓存提高读操作的性能 为了提高读操作的性能,Tablet服务器使用二级缓存的策略。扫描缓存是第一级缓存,主要缓存Tablet服务器通过SSTable接口获取的 Key-Value对;Block缓 存是二级缓存,缓存的是从GFS读取的SSTable的Block。对于经常要重复读取相同数据的应用程序来说,扫描缓存非常有效;对于经常要读取刚刚读 过的数据附近的数据的应用程序来说,Block缓存更有用(例如,顺序读,或者在一个热点的行的局部性群组中随机读取不同的列)。 Bloom过滤器 (alex注:Bloom,又叫布隆过滤器,什么意思?请参 考Google黑板报http://googlechinablog.com/2007/07/bloom-filter.html请务必先认真阅读) 如5.3节所述,一个读操作必须读取构成Tablet状态的所有SSTable的数据。如果这些SSTable不在内存中,那么就需要多次访问硬 盘。我们通过允许客户程序对特定局部性群组的SSTable指定Bloom过滤器【7】,来减少硬盘访问的次数。我们可以使用Bloom过滤器查询一个 SSTable是否包含了特定行和列的数据。对于某些特定应用程序,我们只付出了少量的、用于存储Bloom过滤器的内存的代价,就换来了读操作显著减少 的磁盘访问的次数。使用Bloom过滤器也隐式的达到了当应用程序访问不存在的行或列时,大多数时候我们都不需要访问硬盘的目的。 Commit日志的实现 如果我们把对每个Tablet的操作的Commit日志都存在一个单独的文件的话,那么就会产生大量的文件,并且这些文件会并行的写入GFS。根据 GFS服务器底层文件系统实现的方案,要把这些文件写入不同的磁盘日志文件时(alex注:different physical log files),会有大量的磁盘Seek操作。另外,由 于批量提交(alex注:group commit)中 操作的数目一般比较少,因此,对每个Tablet设置单独的日志文件也会给批量提交本应具有的优化效果带来很大的负面影响。为了避免这些问题,我们设置每 个Tablet服务器一个Commit日志文件,把修改操作的日志以追加方式写入同一个日志文件,因此一个实际的日志文件中混合了对多个Tablet修改 的日志记录。 使用单个日志显著提高了普通操作的性能,但是将恢复的工作复杂化了。当一个Tablet服务器宕机时,它加载的Tablet将会被移到很多其它的 Tablet服务器上:每个Tablet服务器都装载很少的几个原来的服务器的Tablet。当恢复一个Tablet的状态的时候,新的Tablet服务 器要从原来的Tablet服务器写的日志中提取修改操作的信息,并重新执行。然而,这些Tablet修改操作的日志记录都混合在同一个日志文件中的。一种 方法新的Tablet服务器读取完整的Commit日志文件,然后只重复执行它需要恢复的Tablet的相关修改操作。使用这种方法,假如有100台 Tablet服务器,每台都加载了失效的Tablet服务器上的一个Tablet,那么,这个日志文件就要被读取100次(每个服务器读取一次)。 为了避免多次读取日志文件,我们首先把日志按照关键字(table,row name,log sequence number)排序。排序之后,对同一个Tablet的修改操作的日志记录就连续存放在了一起,因此,我们只要一次磁盘Seek操作、之后顺序读取就可以 了。为了并行排序,我们先将日志分割成64MB的段,之后在不同的Tablet服务器对段进行并行排序。这个排序工作由Master服务器来协同处理,并 且在一个Tablet服务器表明自己需要从Commit日志文件恢复Tablet时开始执行。 在向GFS中写Commit日志的时候可能会引起系统颠簸,原因是多种多样的(比如,写操作正在进行的时候,一个GFS服务器宕机了;或者连接三个 GFS副本所在的服务器的网络拥塞或者过载了)。为了确保在GFS负载高峰时修改操作还能顺利进行,每个Tablet服务器实际上有两个日志写入线程,每 个线程都写自己的日志文件,并且在任何时刻,只有一个线程是工作的。如果一个线程的在写入的时候效率很低,Tablet服务器就切换到另外一个线程,修改 操作的日志记录就写入到这个线程对应的日志文件中。每个日志记录都有一个序列号,因此,在恢复的时候,Tablet服务器能够检测出并忽略掉那些由于线程 切换而导致的重复的记录。 Tablet恢复提速 当Master服务器将一个Tablet从一个Tablet服务器移到另外一个Tablet服务器时,源Tablet服务器会对这个 Tablet做一次Minor Compaction。这个Compaction操作减少了Tablet服务器的日志文件中没有归并的记录,从而减少了恢复的时间。Compaction 完成之后,该服务器就停止为该Tablet提供服务。在卸载Tablet之前,源Tablet服务器还会再做一次(通常会很快)Minor Compaction,以消除前面在一次压缩过程中又产生的未归并的记录。第二次Minor Compaction完成以后,Tablet就可以被装载到新的Tablet服务器上了,并且不需要从日志中进行恢复。 利用不变性 我们在使用Bigtable时,除了SSTable缓存之外的其它部分产生的SSTable都是不变的,我们可以利用这一点对系统进行简化。例如, 当从SSTable读取数据的时候,我们不必对文件系统访问操作进行同步。这样一来,就可以非常高效的实现对行的并行操作。memtable是唯一一个能 被读和写操作同时访问的可变数据结构。为了减少在读操作时的竞争,我们对内存表采用COW(Copy-on-write)机制,这样就允许读写操作并行执 行。 因为SSTable是不变的,因此,我们可以把永久删除被标记为“删除”的数据的问题,转换成对废弃的SSTable进行垃圾收集的问题了。每个 Tablet的SSTable都在METADATA表中注册了。Master服务器采用“标记-删除”的垃圾回收方式删除SSTable集合中废弃的 SSTable【25】,METADATA表则保存了Root SSTable的集合。 最后,SSTable的不变性使得分割Tablet的操作非常快捷。我们不必为每个分割出来的Tablet建立新的SSTable集合,而是共享原 来的Tablet的SSTable集合。 (编辑:玉林站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


