一、概述
BigTable是一个分布式的结构化的存储系统,是一个分布式的NoSQL数据库,本质上就是一个分布式的B+树索引。适于存储 结构化、 半结构化的海量数据(通常是分布在数千台服务器上的PB级数据)。二、数据模型
BigTable是一个稀疏的、分布式的、持久化存储的多维度排序Map稀疏:各个row可以包含不同的column key,不需要保持一致
分布式:基于range的数据分布
持久化存储:持久化到磁盘
多维度:每个row可以包含多个column
Map的key是行关键字、列关键字以及时间戳,value是byte数组:
(row:string, column:string, time:int64) -> string
行:BigTable通过行关键字的字典顺序组织数据。按照行关键字的范围(range)对数据进行划分,每个range称作Tablet。Tablet是数据分布和负载均衡调整的最小单位,在BigTable运行过程中,Tablet会分裂或者合并。
行存储的位置相关性:row key前缀相同的行会相邻存储,所以range scan可以搜索到相同前缀的所有行。列族:是访问控制的基本单位,由所有列关键字组成的集合表示。存放在同一列族下的所有数据通常属于同一个类型(压缩比例高)。列族必须事前创建,然后可以存放任意的列关键字。
列族不能创建的太多,最多几百个。通常,每个列族会对应一个SSTable。
列关键字:列族:限定词
时间戳:表的每一项可以包含同一份数据的不同版本,不同版本通过时间戳索引。数据项中不同版本的数据按照时间戳倒序排列。
列族具有两个参数,BigTable通过这两个参数(保持最后n个版本或者是最近一段时间)对废弃版本的数据自动进行垃圾收集。
三、Building Block
1. 使用GFS存储日志文件和数据文件2. 使用SSTable文件持久化存储数据
SSTable是一个 持久化的、 排序的、 不可更改的Map结构
支持操作:(1) get key (2) key range scan
文件格式:有一系列数据块(通常块大小64KB,可配)组成;由块索引(通常处于文件最后)定位数据块。
在打开SSTable文件时,块索引被加载到内存,每次查找可以通过一次磁盘搜索完成:首先二分查找在内存中的块索引找数据块的位置,然后再从硬盘读取相应的数据块。
3. 分布式协调(锁)服务Chubby(高可用)
Chubby提供类似文件目录树的名称空间,每个目录或文件可以当成一把锁,读写文件的操作是原子的。
BigTable使用Chubby完成以下功能:(存储元数据、分布式锁、服务器上下线)
(1)Master选择
(2)存储BigTable数据的自引导指令的位置(Root Table位置)
(3)查找Tablet服务器,以及在Tablet服务器失效时进行善后
(4)存储BigTable的模式信息以及访问控制列表
五、实现
BigTable包括三个主要的组件:链接到客户程序的lib、一个Master服务器和多个Tablet服务器1. Master服务器负责:
(1)为Tablet服务器分配Tablet
(2)检测新加入或者过期失效的Tablet服务器
(3)对Tablet服务器进行负载均衡
(4)对保存在GFS上的文件进行垃圾收集
(5)处理模式信息的修改(新建表、列族及访问控制信息)
2. Tablet服务器负责管理一个Tablet集合(通常每个服务器有大约数十个至上千个Tablet)。Tablet服务器负责处理它所加载的Tablet的读写操作,以及分裂和合并。
3. Tablet Location
BigTable使用类似B+树的结构存储Tablet的位置信息。
第一层是Chubby中的自引导信息,指向Root Tablet。
第二层是Root Tablet,其中的每一个Row指向一个MetaData Tablet。
第三层是MetaData Tablet,其中每一个Row指向一个存放数据的Tablet,Row key由该Tablet所在的表名和该Tablet最后一行组成。MetaData的每一行数据大小大约是1KB。
其中MetaData和Tablet可以自动分裂,Root Table永远不会被分裂,保证索引信息永远不超过三层。
客户端会缓存Tablet的位置信息,当缓存为空时,需要三次网络通信。当缓存过期时,最多需要六次网络通信。
4. Table分配
任何时刻、一个Tablet只能分配给一个Tablet服务器。Master服务器跟踪记录当前活跃的Tablet服务器,Tablet的分配状态以及未分配的Tablet。在需要时,进行Tablet的分配。
当启动一个Master服务器时,需要执行以下步骤获取系统当前状态信息,为Tablet分配作准备:
(1)从Chubby获取Master锁,阻止创建其他的Master实例
(2)扫描Chubby服务器文件锁存储目录,获取当前正在运行的Tablet服务器列表
(3)和所有正在运行的Tablet服务器通信,获取每个Tablet服务器上的Tablet分配信息
(4)扫描MetaData表获取所有的Tablet集合,若发现未分配的Tablet,则将其加入未分配Tablet集合,等待合适的机会分配
5. Tablet服务
Tablet的持久化信息(redo log和SSTable)存储在GFS上。
BigTable通过Redo Log进行故障恢复, 当执行一个更新操作时,首先会先写Redo Log,成功后,再修改内存数据结构memtable。当memtable大小达到某个阈值后,为了减少内存消耗,会进行compaction操作,将memtable转换成SSTable,同时记录下Redo Point。
当恢复一个Table时,Tablet服务器首先从MetaData表中读取元数据(SSTable文件列表,Redo Log以及一系列Redo Point)。Table服务器把SSTable的块索引加载到内存,然后从Redo Point之后对Redo Log进行replay,从而构建memtable。
进行读写操作时会先进行权限验证,一个读操作需要读取所有的SSTable的memtable的结果进行合并。
6. Compaction
minor compaction:memtable -> SSTable
major compaction:memtable + SSTable -> SSTable
minor compaction主要是两个目的:减少Tablet使用的内存,减少Tablet恢复时读取的log量。
major compaction可以减少文件占用的空间,同时可以回收已经删除的数据占用的资源。
六、优化
1. Locality groups客户端可以将多个列族组合成Locality group,在每个Tablet服务器上Locality group会使用一个独立的SSTable。
可以将经常一起读取的列族组合成Locality group,而不需要同时访问的列族使用不同的Locality group,提高读取效率。
可以为Locality group设置调优参数,比如,可以把一个Locality group全部存储在内存中
客户端可以控制一个Locality group对应的SSTable是否需要压缩。如果压缩,需要指定压缩算法,将SSTable中的每个块单独压缩。
为提高性能,Tablet服务器使用二级缓存策略。Scan Cache是第一级,缓存从SSTable接口返回的K-V对。Block Cache是第二级,缓存从GFS读取的Block。
对于重复使用相同数据的应用,Scan Cache很有用。
Block Cache对于数据局部性更有用(比如,顺序扫描)
Bloom filter用于确定集合是否存在某个元素。在执行读操作时,需要读取所有SSTable中的数据,如果SSTable不在内存,则需要多次访问磁盘。通过引入Bloom filter减少磁盘访问次数。使用Bloom filter查询SSTable中是否包含指定行和列的数据,如果不存在则不需要读磁盘。
BigTable为减少GFS并发写,以及ChunkServer上的disk seek,为每个Tablet服务器上所有的Tablet维护一个commit log,将对所有Tablet的修改操作顺序追加到同一个log。
使用这种方式可以提高普通操作性能,但是恢复工作变复杂了。如果一个Tablet服务器宕机了,那么其上的所有Tablet会被分配到其他多个Tablet服务器,这些服务器为了恢复,需要将commit log读多次,同时因为包含了很多冗余信息,也浪费了读效率。
为避免多次读取日志文件,BigTable首先会把log按照关键字(table, row name, log seq num)排序,使得相同table的log相邻排列,保证对同一tablet的修改连续存放在一起。在恢复时,只需一次seek操作,然后顺序读即可。为了加快排序,排序会并行进行。
当master触发table迁移时,如不进行优化,目的Tablet服务器需要进行commit log的replay,导致迁移时间很长。为了避免这种情况,源Tablet服务器会进行两次minor compaction,消除所有的commit log。
第一次minor compaction大量减少commit log,执行期间读写操作继续。完毕之后,会停止读写操作,执行第二次minor compaction,此次的commit log量很少,所以暂停时间很短。
两次minor compaction大幅减少暂停时间
除了SSTable cache(memtable)外,其他的SSTable都是不变的,所以并行读操作不需加锁。
memtable使用Copy on Write,减少读写竞争。
SSTable的不变性是的Tablet分裂非常便捷,只需让分裂的Tablet共享原有的SSTable集合即可。