bigtable 浅析

     Bigtable 是一个用来管理结构化数据的分布式存储系统,具备很好的伸缩性,可以在几千台应用服务器上处理PB数量级数据。谷歌有许多项目都把数据存储在Bigtable中,包括web indexing,Google Earth, and Google Finance. 这些应用对Bigtable的侧重点不一样,可是他们都是海量数据和实时性的应用。尽管需求变化无穷,Bigtable很好的提供了一个灵活多变,高性能额解决方案。
  1. INTRODUCTION
    在不少方面Bigtable都与数据库相似:他们有一样的实现策略。并行数据库和主存数据库都具备高伸缩性和高性能的特色。可是Bigtable提供了一种不一样的接个口。Bigtable不支持彻底的关系数据模型;相反,它给客户端提供了一种简单的数据模型,这种数据模型支持对数据分布和格式的动态控制,而且容许客户端推出底层存储中数据的分布特性。Bigtable的数据可使用任意字符的行列进行索引,Bigtable也把数据看成不可解释的字符串(uninterpreted strings),尽管客户端经常把不一样形式的结构化、半结构化的数据序列化造成这些字符。客户端能够控制经过进行选择模式控制数据的位置。最后一点,调整Bigtable的模式参数能让客户端动态控制是从内存仍是硬盘提供数据。
 
    2.  DATA MODEL
    一个Bigtable 集群是一系列运行Bigtable软件的进程。每个集群都有一组tables。Bigtable中的表是稀疏的、分布式、持久的多维有序map。其数据有三个维度:行、列、时间戳。
(row:string, column:string, time:int64) → string
    咱们称由一个特定行键、列键、时间戳指定的部分为一个单元(cell)。多行组合起来造成负载平衡的基本单元,多列组合起来造成访问控制和资源分配的基本单元。
 
  考虑这样一个具体的列子:一个大量网页和相关信息的集合,该集合会被大量不一样的应用利用。假设咱们称此表为Webtable。在Webtable中,URL为行键,网页的不一样方面成为列键,存储网页的内容。时间戳指的是网页被获取的时间。以下图所示
 
 
    Rows. Bigtable以行键的字典序存储数据,而表中的行键是任意的字符串(目前能达到64KB,尽管对于大部分用户来讲10-100字节就够了)。单行数据的读写是串行的(不管该行数据有多少不一样的列正被读或者写),这种设计使得客户端可以在对某行数据进行并行更新的时候很容易知道系统的行为。换句话说,行是Bigtable控制事务一致性的基本单元,也就是意味着他不支持跨行事务。
    具备连续键值的行组合成 tablets,这是数据分布和负载平衡的基本单元。这样的好处是读取不多的行范围内的数是高效而且通常仅仅须要和少许的机器交互。客户端能够利用这个数据局部性实现高效的数据访问。例如:在Webtable中,相同域名的网页分红一组分布在相邻(contiguous)行,存储的时候把URL的主机部分逆向存储。maps.google.com/index.html会以com.google.maps/index.html做为键存储。把相同域名的网页临近存放使得某些域名和主机分析更加高效。
    Columns. 列键放在一块儿称为列家族,它是访问控制的基本单元。在一个列族中存放的数据一般是相同类型的。在数据用key存储以前必须显式建立列族。在列族建立完成以后,该族任意的列键均可以使用:数据能够在不影响表模式的前提下存储在这样的列键中。咱们的想法是让不一样列族数比较少(最多上百),而且这样的列族在操做过程当中几本不会改变;这种限制控制了共享元数据的大小。可是其对列数是没有任何限制的。
    改变一个表模式可能会删掉全部的列族,在这种状况下,该族任意列键存储的数据都将被删掉。因为Bigtable并不支持跨行事务,若是数据被存储在多行,特定的列键被删除,其对应的数据可能不会被删掉。
    列键是用以下的语法命名的:族:标识符。列族的名字必须是可打印的,可是标识符没有限制。关于Webtable的一个列族例子是网页编写的语言。在语言族中咱们仅使用一个列键和一个空的标识符来存储每一个网页的语言ID。该表另外一个有用的列族是anchor;该族中的每个列键都表明一个anchor。标识符就是所指向网址的名字。单元(cell)则包含与连接相关的文本。
    访问控制以及磁盘内存分配调度都使在列族层次上的。在Webtable例子中,这些控制容许咱们控制几种不一样类型的应用:有些应用新增底层数据,有些则读取底层数据和建立新建的列族,有些仅仅容许访问已存在的数据(甚至因为隐私的缘由,不容许访问全部存在的族的数据)
 
    TimeStamps. 表中不一样单元格能够包含一样数据的不一样版本,版本是经过timestamp索引的。Bigtable的时间戳是64位整数。他们能够被Bigtable隐式赋值,这种状况是“实时的”,精确到毫秒级,或者能够被客户端显式赋值。应用程序必须产生惟一的时间戳来避免冲突。不一样版本的单元格以降序存储,这样最新版本会被最早读取。
    为了简化版本管理,咱们支持两个per-column-family 告诉Bigtable自动进行垃圾版本回收。客户端既能够选择保存最近的几个版本,也能够选择保存足够新的版本(例如,仅保存最近七天写入的)
    在Webtable例子中,咱们能够把时间戳存储在扒取网页的内容中:这列意味着这些网页版本实际扒取的时间。上面描述的垃圾回收机制使得Bigtable仅保存每一个网页的最近三个版本。
 
     3 . API
 
    Bigtable的API提供了建立和删除表和列族的函数。一样也提供了改变集群、表和列族元数据的函数,例如访问控制权限。
    客户端程序能够删除Bigtable中的值或者向Bigtable中写入数据,从单行中检索数据,或者对表中数据子集进行迭代。
 
