Bigtable

121 阅读7分钟

Bigtable 的设计目标

概述

最基础的目标自然是应对业务需求的,能够支撑百万级别随机读写 IOPS,并且伸缩到上千台服务器的一个数据库

但是光能撑起 IOPS 还不够。在这个数据量下,整个系统的“可伸缩性”和“可运维性”就变得非常重要

伸缩性

第一个,是可以随时加减服务器,并且对添加减少服务器数量的限制要小,能够做到忙的时候加几台服务器,过几个小时峰值过去了,就可以把服务器降下来

第二个,是数据的分片会自动根据负载调整。某一个分片写入的数据多了,能够自动拆成多个分片来平衡负载。而如果负载大了,添加了服务器之后,也能很快平衡数据,让各个节点均匀承担压力

可运维性

小部分节点的故障,不应该影响整个集群的运行

Bigtable 基本数据模型

image.png

一条数据里面,有一个行键(Row Key),也就是这条数据的主键,Bigtable 提供了通过这个行键随机读写这条记录的接口。因为总是通过行键来读写数据,所以很多人也把这样的数据库叫做 KV 数据库

每一行里的数据呢,你需要指定一些列族(Column Family),每个列族下,你不需要指定列(Column)。每一条数据都可以有属于自己的列,每一行数据的列也可以完全不一样,因为列不是固定的。这个所谓不是固定的,其实就是列下面没有值。因为 Bigtable 在底层存储数据的时候,每一条记录都要把列和值存下来,没有值,意味着对应的这一行就没有这个列。这也是为什么说 Bigtable 是一个“稀疏”的表

列下面如果有值的话,可以存储多个版本,不同版本都会存上对应版本的时间戳(Timestamp),你可以指定保留最近的 N 个版本(比如 N=3,就是保留时间戳最近的三个版本),也可以指定保留某一个时间点之后的版本

Bigtable 动态区间分区

不再是一开始就定义好需要多少个机器,应该怎么分区,而是采用了一种自动去“分裂”(split)的方式来动态地进行分区

整个数据表,会按照行键排好序,然后按照连续的行键一段段地分区

如果某一段行键的区间里,写的数据越来越多,占用的存储空间越来越大,那么整个系统会自动地将这个分区一分为二,变成两个分区

而如果某一个区间段的数据被删掉了很多,占用的空间越来越小了,那么我们就会自动把这个分区和它旁边的分区合并到一起

image.png 这个分区的过程,就好像你按照 A~Z 的字母顺序去管理你的书的过程。一开始,你只有一个空箱子放在地上,然后你把你的书按照书名的拼音,从上到下放在箱子里

当有一本新书需要放进来的时候,你就按照字母顺序插在某两本书中间。而当箱子放不下的时候,你就再拿一个空箱子,放在放不下的箱子下面,然后把之前的箱子里的图书从中间一分,把下面的一半放到新箱子里

而我们删除数据的时候,就要把书从箱子里面拿走。当两个相邻的箱子里都很空的时候,我们就可以把两个箱子里面的书放到一个箱子里面,然后把腾出来的空箱子挪走

这里的一个个“箱子”就是我们的分片,这里面的一本本书,就是我们的一行数据,而书名的拼音,就是我们的行键。可能以 A、B、C 开头的书多一些,那么它们占用的分区就会多一些,以 U、V、W 开头的书少一些,可能这些书就都在一个分区里面

Master、Chubby 和 Tablet Server

image.png

Tablet Server

Tablet Server 的角色最明确,就是用来实际提供数据读写服务的

一个 Tablet Server 上会分配到 10 到 1000 个 Tablets,Tablet Server 就去负责这些 Tablets 的读写请求,并且在单个 Tablet 太大的时候,对它们进行分裂

而哪些 Tablets 分配给哪个 Tablet Server,自然是由 Master 负责的,而且 Master 可以根据每个 Tablet Server 的负载进行动态的调度,也就是 Master 还能起到负载均衡(load balance)的作用

Bigtable 的 Tablet Server 只负责在线服务,不负责数据存储。实际的存储,是通过一种叫做 SSTable 的数据格式写入到 GFS 上的

Master 作用

分配 Tablets 给 Tablet Server

检测 Tablet Server 的新增和过期

平衡 Tablet Server 的负载

对于 GFS 上的数据进行垃圾回收(GC)

管理表(Table)和列族的 Schema 变更,比如表和列族的创建与删除

Chubby 作用

确保我们只有一个 Master

存储 Bigtable 数据的引导位置(Bootstrap Location)

发现 Tablet Servers 以及在它们终止之后完成清理工作

存储 Bigtable 的 Schema 信息

存储 ACL,也就是 Bigtable 的访问权限

客户端具体是怎么查询的呢

image.png

客户端先去发起请求,查询 Chubby,看我们的 Root Tablet 在哪里

Chubby 会告诉客户端,Root Tablet 在 5 号 Tablet Server,这里我们简写成 TS5

客户端呢,会再向 TS5 发起请求,说我要查 Root Tablet,告诉我哪一个 METADATA Tablet 里,存放了 ECOMMERCE_ORDERS 业务表,行键为 A20210101RST 的记录的位置

TS5 会从 Root Tablet 里面查询,然后告诉客户端,说这个记录的位置啊,你可以从 TS8 上面的 METADATA 的 tablet 107,找到这个信息

然后,客户端再发起请求到 TS8,说我要在 tablet 107 里面,找 ECOMMERCE_ORDERS 表,行键为 A20210101RST 具体在哪里

TS8 告诉客户端,这个数据在 TS20 的 tablet 253 里面

客户端发起最后一次请求,去问 TS20 的 tablet 253,问 ECOMMERCE_ORDERS 表,行键为 A20210101RST 的具体数据

TS20 最终会把数据返回给客户端

Bigtable 实际写入数据的过程

当一个写请求过来的时候,Tablet Server 先会做基础的数据验证,包括数据格式是否合法,以及发起请求的客户端是否有权限进行对应的操作。这个权限设置,是 Tablet Server 从 Chubby 中获取到,并且缓存在本地的

如果写入的请求是合法的,对应的数据写入请求会以追加写的形式,写入到 GFS 上的提交日志文件中,这个写入对于 GFS 上的硬盘来说是一个顺序写。这个时候,我们就认为整个数据写入就已经成功了

在提交日志写入成功之后,Tablet Server 会再把数据写入到一张内存表中,也就是我们常说的 MemTable

而当我们写入的数据越来越多,要超出我们设置的阈值的时候,Tablet Server 会把当前内存里的整个 MemTable 冻结,然后创建一个新的 MemTable。被冻结的这个 MemTable,一般被叫做 Immutable MemTable,它会被转化成一个叫做 SSTable 的文件,写入到 GFS 上,然后再从内存里面释放掉。这个写入过程,是完整写一个新文件,所以自然也是顺序写

  • memtable -> immutable memtable -> sstable -> gfs

  • 相当于sstable是一个checkpoint