4.1 BigTable的外部接口和架构
我们先来看BigTable对外提供的接口。
4.1.1 表
在逻辑上,BigTable的数据按表(table)来组织。在使用BigTable前,需要创建或者打开一个表。图4.1大致描述了BigTable中的一个表。
图4.1 BigTable中的表(此图参考BigTable论文[1])
4.1.2 数据
接下来,我们会逐一介绍图4.1中出现的表的构成元素。
● 一个表中的数据按行(row)组织,每一行用row key标识。row key是一个字符串。
● 在一行中,分成若干列(column),每个列都有自己的名字。
● 列被分成组,一组列叫作列族(column family)。
列族需要提前创建,即在调用写入接口前必须先创建列族。列不需要提前创建,也就是在调用写入接口时可以使用任意列名。
在同一个列族中,不同的行可以有不同的列,并且不会限制列的个数。列名也可以为空。我们来看下面的伪代码(与BigTable的实际接口语法有差异)例子。
综合以上信息,BigTable的数据模型如表4.1所示,BigTable中的表逻辑上类似于这个样子。
表4.1 BigTable的数据模型
4.1.3 原子性
BigTable的接口支持一行内的原子操作,也就是允许一次操作多个列,并且保持原子性。
在下面的代码(参考BigTable论文[1],接近BigTable的真实语法)例子中,对同一行的两个列分别进行Set操作和Delete操作,这两个操作会保证原子性。
4.1.4 时间戳
每个单元格都包含多个版本的数据,这些版本通过时间戳(timestamp)标识。时间戳是一个64位的整型数字。这个时间戳可以是BigTable生成的,也可以是应用指定并传给BigTable的。如果是应用自己生成的,那么应用需要保证生成唯一的、不重复的时间戳。
有了时间戳之后,BigTable的数据模型如表4.2所示,BigTable中的表逻辑上类似于这个样子。
表4.2 BigTable的数据模型(有了时间戳之后)
4.1.5 BigTable的数据模型
上面我们看到了BigTable表的逻辑结构,在BigTable内部,数据模型是一个多维排序Map。这个Map结构把row key、column key(包括family)和时间戳这三个维度映射到一个值,并且按这三个维度排序:
BigTable的实际数据模型如表4.3所示。
表4.3 BigTable的实际数据模型
可以看到,这个实际的数据模型就是很多key-value对按照key排序得到的。所以,虽然BigTable支持表、列甚至列族等复杂的逻辑数据模型,但它仍然被认为是一种key-value型的数据库。
通常这个Map很大,BigTable会按row key对它进行切分,每一份都叫作tablet。
继续拿表4.3举例,这个表可以被拆分成两个tablet,即tablet1和tablet2,分别如表4.4和表4.5所示。
表4.4 BigTable tablet1
表4.5 BigTable tablet2
4.1.6 BigTable的架构
BigTable在架构上包含5个组件:GFS、chubby、client、master和tablet server,分别说明如下。
● BigTable会将数据以日志文件和数据文件的形式存储在GFS中(后面的4.2.3节会详细介绍)。
● BigTable会为每个tablet指定一个tablet server,tablet server负责处理所有对这个tablet的读操作和写操作,并把这些读操作和写操作转化成对GFS的读/写操作。一个tablet只会由一个tablet server负责,一个tablet server会负责多个tablet。
● 一个主(master)节点负责把一个tablet指派给一个tablet server,在BigTable集群中只会有一个master。
● 客户端(client)是嵌入在应用中的一个库(library),或者叫作SDK。客户端不但包含对tablet server操作的代码,还包含了GFS的客户端和chubby的客户端。
● chubby是Google公司内部的一个分布式锁服务,类似于第7章中讲解的ZooKeeper。chubby负责维护BigTable集群,它有两个职责:
■ 选举master。
■ 维护tablet server(即维护当前BigTable集群中有哪些tablet server,并且这些tablet server是否还活着)。
另外,chubby还保存了BigTable集群的基础信息,包括root tablet的location和root tablet在GFS中的存储位置(后面的4.2.1节会详细讲解)。