图2描述了C++代码使用RowMutation抽象进行一系列更新操做。(无关细节略去了)调用Apply执行对Webtable的原子操做:向 www.cnn.com增长了一个anchor,同时删除了一个不一样的anchor。
 
    图3 描述了C++使用一个Scanner抽象对某一个特定row的全部anchor进行迭代。客户机能够在不一样的列族进行迭代,不过也有一些机制来限制scan能够遍历的行、列、时间戳。例如:咱们能够限制让scan仅仅扫描那些匹配正则表达式的列,或者对时间戳进行限制来选择。
    Bigtable支持不一样的特性让用户可以以复杂多变的方式操做数据。首先,Bigtable支持单行事务,这个特性使得对单行数据能够执行原子的读写序列。Bigtable目前还不迟滞跨行事务,尽管其给客户机提供了一个接口能够跨行批量写入。第二,Bigtable容许单元格充当整数计数器。第三,Bigtable支持客户端提供的脚本在服务器地址空间中执行。
 
    4.  BUILDING BLOCKS
    Bigtable 是基于其余几个Google基本结构的。一个Bigtable集群一般在运行各类各样分布式应用程序的共享机器上运行。Bigtable依赖于谷歌的集群管理系统来调度任务,管理资源,监控机器状态,处理异常机器。Bigtable进程与其余应用程序进程共享机器。如图4所示,一个Bigtable服务器可能与MapReduce、应用服务器、GFS服务器等运行在同一个机器
 
