HBase与BigTable的比较(翻译)
知,HBase是Google的BigTable架构的一个开源实现。但是我个人觉得,要做到充分了解下面两点还是有点困难的:
一 HBase涵盖了BigTable规范的哪些部分?
二 HBase与BigTable仍然有哪些区别?
下面我将对这两个系统做些比较。
在做比较之前,我要指出一个事实:HBase是非常接近BigTable论文描述的东西。撇开一些细微的不同,比如HBase 0.20使用ZooKeeper做它的分布式协调服务,HBase已经基本实现了BigTable所有的功能,所以我下面的篇幅重点落在它们细微的区别上,当然也可以说是HBase小组正在努力改进的地方上。
比较范围
本文比较的是基于七年前发表的论文(OSDI’06)所描叙的Google BigTable系统,该系统从2005年开始运作。就在论文发表的2006年末到2007年初,作为Hadoop的子项目的HBase也产生了。在那时,HBase的版本是0.15.0. 如今大约2年过去了,Hadoop 0.20.1和HBase 0.20.2都已发布,你当然希望有一些真正的改进。要知道我所比较的是一篇14页的技术论文和一个从头到脚都一览无余的开源项目。所以下面的比较内容里关于HBase怎么做的讲得比较多点。
在文章的结尾,我也会讨论一些BigTable的如今的新功能,以及HBase跟它们比较如何。好,我们就从术语开始。
术语
有少数几个不同的术语被两个系统用来描述同样的事物。最显著的莫过于HBase中的regions和BigTable中的tablet。自然地,它们各自把一连串的行(Rows)切分交给许多Region server或者tablet server管理。
特性比较
接下来的就是特性比较列表,列表中是BigTable跟HBase的特性比较。有的是一些实现细节,有的是可配置的选项等。让人感到有困惑的是,将这些特性分类很难。
?
?
特性BigTableHBase
说明
读?/?写?/?修改的原子性支持,每行支持,每行因为?BigTable?不像关系型数据库,所以不支持事务。最 接近事务的就是让对每行数据访问具有原子性。?HBase?同样实现了”行锁”的?API?,让用户访问数据时给一行或 者几行数据加锁。
词典顺序的行排序支持支持所有行都按照词典顺序排序数据块支持支持支持在数据存储文件中,数据是由更小的数据块构成的。这使从大的存储文件读取数据更快。数据块的大小是可 配置的,典型配置是?64K?。
数据块压缩支持,按Column Family
支持,按Column Family
Google?使用?BMDiff?和?Zippy?做两步处理。?BMDiff?工作得很好是因为存储文件中相邻的?key-value?对的内容经常非常相似。因为数据支持多个版本,几个版本的内容会被排序然后被存在一起,它们之间有很 多相同的内容。或者?row key?也会被用这样的方式处理,比如如果用?URL?来作为row key?,而这些?URL?来自统一个网站,那么?row key?也会有很多相似之 处。?Zippy?使用的是改进的?LZW?算法。?HBase?使用的是?Java?支持的标准的?GZip?,以及一点点?GPL licensed LZO?格式支持。?Hadoop?也有想使用?BMDiff?和?Zippy?的征兆。
Column Family?数量限制最多几百小于?100
理论上行数和列数是无限的,可是列族(?column family?)却不是。这个只是设计上的一些折中考率?.
Column Famil命名格式可打印可打印HBase?这样做的主要原因是?Column Famil?的名称会被作为文件 系统中的目录名称
Qualifier?命名的格式任意任意任意的字节数组Key/Value?对的格式任意任意任意的字节数组访问控制支持无BigTable?支持?column family?级别的访问控制。?HBase?暂不支持
Cell?多版本支持支持多版本支持是基于时间戳。?
版本数目限制可以基于?cloumn family?级别自 由配置
自定义时间戳支持支持两个系统都支持用户设定时间戳,如果用户不指定,则 使用当前时间作为时间戳。数据?TTL支持支持除了数据可以有多个版本,用户还可制定?TTL?(?time-to-live?),当数据到期后会被清除
批量写入支持支持都支持批量表操作值计数器支持支持两者都可使用特定的列作为原子计数器。?HBase?实现是:当计数器的值要增长时,它必须获得行锁。
行过滤器支持支持两者都支持扫描行时支持行过滤器客户端脚本执行支持不支持BigTable?使用?Sawzall?使客户端可以处理存储的数据。
MapReduce支持支持支持两者都有方便的工具类让?MapReduce Job?扫描 表。
底层文件系统GFSHDFS,S3, S3N, EBSBigTable?工作在?GFS?之上,?HBase?可以使用任何文件系统,只要有该文件系统的代理或者驱动即可。
存储文件格式SSTableHFile块索引在文件最后在文件最后两者都有相似的块结构化的存储文件格式,并且块索引 被放在文件的最后内存映射支持不支持BigTable?可以让存储文件直接映射到内存。
锁服务ChubbyZooKeeperZooKeeper?被?HBase?用来协调任务并非当成锁服务。总体说来,?HBase使用?ZooKeeper?达到了?BigTable?使用?Chubby?的效果,只有语义有点细微区别。
单个?Master是不是HBase?近来支持多个?Master?。多个?Master?是”热”待命模式工作,它们都侦听?ZooKeeper?上的?Master?节点。
Tablet/Region数目10-100010-1000两个系统都推荐每个?Region server?分配相同数目的?region?。当然这决定于很多因素,由于两个系统都使用普通电脑,出于负载考虑,它们推荐相同的数目
Tablet/Region大小100-200MB256MB在两个系统中,单个?Region?大小是可配置的,在?HBase?中,默认大小为256MB
Root?位置1st META / Chubby-ROOT- / ZooKeeperHBase?会使用一个只有单个?Region?的自身表来存储?Root?表。二者启动时都会把?Root region?所在机器的 地址放到?ZooKeeper?或者?Chubby?中。
客户端Region?信息缓存支持不支持二者客户端都支持?Region?位置信息缓存并且有相应的机制去除 过时的缓存和更新缓存
Meta?预读支持不支持(?)BigTable?的一个设计就是会预读超过?1?个?Meta Region?信息并将之放入客户端缓存。
Region?事件记录支持支持Region?相关事件(切分,分配,再分配)都会记录在?Meta?表中
存储位置分组(?Locality Groups?)支持不支持这不是很确定,但是看起来?BigTable?中的任何东西都有 个位置分组的属相。如果多个列族的位置分组相同,那么它们将被存放在一起,并且拥有相同的配置参数。单个列族就可能是一个拥有一个成员的位置分组。HBase?不支持这种选项,并将不同的列族分开存储。
完全内存Column Family?存储支持支持这是为需要高速存取小表准备的KeyValue?缓存支持不支持缓存热点?Cell?数据
数据块缓存支持支持数据块从存储文件读入到在可配置的缓存中布隆过滤器(Bloom Filters)支持支持这些过滤器会消耗一些内存,但是可以快速检查一个指定的?cell?是否在一个?Region Server?上存在
Write-Ahead Log (WAL)支持支持每个?Region Server?都会记录被它管理的所有?Region?上的数据改动
Secondary Log支持不支持出于性能考虑,一旦?WAL?性能下降,?BigTable?还有别的?log?可以使用
忽略?Write-Ahead Log?支持在大量数据导入时,?HBase?的客户端可以选择忽略?WAL
快速?Region切分支持支持切分?region?是快速的,因为切分出来的子?region?暂时还会去读取原存储文件直到一个?compaction?将数据写入?region?的自有的存储文件
?
BigTable 新特性
OSDI’06 BigTable论文发表已有几年,BigTable当然也有改进。杰夫.迪恩—一个在Google的家伙在近来的一些演讲和演示中提到了 BigTable的新特性。我们就来瞧瞧部分新特性吧。
?
?
?
?
特性BigTableHBase说明客户端隔离支持不支持BigTable?可以内在地被用来服务很多单独的客户端,并且使它们的数据隔离不互相影响
协同处理(Coprocessors?)
支持暂不支持BigTable?在?region?中运行的代码可以随着?region?的被切分,代码也被会切分到新的?region?上运行。
数据错误安全支持不支持BigTable?使用?CRC?校验码确认数据是否被安全写入。?HBase?没有这个特性,问题是:?Hadoop?是否会包含这个特性?
数据中心间数据复制支持暂不支持HBase?的一个?issue?:?HBASE-1295?就是关于这个特性的。
?
?
变化和差异
上面讨论的一些特性比较可以看出有些特性差异并不是可以简单归结为”是或否”类的问题,对这类问题我将在下面单独探讨。
锁服务
下面的来自BigTable论文
?
BigTable用Chubby来完成一些不同的任务:保证在任何时候只有一个活动的Master;存储BigTable引导区地址;发现tablet server以及在table server死亡时做善后工作;存储BigTable的schema信息(每个表的列族信息);存储访问控制列表。如果Chubby在一段较长的时候变得不可用,那么BigTable系统就会变得不可用。?BigTable如何使用Chubby跟HBase如何使用ZooKeeper有很多异曲同工之处。但有一个区别就是:HBase并不把 Schema信息存储在ZooKeeper中。它们都非常依赖锁服务的正常运作。根据我自身的经验以及我阅读HBase邮件列表所得到的,我们经常低估当 ZooKeeper无法取得足够的资源去作出实时回应时的后果。宁可让ZooKeeper集群运行在相对较老旧的但是什么别的事都不干的机器上,而不是运 行在已被Hadoop或者HBase进程搞得不堪重负的机器上。一旦你的ZooKeeper没有足够的资源提供服务,就会引发多米诺骨式的效 应,HBase将会挂掉—包括master节点。
?
更新:在跟ZooKeeper开发小组讨论后,我想指出的是这并不真正意义上是ZooKeeper的一个问题。因为如果运行ZooKeeper的机 器负荷很重,那么存取ZooKeeper上的资源很可能会超时。在这种情形下,HBase的Region Server甚至Master可能会认为协调服务已经坏了,它们就会让自己停工关闭。??帕特里克?.?亨特已经 通过邮件和发帖对此作出回应。你可以读他的邮件或者帖子,然后检查自己的ZooKeeper?是否有能力处理负荷。我个人建议是 将?ZooKeeper?集群跟?HBase?集群分开。你可以把?ZooKeeper?集群运行在一组空闲的稍微有点过时 但是性能还相当不错的机器上。这样你可以单独监控?ZooKeeper?集群和?HBase?集群中的机器,而不必有以下的烦恼:当一个机器的?CPU?负荷?100%?的 时候,你搞不清楚这个负荷究竟来自哪个进程或者有什么后果。
?
另外一个重要区别是:?ZooKeeper?并不是一个像?Chubby?一样的锁服务系统,但是目前为止,这并不是?HBase?所 关心的。?ZooKeeer提供一个分布式的协调服务,让?HBase?可以选举出?Master?节 点。它也可以提供用以表示状态或者某个动作需要的信号量。当?Chubby生成一个锁文件来表示一个?tablet?活动的,与此相对应的一个?Region server?会在?ZooKeeper?中生成一个节点来表示自己的存在。这个节点创建以后,只要?ZooKeeper?不挂,它会一直存在。在?BigTable?中,当一个?tablet server?的锁文件被删除时就表示与这个?tablet server?的租约失效。在?HBase?中,因为?ZooKeeper?相对少点限制的架构,这种行为会被处理得有所不同。它们只是语义上有所差别,并不意味着谁优谁劣,仅仅有所不同而已。
?
在Chubby中,第一层级的文件包含着根tablet的位置信息。根tablet包含着一个特别的名叫METADATA(元数据)表的所有的tablet的位置信息。每个METADATA的tablet包含着一组用户表的tablet的位置信息。根tablet仅仅是METADATA表中第一个tablet,但是它被特别的看待—它从不会被切分,这是为了保证tablet的位置层级不会超过3层。?就如上面所说的,在HBase中,根region是一个只有单个region的表。要说有什么区别的话,那就是根region并不是meta表中的第一个 不可切分的region。它们是相同的功能,只是实现上有差别。
?
METADATA表存储着tablet的位置信息,跟在起始row key和末尾row key以及表名之后。?HBase的做法有点不同,它也会为每个region存储起始的row key和末尾row key,但是末尾的row key并不是属于当前的region的,它会是另一个region的起始row key.
?
Master的行为
?
为了侦测一个tablet server是否不再为它的tablets服务,master会定期地查看每个tablet server锁文件的状态。当一个tablet server报告它丢失了锁文件,或者master经过几次尝试后未能联系上tablet server, master就会尝试在tablet server的锁文件上获得独占锁。如果master可以获得这个锁,还有Chubby是良好工作的,这时如果tablet server已经死亡或者已经把错误上报给Chubby,这时master就可以确定该tablet server不可能恢复,于是会删除它的锁文件。一旦一个tablet server的锁文件被删除,本来被该tablet server服务的tablets会被移到没被分配的tablets集合中去。为了保证master和Chubby之间网络畅通,只要master的Chubby session过期,master将会自杀。?直到0.20.2版本,HBase的行为都相当不同。Region server利用heartbeat协议给master定期报告,master接到报告就知道region server还活着。
?
Master启动
?
Master启动有以下步骤:(1)Master从Chubby中获取唯一的master锁,用来防止多个master的初始化(2)Master 扫描Chubby中的server目录找到活动的server.(3) Master跟多个活动的tablet server通讯,收集tablets的分配情况(4)Master扫描METADATA表得知所有的tablets集合。(4)Master如果发现有tablet并没有分配到tablet server,它会将之放入未被分配tablets集合,这样这个tablet就会被认为是可以被分配的。?就如我上面提到的,HBase实际上会等待所有的region server上报它负责的region情况。当然,它也会扫描.META. 表去了解有哪些region以及它们是分配到哪些region server上了。
ZooKeeper仅仅用来发布-ROOT- region所在的region server的地址。
?
Tablet/Region切分
?
在切分通知被丢失时(因为tablet server挂了或者master挂了)的情况下,当master要求tablet server装载被切分的那个tablet时,master会发现新的tablet. Tablet server会将此次切分通知master,因为它会发现在METADATA中找到的tablet只是master要求它装载的tablet的一部分。?
Master节点会单独利用.META.表去发现一个region去切分,但是切分事件消息被丢失的情况。Master会按往常一样扫描.META.去发 现,哪些region并没有被分配。一旦发现没有被分配的region,master会用默认的策略将之分配到一个region server上。
?
压紧(Compaction)
随着写动作的执行,内存表的大小会不断增长。当内存表的容量到达一个临界点时,内存表将被冻结,一个新的内存表将会被创建,被冻结的内存表将被会转换成SSTable并被写入到GFS中。这种minor compaction操作有2个目的:1降低tablet server的内存用量,2当一个tablet server死而复生从commit log读取数据时,减少数据总量。当压紧动作发生时,认可继续执行读写操作。?HBase也有相应的操作,不过被命名为”flush”。对应BigTable的”minor compaction”,HBase会把最近生成的很多较小的存储文件重写为一个包含较多数据的大文件。
?
…我们将限制此类文件的数目,方式是定期地在后台执行合并压紧操作。合并压紧操作就是读取几个SSTable和内存表的内容,然后写到一个新的SSTable中去。一旦合并压紧操作完成,老的SSTable和内存表就将可被丢弃。?
这个动作跟HBase中的”minor compaction”差不多。
?
读取所有的SSTable,重新写成一个SSTable的合并压紧操作被称为是major compaction?相应的在HBase中,一个major compaction就是把所有的存储文件(HFile)重写成一个新的文件。
?
文件不可修改
要知道文件一旦被写入将不能修改,BigTable有如下的假定:
唯一可以被修改的数据结构就是读写都可以的内存表(memtable)。为了避免同时读写内存表的冲突,我们在写内存表的数据行的时候,会先复制一个副本,这样读写就可以并行处理了。?我相信HBase中的做法跟此类似,但不是很确定。可以确定是,根据HDFS的架构,文件一旦被写入就是不能被修改的。
我唯一的建议就是你自己去阅读BigTable的相关资料以形成自己的见解。这篇帖子的灵感来自一个念头:BigTable究竟实现了哪些特 性,HBase涵盖了BigTable的哪些特性?写这片帖子的困难毫无疑问是我们对BigTable的细节所知不多。但是关于它的论文数量–即便是 2006年的论文列表都给人留下难以磨灭的印象。HBase作为一个开源项目,而且项目的贡献者是为数不多的有着全职工作的人,现在虽不能说跟 BigTable相提并论,但怎么说都我都认为现在的成果是一个巨大的成就。看看0.21和0.22的路线图,将来它们之间不大的差距将会变得更加缩小。
?