BigTable

Posted noncontradiction

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BigTable相关的知识,希望对你有一定的参考价值。

 

简介

BigTable是一个分布式的结构化数据存储系统,设计用来处理海量数据,通常在数千台服务器,PB级的数据
 
BigTable是一个稀疏的,分布式的,持久化存储的多维度排序 Map
Map的索引(key)为:行关键字,列关键字,时间戳
Map的每个value都是未经解析的byte数组。
(row:string, column:string,time:int64)->string
例如:
 
行关键字是一个反向URL:com.cnn.www
contents列族存放了网页的内容
anchor列族存放引用了该网页的锚链接文本(样例中CNN被SI和My-look引用)
而contnts列包含三个版本,分别是t3,t5,t6

行关键字可以是任意的字符串,对同一行的读写操作都是原子的,不论其中有多少列。
BigTable通过行关键字的字典序来组织数据。

列族

列关键字组成的集合叫列族。列族时访问控制的基本单位。
列族的名字必须时可打印的字符串,而限定词的名字可以是任意字符串。

时间戳

    在BigTable中每个数据项都可以包含多个版本。时间戳是64位整数,因此可以给出精确到毫秒的“实时”时间。用户程序也可以给时间戳。如果程序需要避免数据版本冲突,那就必须自己生成唯一性的时间戳。不同版本排序时,新的在最前面。
    为了减轻负担,可以设定两个参数来自动进行垃圾收集。可以指定只保存后n个版本,或者最近几天的写入的内容。

API

    BigTable提供了修改集群,表和列族的元数据的API。
可以匹配正则表达式,进行对行,列,时间戳限制性扫描。

BigTable构建

利用Google的分布式文件系统(GFS)存储日志文件和数据文件
内部存储数据的文件是Google SSTable格式的。SSTable是一个持久化,排序的,不可更改的Map结构(类似于LevelDB,RocksDB中的LSM树)由于是排序好的,可以进行二分查找
还依赖一个高可用的,序列化的分布式锁服务组件,叫做Chubby。一个Chubby服务有5个活动的副本,其中一个是被选为Master,并处理请求。只有大多数副本正常运行,并且能相互通信时,Chubby才是可用的。主要用于片定位,片服务器的状态监控,访问控制列表存储等

介绍

BigTable包括了三个主要的组件:链接到客户程序等库,一个Master服务器和多个Tablet服务器。(可以动态添加/删除 Tablet服务器)
Master服务器:为Tablet服务器非配Tablets,检测添加或删除的Table服务器,进行负载均衡,以及对GFS上的文件进行垃圾收集。还可以建表和列族等。
每个Tablet服务器都管理一个Tablet等集合,处理读写操作,当Tablets过大时,对其进行分割。
客户端读取数据不经过Master服务器,直接与Tablet服务器通信进行读写操作。

Tablet的位置

 
一个三层的,类似B+树的结构存储Tablet的位置信息
第一层是一个存储Chubby中的文件,包含了Root Tablet的位置信息。Root Tablet实际上时METADATA Tablet(元数据表)的第一个Tablet,而且他不会被分割
在一个128MB(2^17)的METADATA Tablet,可以表示2^34个Tablet的地址。(如果每个Tablet存储128MB数据,那么一共可以存储2^61字节数据)
METADATA Tablet还存储了次级信息,包括每个Tablet的事件日志

Tablet分配

任何时刻一个Tablet只能分给一个Tablet服务器,Master会记录哪些活跃的Tablet服务器,哪些Tablet分配给哪些Tablet服务器,还有哪些没被分配。
Master执行的步骤:
1.从Chubby获取一个唯一的Master锁,阻止创建其他的Master服务器实例
2.扫描Chubby的服务器文件锁存储目录,获取正在运行的服务器列表
3.和正在运行的Tablet服务器通信,获取每个Tablet服务器的分配信息
4.扫描METADATA表获取所有Tablet的集合

Tablet服务

Tablet的持久化状态信息保存在GFS上。更新操作提交到REDO日志。最近的更新操作会放在排序的缓存中(memtable)。较早的更新放在SSTable中。为了恢复一个Tablet,服务器会先从METADATA读取它的元数据。Tablet的元数据包含了组成这个Tablet的SSTable列表,以及一系列的Redo Point,这些Redo Point可能包含该Tablet数据中的记录。服务器会把SSTable的索引读入内存,之后对于重复的Redo Point更新来重建memtable
 