上。
    Bigtable 使用GFS来存储日志和数据文件。GFS是一个分布式文件系统,能够保存每一个文件的多个备份以提升可靠性和易用性。
    Bigtable使用Google SSTable不变文件格式存储Bigtable数据文件。一个SSTable提供了一个持久的有序不变的从key到values的map。用户能够经过指定的key查找关联的value,也能够对指定的key范围内数据迭代。每个SSTable都包含了连续的几个块(默认状况下,每一个块64KB,可是大小是能够配置的)块索引(存放在SSTable的最后)是用来定位块的;当SStable打开时索引就被加载进内存。这样查询只须要一次磁盘寻道:首先经过对内存索引进行二分查找找到对应的块。根据实际状况,一个SSTable能够彻底被映射到内存,从而在执行查找和扫描是无需访问硬盘。
    Bigtable依赖一个高易用性、持久化的分布式锁机制——Chubby。一个Chubby服务由五个活动的副本组成,其中一个被选为主要的用来处理请求。当大部分副本都处于运行状态而且相通讯时Chubby处于活动状态,Chubby使用Paxos算法来保证在遇到问题时副本之间的一致性。Chubby还提供了一个由目录和小文件组成的名字空间。每个目录或文件均可以看成锁,读写文件都是原子操做。Chubby客户端库都提供了对Chubby文件的一致映射。每个Chubby客户端都和Chubby服务维持一个会话(session).当客户端session不可以在到期以前续期就会失效,当session失效后,它将失去全部的锁。Chubby客户端能够对文件或目录注册一个回调函数以便在session超期或发生改变时接受通知。
    Bigtable的Chubby能够处理不一样的任务:保证任什么时候候之多只有一个master;存储Bigtable数据的启动位置(见5.1);寻找tablet服务器以及肯定tablet服务器的死亡(见5.2);存储Bigtable模式(见5.5)。若是Chubby服务在一段时间内不可用,Bigtable就会不可用
    5. IMPLEMENTATION
    Bigtable的实现主要包括三哥主要部分:一个连接到每一个客户端的库,一个master服务器,许多tablet服务器。Tablet 服务器可动态添加到集群(或删除)以适应不一样的负载。
    master服务器负责把tablets分配到tablet服务器上,检测tablet服务器的增长和超期,平衡tablet服务器负载,对GFS进行垃圾收集。此外,还能处理模式改变,例如列族的建立和删除。
    每个tablet服务器都管理一组tablets(通常每一个tablet服务器上有10-1000个tablets)。tablet服务器处理对该服务器上tablets的读写请求,也可以将特别大的tablets分红几个。
    像许多单master的分布式存储系统同样,客户端的数据不会移动到master上:客户端直接与tablet服务器进行读写交互。由于Bigtable客户端无需经过master知道tablet的位置信息,大部分client历来都不喝master交互。这样,在实际中,master的负载就会特别小。
    一个Bigtable集群存储了大量的tables。每个表都由一组tablets构成,每个tablet包含一个行范围内的数据。初始状况下,每一个表仅包含一个tablet。随着表大小的增加,它会自动分裂成多个tablets,默认每一个表能够达到1GB。
    尽管,咱们的模型支持任意大小的数据,可是目前的Bigtable的实现还不能很是大的数据。下面的部分江介绍Bigtable实现的细节状况。
    5.1  Tablet Location
    咱们使用一个相似于B+树的三层结构存储位置信息。第一层是存储在Chubby中的文件,该文件包含root tablet的位置信息。root tablet包含一个特殊METADATA table全部teblets的位置信息.每个tablet包含许多用户tablets的位置信息。root tablet和其余tablet有所不一样——历来不会分裂——这种特性使得tablet位置层次结构不会超过三层。
    METADATA表用一行存储一个tablet的位置信息,位置信息包括tablet表标识符的编码和末行号。每个METADATA行大约占用1KB的内存。而每一个METADATA表的上限是128MB,这样咱们三层结构可以处理
