Google BigTable
1.什么是BigTable
BigTable是一个分布式的存储系统,可以支持PB级别的数据。对于不同数据量和延迟需求的应用都有灵活、高性能的解决方案。
BigTable和数据库有很多相似之处。Bigtable不支持完整的关系数据模型,而是提供了可以对数据部署和格式动态控制的数据模型。
BigTable使用行和列索引,行名和列名可以是任意字符串。
BigTable用灵活的模式参数,允许用户选择从磁盘还是内存获取数据。
2.数据模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2vMdNizg-1646977304526)(C:\Users\LXC\AppData\Roaming\Typora\typora-user-images\image-20220311133542064.png)]
一个BigTable是一个**稀疏的、分布式的、永久的多维排序图。**有行健、列键、时间戳三层图索引。
row:string,column:string,time:int64 推出string
Webtable:很多项目使用的,庞大的网页集合和网页相关信息。
在Webtabe中,使用URL作为行健,网页元素的类别作为列键,例如网页内容存在contents:column中。
2.1 行
任意字符串,**每行的数据读写都是原子操作,无论有多少列。**这样便于用户知道同一行的并发更新的发生。
按字典序维护,表的航区间是动态划分的,没有固定大小限制,每一行叫一个Tablet,是负载均衡和数据分配(一个tablet一条数据)的基本单位。
对URL进行反转可以提高存储效率、压缩效率(后面会讲),可以把同一领域(类型)的数据临近存储。
2.2 列
列键又叫做家族,是基本的**访问控制单元。**同一家族的数据属于同一数据类型,也就意味着同一家族的数据可以压缩存储。
列在操作作中很少发生变化。
列名:family:qualifier。例如网页的language列存储了网页所用语言的信息。网页的另一个列家族anchor,每一个元素qualifier是引用网站的名称。
2.3 时间戳
每个单元格会有相同数据的多个版本,版本按时间降序存储,保证获取的第一个是传新版本。
以列为单位,可以选择保存每个单元格最近的n各版本或最近n天的版本。
例如,为contents列设置时间戳,数值就是这个网页被抓取的时间,只保留网页最新的三个版本的内容。
3. API
删除创建表、列;改变簇、列家族的元数据,例如访问控制权限。
增删表中的值,按行查询,,对表的子集遍历(游标)。
//Apply函数调用
//open the table
Table *T = OpenOrDie("/bigtable/web/webtable");
//write a new anchor and delete an old anchor
RowMultitation ri(T,"com.cnn.www");
ri.Set("anchor:www.c-span.org","CNN");
ri.Delete("anchor:www.abc.com");
Operation op;
Apply(&op,&r1);
//apply操作会执行原子更新操作:向www.cnn.com添加一个anchor并删除一个
利用Scanner遍历某行的所有anchor,可以限制扫描到的anchor符合正则表达式anchor:*.cnn.com或十天内的。
Scanner scanner(T);
ScanStream *stream;
stream = scanner.FetchColumnFamily("anchor");
stream->SetReturnAllVersions();
scanner.LookUp("com.cnn.www");
for(;!stream.Done();stream.next()){
print(%s%s%lld%sn),
scanner.Rowname(),stream->ColumnName(),stream->MicroTimeStamp(),stream.value();}
*其他功能
1.支持单行事务
允许某一行数据进行原子的读写改。
2.单元格可以作为整数计数器
3.支持服务器在地址空间内执行客户端的脚本
4.建立块结构
1.BigTable的一个会在一个共享机器池内工作,与其他分布式应用同时运行。所有应用共享共同的硬件机器。
2.**BigTable的内部文件格式:SSTabel。**每一个SSTable是一个持续的,有序的,不可变的,从键到值的映射。
3.BigTable提供了查找指定键和在一个键区间内查找所有键值对的操作。
4.每个SSTable内部包换一个块序列。每个块64KB,但块大小是可配置的。
5.SSTable末尾存储自己内部的块索引,用于快速定位块的位置。
6.每当SSTable被打开,其内部的块索引就会被装入内存用于快速读取。
7.一次查询操作只需要进行一次磁盘扫描(二分查找)。
8.完全读入一个SSTable到内存,就可以在执行查找操作时不需要在访问磁盘。
9.每个SSTable有一个块序列,也就是一些块,末尾是这些块的索引。
4.1 高可用性和持久性的分布式锁:Chubby
一个chubby服务有五个副本,主副本对外提供服务,当大部分副本可用(能相互通信时),才对外提供服务。
chubby有一个命名空间,内部有目录和小文件,每个目录和文件都可以视作一个锁。文件的读写操作都是原子性的。
所以命名空间就是以他名字命名的一块地址空间。
chubby客户端函数库提供了对chubby文件的持久性缓存。每个chubby客户啊短同时维护一个session,是chubby提供服务的证明。如果session在规定时间内得不到更新(心跳消息),这个客户端session就会过期。
此时这个客户端的所有锁都会丢失,同时放弃句柄。
chubby客户端也可以注册/申请对chubby文件的回调服务,用来通知session过期或延长服务。
4.2 Chubby的功能
(1)每个时刻,只有一个chubby副本是活跃的。
(2)存储数据boostrap的位置。
(3)发现tablet服务器和宣告他的死亡。
(4)存储BigTable的模式信息(每个表的列家族信息。
(5)存储访问控制列表。
4.3 BigTable实现功能的三个主要组件
库函数:链接每一个客户端和服务器。
主服务器:唯一
很多Tablet服务器
Tablet服务器(行数据服务器)可以根据负载变化对族中数据进行删除。每个tablet服务器管理一个tablet集合,可以对已经加载的tablet发出读写请求,对大的tablet进行划分。
主服务器把tablet分配到tablet服务器,同时监听tablet服务器的增加和过期,进行负载均衡。主服务器还可以进行模式修改,比如表、列家族的创建。
BigTable包含很多表,每个表又包含很多Tablet,每个tablet包含了某个区域的所有数据。
表会根据情况增加tablet数量。
*单服务器分布式系统的特点
客户端不从主服务器上拿数据,而是从tablet服务器上拿。也不依赖主服务器获取tablet的位置信息。以此减小主服务器的负载。
5.实现
5.1 TabLet的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6b8aRQ8-1646977304529)(C:\Users\LXC\AppData\Roaming\Typora\typora-user-images\image-20220311133622455.png)]
一个类似B+树的三层结构。
第一层次:文件
一个存储在chubby中的文件,包含Tool Table的位置信息。
第二层次:Root Table
存储所有的tablet位置信息,存在一个metadata中。每个metadata表包含很多user tablet的位置信息。
第三层次:METADATA
存储某一行的tablet的位置信息,行健表示的就是tablet表的标识符和最后一行的位置。
第四层次:User Table
客户端函数库负责缓存tablet位置。客户端每次获取tablet位置信息(丢失,错误时),需要向上逐层访问(类似于DNS寻找域名需要到根服务器)进行3/6次查询。
5.2 Tablet 分配
当一个tablet被分配到tablet服务器时,主服务器通过发送tablet负载请求到tablet服务器,主服务器负责调度。
Bigtable通过chubby追踪tablet服务器。每个tablet服务器启动时获得一个排它锁,这个锁用于锁住特定的chubby目录下的唯一文件。主服务器通过监视这个目录来发现tablet服务器。tablet服务器停机时丢失这个锁。
Tablet允许tablet服务器检查锁:如果文件还在,tablet服务器会尽力重新获得锁,否则会立刻自杀(因为不能在提供服务),同时释放锁。
主服务器在tablet服务器失联或反映丢失锁时,会去获得针对这个服务器文件的排他锁。来判断tablet服务器到底是否可用。
5.3 主服务器启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZeAsRcB-1646977304530)(C:\Users\LXC\AppData\Roaming\Typora\typora-user-images\image-20220311133641834.png)]
(1)获取master lock,保证只有一个主服务器。
(2)扫描chubby的服务器目录,了解当前的可用tablet服务器。
(3)和每一个tablet服务器联系,了解tablet服务器上tablet的分布。,没有分配的tablet加入未分配集合。
(4)因为meta table不分配空间是无法扫描的,所以需要先分配root table,再逐层向下扫描,获得所有meta table 和user table的名字和分配情况。
分配完后,只有tablet被创建/删除/合并/分解才会改变tablet集合。
tablet分裂会记录在日志中,读取分裂的tablet时,会调用日志找到所有文件碎片,读取合并。
5.3.1 Tablet服务
一个tablet通过GFS的持久性存储。
最近的更新存放在内存一个叫memtable的排队缓冲区,老的更新存放在一系列SSTable中。
5.3.2 Tablet恢复过程
tablet服务器从metadata级别的表中读取这个tablet的元数据,**元数据包含了一个SSTabel列表,每一项是sstable和redo指针域的集合。(redo指针指向可能包含sstable需要数据的重做日志)。**服务器读入sstable索引到内存,执行这个sstable对应的redo指针后面的更新,重构mentable。
SSTable有重做点日志,通过redo指针指向。
通过执行重做点以后的更新,重构mentable。
5.3.3 Tablet服务器处理读、写请求
5.3.3.1 写操作
先检查写操作是不是完整的,操作者权限是不是符合(从chubby客户端的缓存中的一个chubby文件,里面有写入权限的访问者列表)。写操作提交后,内容插入memtable。
5.3.3.2读操作
类似于读,可以在读操作的时候可以合并一系列SSTable(老的提交的读操作)和memtable(新的读操作)。二者都是字典序的数据结构,便于排序。
合并时不影响正在进行的读写操作。
读操作必须从当前tablet的所有sstable中读数据。
5.4 块压缩
5.4.1 次压缩
mentable尺寸会随着写操作不断增加,当一个memtable达到上限时,会创建新的原来的memtable转化成一个SSTable。
次压缩有两个目标:1、减少tablet服务器的内存使用率,把老的请求保存到硬盘上。2、服务器需要从死亡中恢复时,减少了从重做日志中读取的数据量。
后台还会运行一个合并压缩程序,将一些sstable和memtable合并成一个sstable,删除源文件。
5.4.2 主压缩
把所有的sstable写成一个sstable。**非主压缩产生的sstable可以包含一个删除入口,把被删的数据保存在其他的老的sstable中。主压缩不会保留删除信息和被删除的数据。**BigTable会周期性执行主压缩,这对于存储敏感的服务很重要。
6.改进、完善措施(refinements)
6.1 家族组(locatity groups)
把多个列家族放到一个组内,为每个组单独创建一个sstable,可以实现高效读(利用程序的空间局部性)。例如,WebTable的页元数据(语言,校验码)放在一个组中,网页内容放在一个组中。
通过修改参数,一个组可以直接存放在内存中。
常驻内存(非调入)的家族组的sstable通过被动加载,被加载到tablet服务器的内存中’即应用请求sstabel的数据而数据不在服务器内存中,才会把sstable调入内存。只要sstable被调入内存,这个组的所有家族都可以直接访问而不必访问磁盘。适用于被频繁调用的小量数据。(不占太多内存空间)
6.2 压缩
之前提到了对于多个sstable进行压缩,现在进一步分析。
客户端可以决定要不要对**家族组对应的的一系列sstable进行压缩。**用户可以设定家族组的参数来控制sstable的块大小,以块为单位进行压缩。
以块为单位进行压缩会损失一些空间,但是解压缩时不必对所有文件进行解压。
压缩算法的性能取决于压缩文件的组织格式。例如,WebTable的行存储方式是DNS的倒序,这使得来自同一站点的网页存在相邻的位置,便于压缩算法从一个站点的网页获取相似的内容。好的行组织方式可以取得很好的压缩率。
6.3 缓存
tablet服务器使用两个层次的缓存:scan和block。
6.3.1 scan缓存
高层次缓存,缓存键值对,值是sstable接口,适用于频繁读取相同数据的应用。
6.3.2 block缓存
低层次缓存,缓存了GFS中读取的sstable块,适用于倾向读取与最近读取数据相邻数据的应用(顺序读取/读取一个locatity group中随机列)。
6.4 bloom filter
一种牺牲了准确性,换取时间空间效率的,判断包含关系的数据结构。
不同于链表和树,先把所有元素保存下来,然后检索查找的数据是否在数据结构内,bloom filter采用哈希函数,把一个元素映射到长为m的向量的一个属性上。如果这一位为1,说明在,否则不在。为了减少哈希冲突,对每个元素使用k各哈希函数,只有这些函数对应的位都是1,才说明集合包含这个元素。
优点:只查询不保存元素有很好的安全性(防止肆意修改数据),但是元素越多,误差越大,而且不可删除任一个元素,因为每一位代表的可能不止一个元素,删除一个会对其他数据产生影响。
言归正传,因为读操作需要tablet的所有sstable,对于不在内存的sstable就需要磁盘访问。因此,创建boom filter 关联一个家族组的所有sstable。我们可以查询指定行/列的数据是否包含在sstable中。如果查询的的行/列目前不存在,就不需要访问磁盘(访问磁盘之后告诉内存我也没有)。
6.4.1 事务提交的实现(commit-log implimentation)
如果每一个tablet(行数据)都通过一个单独文件来保存提交的日志,会产生大量并发操作,而且会降低分组提交优化的效率。因此,采用一个统一的物理日志文件来存储不同tablet的提交事务。但是这样会导致故障恢复需要多次遍历提交日志来复现故障tablet服务器的状态。
因此,通过通过(表,行号,日志顺序)的顺序对日志排序,这样把对同一个表(tablet)的操作连续存放。磁盘寻找时据就以批量读取同一个表的数据。这个排序过程在故障恢复开始时进行。
同一个tablet服务器会有两个写线程,只有一个处于工作状态。选择性能较好,不受网络拥堵的那个。
6.4.2 加速tablet恢复
当主服务器把一个tablet从一个服务器转移到另一个服务器,就需要做一次压缩。???????
6.4.3 提高鲁棒性
所有SSTable都是不变的,因此读sstable的数据不需要任何文件系统的同步,针对行的并发控制很高效。而memtable会同时被读和写,因此采用写时拷贝(只有内容变化时,才会复制一份给另一个进程),允许读和写并发执行。
因为sstable是不变的,所以可以移除metadata表中的sstable注册项,标记为垃圾数据。
7.性能测试
建立BigTable族,含有N台tablet服务器,改变N的值可以测试bigtable的可扩展性和性能。Tablet服务器把数据写入到GFS单元格中,*这里采用客户机和tablet服务器数量相等,防止客户端成为瓶颈 。*所有机器位于二层树形交换网络中,机器位于同一个域内(计算机网络的区)。
每台机器运行1个GFS服务器,一部分机器运行1个tablet服务器和1个客户端进程,共有R个行健
7.1 写操作,benchmark
顺序写:行健名0-R-1,把行健分成10N个区间,中央处理器调度到10N个客户端,客户端完成一个区间立刻再分配,防止当前进程状态改变对机器造成影响。
在每个行健下面写一个随机的、单字符字符串,不可压缩,防止出现跨行压缩。
随机写:上述步骤,加上对行健以R为摸进行哈希,均衡负载。
7.2 读操作,benchmark
类比写,顺序读是读取存储在行键下的字符串。
使用scan benchmark(bigtable的api),可以读取域内所有值,只需要一个RPC就可以从tablet服务器读取大量数据。
7.3 测试结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BB4T2CtO-1646977304531)(C:\Users\LXC\AppData\Roaming\Typora\typora-user-images\image-20220311134045233.png)]
7.3.1 单tablet服务器
随机读性能差。随机读操作包括把一个64KB的sstable块通过GFS传输到tablet服务器,用到的只有1000B。
从内存随机读更快,因为不需要从GFS中获取数据块。
随机和顺序写的性能优于读,因为tablet服务器会把到达的写请求追加到日志中,并使用分组提交。这种情况下随即和顺序写没有太大区别,因为请求都写在一个日志中。
顺序度性能优于随机读,因为读取了64KB的数据块,可以为后64个读服务(因为是顺序,一定在一个块里)。
scan读更快,他的开销可以分摊到大量的值中。
7.3.2 scaling,增加服务器数量
服务器数量增长带来吞吐量激增,但是达不到线性,每个服务器的吞吐量会降低(多服务器环境下,负载不均衡)。因为随机读的瓶颈是单个tablet服务器的cpu。
负载均衡算法无法完美解决的原因:1.为了减少tablet迁移的开销,系统有时会禁止负载均衡操作。2.benchmark的负载是动态变化的。
和单处理器一样,随机读的可扩展性是最差的每次读都需要一个64K的块,这会让网路负载达到饱和。
8.Applications(Service)
8.1 Google Analytics
Google analytics 是帮助网站管理者分析网站流量的服务。每当页面被访问,一个js程序就会把访问用户的所有信息记录在谷歌分析中。
例如,网站点击表:网站名;session(类似cookie,存储时间是浏览器关闭时间)的创建时间。这个表保证了访问同一个网站的session是最近的。
再例如,汇总表,通过周期性运行mapreduce作业,获得网页最近的session信息。系统吞吐量受GFS吞吐量限制。
8.2 Google Earth(Google地图专业版)
通过一个表进行数据预处理,另一个表给用户提供服务。预处理管道的表用来存储卫星照片,这个表有70TB的压缩影像数据,只能从磁盘读取,不需要bigtable再压缩了。
每行代表一个地理片段,保证相邻地理地段临近,列表示地段数据,基本一张影像一列,很稀疏。
预处理管道性能取决于Mapreduce的数据转换效率。这个服务系统通过表建立索引,索引表运用到多个tablet服务器上,增加可用性。
8.3 Personalized Search
和之前的差不多,通过再bigtable上使用mapreduce形成用户的profile,利用profile帮助用户生成个性化结果。用户数据被分配到多个bigtable族簇中,早期使用的是客户端副本模式(见GFS文章),现在是复制子系统,一个内嵌到服务端的。
这个表允许多个分组共享,其他分组可以添加新的用户信息到自己分组的列中,因此这个表的列是所有分组的和,很大。因此,bigtable通过配额限制应用的存储空间。
9.Lessons(教训)
1.大的分布式系统会遇到很多问题,不只是网络故障,还有内存故障,时钟扭曲,机器挂起与恢复,其他系统的故障和兼容,GFS配额溢出,硬件维护…因此需要改变其他系统的一些假设,新增功能(例如,RPC机制的checksumming)。
2.没想清楚特性的功能之前不随意添加。
3.合适的系统级别的监视。
4.简单设计
10.相关工作
Boxwood基础框架,bigtable利用boxwood的类似文件系统和数据库,设计需要存储数据的客户端。
)))))))))))))))))))