对Tablet服务器进行读操作时,会在一系列SSTable和memtable合并的视图里执行。
当进行Table的合并和分割时,正在进行的读写操作能够继续进行。

Compactions(空间收缩)

随着写操作的执行,memtable越来越大,当到达一个门限制,这个memtable会被冻结,创建一个新的memtable,冻结的memtable会转为SSTable写入GFS。
       Minor Compaction有两个目的: 服务器使用内存,以及恢复过程,减少从日志中读取的数据量。在Compaction过程中,读操作仍能进行
能够清除已经删除的数据

优化

局部性群组

把多个列族合成一个局部性群组,对每个局部性群组都生成一个单独的SSTable。将通常不会一起访问的列组分割成不同的局部性群族可以提高读取效率。
而且可以设定参数,例如对经常访问的局部性群组全部存储在内存。

压缩

可以控制一个局部性群组的SSTable是否需要压缩。需要压缩用什么方式。虽然分块压缩浪费了少量空间,但对于只读取SSTable的一小部分时就不用解压整个文件了。
通常使用两遍可制定的压缩方式。第一遍在一个很大的窗口对常见的长字符串进行压缩。第二遍在16KB的小扫描窗口中寻找重复数据。速度都很快。而且仅仅存一个版本数据时,空间压缩比就到达了10:1

通过缓存提高读操作的性能

为了提高读操作的性能,Tablet服务器使用了二级缓存的策略。扫描缓存是第一级缓存,主要存储获取的key/value,Block缓存是二级缓存,存储GFS读取的SSTable的Block。这对经常读取相同数据,或者刚刚读取的数据附近的数据的应用程序非常有效。

Bloom过滤器

可以使用Bloom过滤器,大概率筛选掉那些不再磁盘中的,减少访问磁盘的次数。(但减少的只是访问不存在的行或者列)

Commit日志的实现

对每个Tablet服务器设置一个Commit日志文件,把修改操作的日志以追加的方式写入同一个日志文件。
但对于恢复变得复杂了,当一个Tablet宕机时,被加载的Tablet会被转移到不同的服务器上,这样当恢复一个Tablet时要从原来的日志中提取修改操作的信息。如果要是被分配时,新的Tablet都读取一遍日志的话,会产生多次读取日志文件。为此我们把日志按照关键字排序,对于一个Tablet的操作的日志记录就连续放在一起了,因此我们只需要一次磁盘seek操作即可。位了并行排序,我们将日志分为 64MB的段,不同的Tablet服务器对进行并行排序。这个排序由Master服务器来协同处理。
为了确保GFS负载高峰时仍能顺利进行,每个Tablet由两个日志写入线程,任何时刻只有一个工作,当一个写入的时候效率很低,就切换到另一个线程。而且每个日志记录都有一个序列号。恢复时自动忽略那些由于线程切换而重复的记录。

Tablet恢复提速

当Master服务器将一个Tablet从一个服务器转移到另一个服务器时,会对其进行一次Minor Compaction。这个操作会减少日志文件中没有归并的记录,减少恢复时间。Compaction以后,该服务器就停止对其提供服务,在卸载Tablet之前,源Tablet服务器还会做一次(通常很快)Minor Compaction,消除压缩过程中产生未归并的记录。第二次Minor Compaction以后,Tablet就会被转载到新的Tablet服务器上了,并且不需要从日志中进行恢复。

利用不变性

在使用BigTable时,除了SSTable缓存之外的其余部分产生的SSTable都是不变的。这样我们就可以高效的实现对行的并行操作。memtable时唯一一个能被读和写操作同时访问的可变数据结构。我们采用COW(Copy-on-write)机制,使得允许读写操作并行。
因为SSTable是不变的,因此可以把永久删除被标记为删除的数据的问题,转为对废弃的SSTable进行垃圾收集的问题了。
最后,SSTable的不变性使得分割Tablet操作非常快捷,不必为每个分割的Tablet建立新的SSTable集合,而是共享原来的集合。

以上是关于BigTable的主要内容,如果未能解决你的问题,请参考以下文章