这就是搜索引擎(6) 云存储之BigTable
0. 背景
BigTable是一个负责管理海量结构化或者半结构化数据的分布式存储系统。在Google的云存储体系中处于核心地位,起到了承上启下的作用。之前说的GFS是一个分布式的海量文件管理系统,其对于存储的文件没有任何假定,而BigTable是在GFS的基础上建立了数据的结构化解释。BigTable存储的的有结构化解释的数据能够更贴近实际应用,因此MegaStore和Percolator计算模型都是建立在BigTable之上的存储和计算模型。
- BigTable存储海量数据,通常是分布在数千台普通服务器上的PB级数据
- BigTable要求提供灵活的、高性能的数据存储方案
1. 数据模型
BigTable的数据模型是多维度排序Map,Map的key是一个包含(行关键字、列关键字、时间戳)的三维索引,value是一个未经解析的字节数组。
如下图所示,是一个WebTable具体表格的部分样例,里面存储了互联网的网页,表中每一行存储了某个网页的相关信息,且以网页的url作为主键。
因为网页可能会随着时间的变化而变化,因此同样的数据可以通过时间戳不同进而随时间的变化而保留不同的版本,
- BigTable要求存储数据的主键必须是字符串的形式,BigTable在具体存储数据的时候,会按照主键的字典序大小进行排序。
- BigTable的数据模型看起来很像关系型数据库的关系模型,实际关系型数据库的列在建表之初就必须确定,BigTable可以随时对表格列进行增删,并且每行只存储列不为空的数据,这被称之为自由型(Schema Free)数据库。
2. 整体结构
在开发者视角下,BigTable就是由很多WebTable这样的三维表格共同组成的一个系统,开发者只要考虑对表的内容、列字段的定义,数据的增删等操作,实际的内部存储交由BigTable管理。
2.1 组成部分
BigTable是一个分布式的海量存储系统,对同一张表,需要有成百上千的机器提供存取服务,因此在实际存储表格时,BigTable会将表格按照行主键进行切割,将一段连续的行数据组成一个存储单元,称为“子表”(Tablet)。每个子表数据会交由各自的子表服务器(Tablet Server)来管理,实际数据在子表服务器中是以一个或多个SSTable进行存储的。
如下图所示,BigTable分为四个部分:客户端(client), 主控服务器(Master Server), 子表服务器(Tablet Server), Chubby。子表服务器向下负责子表中数据的存储,向上需要响应客户端的读写请求,主控服务器负责整个系统的管理工作包括子表的分配、子表服务器的负载均衡、失效检测等。Chubby负责维护系统的管理数据。
2.2 数据管理
Chubby在上一篇中有提到,是Google开发的一个分布式锁服务,用于维护分布式系统的公共资源。在BigTable中,Chubby和元数据表(MetaData Table)一起被用于维护系统管理数据。元数据表是BigTable中一张特殊的表,表格的每一行记录了BigTable中某个具体的子表存储在哪台子表服务器上之类的管理信息,这张表同样被切割成多张子表,存储在子表服务器中,其中第一个字表称为Root子表,用来存储其他元数据子表的位置信息。而Chubby中某个文件则指出了Root子表所在服务器的信息,这样一来,Chubby、Root子表以及元数据表中的其他子表就组成了一个三级查询结构,通过这样的结构就能够定位具体应用的某个子表放置在哪个子表服务器。
2.3 Master Server
MasterServer在BigTable中专门负责管理工作
- 自动发现是否有新的服务器加入
- 是否有子表服务器因为故障不能提供服务
- 是否有子表服务器负载过高
- 负责子表服务器之间的负载均衡,保证每个服务器处于合理负载
当主控服务器被启动时,就需要获知子表的分配情况,而系统数据的维护放在Chubby上,Chubby不仅负责管理数据,而且提供了粗粒度的加锁服务,Chubby中有一个server目录,记载了所有子表服务器的地址信息,在获取了这些信息后,master就可以直接和子表服务器通信。因此在Master启动时,流程如下
- 从Chubby中获取Master锁,从而阻止其他Master Server再次启动。
- 读取Servers目录,获取每个子表服务器的地址信息。
- 和每个子表服务器直接通信,获知每个子表服务器存储了哪些子表并记录在内存管理数据中。
- 从Chubby的root节点获取元数据,这里记载了系统所有子表的信息。
- 对于MetaData和子表服务器反馈的信息,会发现有一些子表在MetaData中但没有子表服务器进行存储,说明这些是未分配的内容,因此将其加入未分配集合中,在适当的时机分配给负载低的子表服务器。
- 当有新的子表服务器加入的时候,服务器会将自己的信息注册到Chubby的Servers目录下,当Master Server轮询时很快能够发现有新的子表服务器,之后可以将高负载的其他子表服务器下的数据或是未分配的子表数据交由新服务器管理。
- 当有子表服务器无法取得联系之后,会将Severs下的信息从Chubby删除,并将其子表放入未分配子表中,之后找机会将这些字表分配。
2.4 Tablet Sever
子表服务器用来存储和管理子数据,主要支持以下功能
- 存储子表数据,包括子表存储、恢复、分裂、合并
- 响应客户端的读写请求。
需要注意的一点在于,由于BigTable整个是基于GFS开发的,因此实际的数据也都存在GFS上,子表服务器管理的数据并一定不存放在子表服务器里,因此子表服务器的崩溃并不会导致磁盘上数据的丢失,所以对磁盘数据的恢复实际上是对BigTable这一层屏蔽的,是GFS需要关心的。
而每一个Table Server,其实类似于是一个LevelDB,其接受用户的读写请求,并将写到内存中的数据根据一定的触发条件,写入到磁盘上,从而保证海量数据的高性能写入,对LevelDB原理的阐述在另一系列(LevelDB)中有讲到,这里就不详细展开了。
唯一有区别的是Tablet Server底层的LSM结构中,对SSTable之间的合并与默认的LevelDB合并逻辑有所区别,我想这也是为了适应搜索业务下大规模数据而做出的改变,Table Server的有三种合并策略
- 微合并(Minor Compaction): 发生于MemTable中的数据达到阈值,写入到磁盘上一个新SSTable的过程
- 部分合并(Merging Compaction): 将MemTable的内容和部分SSTable的合并
- 主合并(Major Compaction): 将MemTable和所有SSTable进行合并