2^34的tablets。
    客户端库遍历位置层次结构定位tablets,而且缓存寻找到的tablet的位置。若是client不知道一个tablet的位置,或者它发现它缓存的信息是错误的,那么它将第贵的在位置层次结构中移动。若是客户端缓存是空的,这种寻找算法须要三次来回传递消息,包括一次从Chubby中读取。若是客户端缓存中的信息过期了,这中算法须要6次来回消息传递才能找到某一个tablet,因为过期的缓存项只有在miss的状况下才会发现。尽管tablet位置信息存储在内存中,无需GFS访问,咱们经过客户端库预取tablet位置信息进一步减小这种一般状况下的开销:无论何时客户端读取METADATA表时多读取几个tablet的metadata。
    咱们一样在METADATA表中存储了耳机信息,包括与每个tablet相关的全部事件的日志(如服务器向其提供服务的时间)这种信息对调试和性能分析的做用是很大的。
 
    5.2  Tablet Assignment
        每个tablet一次至多只能分配给一个tablet服务器。master服务器跟踪这些活动的tablet 服务器以及当前正被分配给tablet 服务器的tablet,包括违背分配的tablets.当一个tablet是未分配的,tablet server 有足够的空间容纳一个tablet,master就会经过向tablet server发送一个tablet装载请求给tablet分配服务器。分配只有在一个master的失效备援工做以前tablet装载请求尚未收到:由于tablet server 仅接受当前master的装载请求。所以当一个master服务器发送了一个装载请求,它能够假设这个tablet被赋给了某个tablet服务器知道该服务器死亡,或者该tablet服务器通知master它已经卸载了该tablet.
        Bigtable使用Chubby跟踪这些tablet服务器,当一个tablet服务器启动时,在一个特定的Chubby目录下,对一个惟一名字的文件建立一个排它锁。master监测此目录来发现tablet服务器。当tablet失去排它锁时,就会中止对其上的tablets提供服务。例如:网络中断可能致使服务器失去和Chubby的会话。tablet服务尝试从新获取一文件的排它锁只要它的文件依旧存在。若是它的文件不存在了,服务器将不能提供任何服务,它就会终结它本身。不论何时tablet服务器终止,它将尝试释放本身的锁,以便master能够更快的给它的tablets分配新的服务器。
        master负责检测tablet服务器再也不向tablets提供服务的情形,尽量快的从新分配这些tablets。为了检测一个tablet 服务器再也不向tablet提供服务,master周期性询问每个tablet服务器的锁的状态。若是某个tablet服务器告诉master它丢失了它的锁,或者master几经尝试都不可以到达一个tablet服务器.master就会尝试获取服务器文件排它锁。若是master可以获取该锁,这说明chubby正常而且tablet服务器要么终结了要么不可以到达Chubby.master将会经过删除它的服务器文件保证该tablet服务器永远都不可以再提供服务。一旦一个服务器文件被删除,master就会将以前分配给他的tablets从新变成未分配的。为了确保master和Chubby之间不易受到网络问题的影响,master终结本身当它与Chubby之间的会话插旗。master失败并不会影响tablets分配。
    当一个master被集群管理系统启动时,在改变tablet分配以前,它须要知道当前的tablet分配状况。在启动时,master会执行如下的步骤:
    (1)首先master会在Chubby中获取一个为一个master lock,此锁能够避免并行的master实例
    (2)master扫描Chubby的servers目录发现活动的tablet 服务器
    (3)master与每个活动的tablet server交互发现那些tablets已经分配到每个服务器,更新当前master所了解到的信息(任何以前masters发送的tablet装载请求都会被拒绝)
    (4)master扫描METADATA表知道有哪些tablets.不论何时扫描到一个没有被分配的tablet,master把这个tablet放到未分配tablet集合中,这样能够是得tablet分配更加方便。
        
        一个比较复杂的事情是只有当METADATA tablets已经被分配以后才可以扫描METADATA表。所以,在开始扫描以前(第四步),master把root tablet加到未分配tablet集合中若是在第三步中没有对root tablet进行分配。这会保证root tablet会被分配。由于root tablet包含全部的METADATA表的名字,master在扫描完root tablet以后就会知道全部的METADATA表的名字。
        这些tablets集合只有在表建立或删除的时候、俩个tablets合并成一个更大的tablet,或者一个tablet分裂成俩个更小的tablets时才会改变。master可以跟踪这些改变由于除了最后一个操做,其余操做都是由master初始化的。对于tablets分裂须要特殊对待,由于这个操做是由tablet servers启动的。tablet server 分裂过程是这样的,在METADATA表中记录新的tablet server的信息。在执行分裂以后,tablet server通知master.若是分裂通知丢失(因为tablet server或者master死亡)。master会发现该新的tablet当其要求tablet server加载已经分裂的tablet的时候。tablet server会告知master关于该分裂,由于master发如今METADATA表中的tablet项仅能描述它要求加载tablet的一部分信息。
 
    5.3  Tablet Serving
        tablet的持久化信息被存储在GFS中,如图6所示
 
        更新信息都被提交到一个存储恢复记录的commit log中。最近提交的信息都被存储在内存中的一个叫作memtable的有序缓冲中。一个memtable保存了row-by-row basis的更新,每一行都经过写时复制来保证行层次的一致性。更早的更新存储在一系列的SSTables中(不可改变)
        为了恢复一个tablet,tablet server从METADATA表中读取元数据。这些元素据包括组成tablet和一系列的还原点的SSTables,这些元数据可能包含tablet数据的提交日志的指针。server把SSTables的索引读进内存,经过还原点中更新记录重构memetable。
        当tablet server接受到一个写操做请求,server检查该请求是不是良定义的,发送者是否有权限执行该操做。检查权限是经过Chubby文件中一个容许写的列表(该文件在CHubby客户端缓存中,几乎每次都能命中)。合法的改变都会写入到提交日志中。批量提交小的改变能够提升系统吞吐量。当写操做提交以后,内容就被插入到memtable总。
        当tablet server接收到一个读操做时,一样先检查其形式和权限。
        当tablets正在分裂或者合并的时候读写操做仍然能够继续。当tablets在被压缩时,读写操做仍然能够进行。
    5.4  Compactions
        当执行写操做时,memtable大小增长。当memtable大小达到阈值时memtable就会被冻结,一个新的memtable会被建立,冻结的memtable会被转换成SSTable并被写入到GFS中。这种小型压缩过程有两个目的:减小tablet server的内存消耗,减小在server死亡状况下恢复过程当中须要从commit log中读取的数据量
        每一次微小压缩(minor compaction)都会建立一个新的SSTable。若是这种行为不受约束,读操做可能须要合并任意数量的SSTable中的更新记录。相反,若是咱们限制了这些文件的数目经过在后台周期性执行一个合并压缩。合并压缩读取几个SSTable和memtable的内容,写入到一个新的SStable中。当压缩完成后,输入SSTables和memtable能够被丢弃掉。
        把全部SSTables中的内容写入到一个SSTable的合并操做称之为大的合并。由小型压缩产生的SSTables能够包含特殊的删除项,该删除项能够限制在比较老的SStable中仍然存活着的数据。另外一方面,大的压缩产生的SSTable不包含任何删除信息或数据。Bigtable周期性的检测全部的tablets而且周期性应用大的压缩。这些大的压缩让Bigtable回首被删除数据使用的水资源,同时也能保证让需删除的数据机试从系统中小时,这一点对于存储敏感数据的服务很重要。
        Bigtable读性能得以与GFS的局部性优化。当文件被写入时,GFS尝试把数据的副本放在写者的机器上。当读取GFS文件时,读取的数据来源于最近可用的副本中。所以,在tablet servers与GFS servers共享机器时,tablet servers会压缩到在硬盘上有副本的SStables中,这样能够在处理连续的读请求时快速访问这些SSTables
 
    5.5 模式管理
        BigTable的模式存储在Chubby中。Chubby给Bigtable模式提供了一个搞笑的交流基质,它提供了对整个文件原子写操做和小文件的一致性缓存。列入,假设一个客户端程序想要删除表中的某些列族。master执行访问控制检查,验证结果模式是梁定义的,而后向Chubby中重写相应的模式来安装新的模式。不论何时tablet servers须要决定那些列族存在,仅须要读取Chubby中相应的模式,这在Chubby的客户端缓存中经常是存在的。由于Chubby缓存是一致的,tablet servers 必定可以看到对那个文件的全部改变。
 
 
        参考资料:

    Bigtable: A Distributed Storage System for Structured Data

html

相关文章
相关标签/搜索