分布式表格系统Google Bigtable详解

概述

bigtable系统由表格组成,每行有一个主键(Row Key),每行又包含很多列(Column),某一行的某一列构成一个单元(Cell),每个单元包含多个版本的数据。整体上看,Bigtable是一个分布式多维映射表:

( r o w : s t r i n g , c o l u m n : s t r i n g , t i m e s t a m p : i n t 64 ) − > s t r i n g (row:string, column:string, timestamp:int64) -> string (row:string,column:string,timestamp:int64)>string

分布

另外,Bigtable引入了列族(column family)的概念。多个列形成一个列族。其由两部分组成:(columnh family qualifier)

Bigtable支持时间戳是谷歌上层业务的需求。为了简化不同版本的数据管理,Bigtable提供了两种设置:

  1. 保留最近的N个版本
  2. 保留限定时间内的所有版本

Bigtable架构

Bigtable主要由以下几种服务组成:

  1. Client对表格进行CRUD,通过Chubby获取一些控制信息
  2. Master管理所有的子表,包括子表的分配、合并、接收从TabletServer传来的子表分裂消息、监控TabletServer,以及TabletServer的负载均衡和故障恢复
  3. Tablet Server子表服务器,完成所有存储相关操作,如CRUD,子表的合并与分裂,操作日志(每条操作日志都有唯一的序号)和sstable的操作。所有的表都是单副本。
  4. Chubby:bigtable使用Chubby完成以下功能,如果Chubby不可用,则bigtable整个系统不可用,其主要提供了以下服务:
    1. Master选主
    2. 存储bigtable根表的位置信息
    3. TabletServer注册服务
    4. 存储bigtable schema信息

Bigtable主要提供了三种类型的表:

  1. User Table:存储实际用户数据
  2. Meta Table:存储用户表的元数据
  3. Root Table:存储Meta Table的元数据。相当于跟表引导到元数据表再引导到用户表

Tablet location hierarchy.

数据分布

bigtable底层分为100M-200M的子表,通过一个根表和两级元数据表建立索引。我们假设平均一个子表为128M,每个子表的元数据信息为1KB,那么两级元数据表能支持的数据量为 128 M ∗ ( 128 M / 1 K ) ∗ ( 128 M / 1 K ) = 2048 P B 128M*(128M/1K)*(128M/1K)=2048PB 128M(128M/1K)(128M/1K)=2048PB

  1. 客户端会缓存子表元数据,如果缓存为空或者缓存过期,则通过根表->一级子表->二级子表的查找顺序来读取子表的位置信息。
  2. 使用prefetch(预取)技术,每次访问元数据表的时候,不仅读取所需子表的信息,而且读取连续多个子表数据,这样访问下一个子表就不需要再次访问元数据表了。(mysql也有类似的优化)

保证

  1. 通过Chubby提供跨进程锁,保证同一时间只有一个TabletServer访问一个子表,如果该TabletServer出现故障,Master需要等TabletServer互斥锁失效,才能将其上的子表迁移到其他TabletServer上。
  2. 由于bigtable底层使用GFS,GFS的追加写只保证“同一纪录至少成功写入一次”,所以bigtable使用了如下方法保证数据的安全:
    1. 记录操作日志(redo log),每条操作日志都有唯一的序号,从而去除重复的操作日志,故障的时候一部分sstable数据由于存储在内存中丢失了,需要通过回放操作日志来恢复
    2. 每个子表包含的sstable数据,bigtable以最后一条写入成功的sstable数据为准
  3. bigtable可以实现单行事务single-row transactions。这点可以支撑其上的MegaStore

副本位置与负载均衡

Table Server会定期向Master汇报负载状态,如果Master发现某个Table Server负载过重,会自动对其进行负载均衡。负载由很多指标构成,也考虑了很多现实中遇到的问题(可以在论文中行收balanc了解)。负载均衡就是将该Table Server中的某些子表迁移到其他Table Server上。

首先Bigtable是构建在GFS之上的,所以子表自己不存在replication。子表的迁移实质上是当前Table Server将所有的mutable数据持久化到GFS中,然后挂载到另一个Table Server中(有点像Shared Disk中的备升主)。理所应当的,迁移的过程中会有短暂的IO停顿。子表迁移前原有的Table Server会对其执行Minor Compaction操作(为了缩短停止服务的时间,可能会有两次Minor Compaction),然后迁移。当然了,发生迁移最频繁的场景我认为还是recovery,因为文中提到了,迁移子表的这种负载均衡策略并不是Silver Bullet,在工程上经常遇到以下问题:

  1. 因为迁移会导致当前子表停止服务一秒以内,因此迁移不能经常操作。
  2. 另外就是系统的负载热点不会一直集中在某点。他会随着时间的推移而变化。

存储

表的分裂与合并

一个table只存在于一个Tablet Server上。但是table太大或太小时需要分裂或合并。

  1. 分裂操作由Tablet Server发起,需要修改元数据。
  2. 合并由Master发起,两个表需要先迁移到同一个Tablet Server,再执行合并。

存储引擎

Tablet Representation

  1. 分为memtable和sstable,数据在sstable中是有序存储的。读取的时候需要按从旧到新的的时间顺序合并SStable和Memtable的数据
  2. 写入memtable前要先持久化redo log,然后将数据写到memtable中
  3. CRUD操作在sstable看来都是一样的,sstable中记录的只是操作,而不是最终的结果,只有到read操作的时候才会合并得到最终的操作
  4. 三种策略:Minor compaction、Merging Compaction、Major Compaction
    1. Minor compaction是指将Memtable转储到GFS中
    2. Merging Compaction和Major Compaction都是将多个sstable合并成一个更大的sstable,区别在于Merging Compaction合并的不完全,其结果可能包含一些删除、增加等操作,而Major Compaction会合并所有的sstable和memtable,生成最终的结果。

垃圾回收

compaction完成之后,原来的SSTable需要被回收掉。Master会定期执行垃圾回收任务。需要注意的是,对sstable执行compaction和修改sstable对应的元数据这两个操作不是原子的。需要注意这里面可能造成的数据不一致问题。

总结

Bigtable构造在GFS之上,其操作不用考虑底层数据的可用性等问题,并且GFS也大大增强了Bigtable的扩展性,让Bigtable的设计者可以不用考虑底层,更专注于上层的优化。但是也存在一些问题:

  1. 由于table只存在于一个Tablet Server上,单个节点显然会有瓶颈。当然我们可以引入诸如分库分表、Shared Disk等概念缓解这个瓶颈。
  2. 整个系统构建在GFS上,并且很多实现依赖GFS的支持。两者之间有一定耦合。如果出现问题排查较为困难。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值