论文题目:BigTable: A Distributed Storage System for Structured Data
中文翻译参考:
Google Bigtable (中文版)dblab.xmu.edu.cn![5a8ba80482d54b4fe03112efaddad565.png](https://img-blog.csdnimg.cn/img_convert/5a8ba80482d54b4fe03112efaddad565.png)
文章架构:
- 第 2 部分详细描述了数据模型;
- 第 3 部分大概介绍了用户 API;
- 第 4 部分简要介绍了 BigTable 所依赖的 Google 底层基础设施;
- 第 5 部分描述了 BigTable 的实施方法;
- 第 6 部分描述了我们针对 BigTable 做的性能改进;
- 第 7 部分提供了 BigTable 的性能衡量方法;
- 第 8 部分给出了几个实例来介绍 Google 如何使用 BigTable;
- 第 9 部分介绍了我们在设计和支持 BigTable 过程中得到的经验教训;
- 第 10 部分介绍相关工作;
- 第 11 部分给出结论。
以下筛选出重点,并进行简要的分析。
第2部分-DataModel
BigTable的具体实现方式:一个稀疏的、分布的、永久的多维排序图。
采用行键(row key)、列键(column key)和时间戳(timestamp)对图进行索引。
(row: string, column string, time:int64)→string 【键值对】
Rows:
一个表中的行键,可以为任意字符,长度64KB,依据字典顺序进行维护。
特别的,在一个表中行区间(Tablet)是动态划分的。行区间是负载均衡和数据分发的基本单位。所以较短行区间的读取比较高效。(例如在同一域名下的网页将会被安排到相同的行区间中,使最后的检索速度加快。)
Column Families:
多个列键组成“列家族”。一行数据一般具有多个维度的参数,但是设计数据标签的时候要求列尽可能少。
BigTable中列键采用下面的语法命名:family:qualifier。family可以理解为一个特定列组的名称,一般必须有字段。qualifier相当于是该列组下的具体分类标识符。
从文献中可以看出,如果某一列的qualifier没有命名,那么该单元可以存放多个相同数据类型的数据。如果qualifier有命名,那么应独自占用一列。显然不同的的qualifier的数据类型不同。
此外,数据的访问与控制都是基于列家族的内容的进行操作的。
Timestamps:
Timestamps作为时间戳,给每一个数据添加一个独一无二的时间标签,精确到毫秒。这一点对于相同数据的多个版本特别有用。
第3部分-API
API部分主要是介绍BigTable具有的增删改等等功能。
需要注意的是,BigTable的采用的原子更新操作。可以将多个相关联的动作合起来一起操作。为后期Hive等其他的开发提供借鉴意义。
第4部分-基于Google的基础设施
BigTable使用分布式Google文件系统(GFS)来存储日志文件和数据文件。
存储的文件格式为Google SSTable文件格式。一个SSTable提供一个持久的、排序的、不可变的、从键到值的映射。一个SSTable的大小是64KB(大小可配)。
块的索引在SSTable的结尾,用来快速定位块的位置。
当一个SSTable打开时,块索引就会先被读取进入内存。利用二分查找找到合适的块地址,最终才从磁盘读取相应的块。当然如果SSTable足够小可以读取进去内存,那么BigTable就会直接将该SSTable读取进去内存操作。
同时,BigTable还依赖Chubby(一个高可用、持久性的分布式锁服务)。Chubby使用Paxos算法来保持一致性。
第5部分-具体实施方法
BigTable包括三个主要的功能组件:1)库函数、2)一个主服务器、3)多个Tablet服务器。
其中:库函数的主要负责服务器与客户端的通信。主服务器负责Tablet服务器的负载均衡,维护GFS的垃圾回收机制,表、列的创建等。每个Tablet服务器一般存放10~1000个Tablet,每个Tablet默认尺寸大小为100~200MB。
这里显然采用的是主从模式,一般来说会对主服务器带来很大的负载。论文中特意强调,实际设计时客户端并不是直接从主服务器读取数据,而是直接从Tablet服务器上读取数据,所以主服务器的负载较小。个人的观点是可能在客户端中存放了各个Tablet索引的元数据,从而减轻了主服务器的负载压力。Hadoop的1.X也是使用主从模型。
1、Tablet定位方法
Tablet的位置通过维护元数据表实现,每个元数据表分别记录了对应的表的位置。元数据表存放在分布式锁服务Chubby中,保证了数据的一致性。上述操作采用三层结构实现。
由于客户端不会直接与主服务器进行交互,所以客户端函数库会缓存Tablet位置信息。客户端的Tablet位置信息保存在缓存中,速度快。同时,定期更新Tablet位置信息。
2、Tablet的分配
主服务器根据全局的负载情况分别Tablet到对应的Tablet服务器。BigTable使用Chubby来跟中Tablet服务器。主服务器见识对应的Chubby上的目录,来发现Tablet服务器。
显然主服务器把Tablet的位置信息转交到Chubby上,这样可以更专注的进行负载均衡,表、列创建等其他事情。
3、Tablet的持久化存储与恢复
Tablet的持久化存储在GFS中。为了恢复一个Tablet,
第6部分-提升性能
6.1、Locality groups
客户端把多个列家族一起分组到一个locality group中,而每一个locality group大多独占一个SSTable。为了实现更高效的读操作,不被一起访问的列家族分割到不同的locality group中,反之亦然。
特别地,对于一些小的、常被访问的数据量(SSTable中)可以设置在内存保存。
6.2、压缩
客户端可以决定是否对某个SSTable进行压缩,通过压缩可以节省空间。而且是针对每一个块进行单独压缩,这样的话在解压缩时,只需要对小部分数据进行解压。
压缩算法采用“两段自定义压缩模式”。第一遍采用Bentley and Mcllroy模式,在大窗口下对长的、公共的字符串进行压缩。第二遍采用一个快速的压缩算法,已16KB为数据量窗口寻找重复的数据。
特别地,由于相邻行中存储的时相关信息,所以两种方法的压缩速度和效率都很快。编码在100~200MB/S,解码在400~1000MB/S,空间压缩率10:1。
6.3、读缓存
设置二处缓存机制,Scan缓存与Block缓存。Scan缓存了SSTable中常用的键值对,Block缓存了从GFS当中读取的SSTable块。
6.4、Bloom过滤器
允许客服端为某个特定的locality group中的SSTable创建Bloom过滤器。通过查看对应的Bloom过滤器可以知道该SSTable是否含有指定的“行-列队”的特定数据。
6.5、日志
每个Tablet服务器具备一个独立的日志文件,其中包含的时多个不同的Tablet的更新内容。
特别地,为了避免日志的重复读写,以键(表、行名称、日志顺序号)的顺序队日志文件的条目进行排序【支持并行排序】。(好处:一次查询,便可顺序批量读取数据。)
此外,更为更快排序,采用并行排序。为了日志读写正常,采用双线程进行日志记录。
6.6、加速Tablet的恢复
当主服务器根据负载均衡的需要,把一个Tablet从一个Tablet服务器转移另一个Tablet时,源Tablet服务器会对该Tablet进行一个次压缩。在完成压缩过程之后,这个源Tablet服务器就停止提供针对这个Tablet的服务。
6.7、SSTable的不可变性
SSTable一旦写成功之后是不可变更的,Hdfs与其类似。注意,由于读取数据时系统可能需要对mentable数据结构进行读写操作,所以需要每个mentable行具有“copy-on-write”。
7、应用案例
略
8、性能
略
9、经验教训
- 大的分布式系统很容易发生多种错误,不仅是其他分布式系统遇到的网络分割和故障,而且还包括:内存与网络故障、时钟不对齐、机器挂起、依赖的其他系统组件问题、非计划之外的硬件维护。
- 不要盲目开发新的特性,需要根据实际需求进行开发。
- 需要一套合理的自我监控系统,包括监视进程。
- 遵循简单设计原则。