Understanding HBase and BigTable 译文
有时间翻译一下这篇文章。http://jimbojw.com/#understan...
Google BigTable论文可下载:https://ai.google/research/pu...
在学习HBase(Google BigTable 的开源实现)的时候,我们面临的最为困难的部分就是你需要重构你的思路来理解BigTable的概念。
非常不幸的是,在BigTable和HBase名称中出现的table和base这两个单词,很容易让我们与RDBMS(关系型数据库管理系统)中的概念相混淆。
本文旨在从概念维度去描述清楚分布式数据存储系统的含义。我们希望在读完这篇文章之后,你能够更有经验去决定你到底需要的是HBase还是一个传统数据库系统。
术语
幸运的是,在论文Google's BigTable Paper中已经清晰的解释了BigTable是什么。在Data Model章节的第一句话是:
BigTable是一种稀疏的、分布式的、持久化的多维有序字典。
论文继续解释到:
BigTable由索引行、索引列以及时间戳组成,在字典中的每个值都是无解释的字节数组。
在Hadoop wiki的HBaseArchitecture页面中指出:
HBase使用了一种与Bigtable非常相似的数据模型。用户在标记表中存储数据行,数据行中有一个有序的key和任意数量的列。这张表的存储是稀疏的,所以如果用户喜欢的话,甚至可以在同一张表的每行中疯狂的存储差异巨大的列。
上面提到的这些概念似乎很神秘,但其实如果你要是想明白的话这些说法都是有道理的。下面我们就按照顺序讨论一下几个主题:字典、持久化、分布式、有序、多维和稀疏。
相比起试图直接描述一个完成的系统框架,我觉得描述清楚构建系统框架的核心要素更加容易理解。
字典
HBase 和 BigTable的核心是字典。根据你所使用的编程语言背景,你可能更加熟悉与之类似的词语是数组(PHP)、字典(Python)、哈希表(Ruby)和对象(JavaScript)。
维基百科对字典的定义是:由一组关键字和值组成的抽象数据类型,其中每个关键字都关联一个值。
使用JavaScript Object Notation语法,简单的字段示意如下所示:
{ "zzzzz" : "woot", "xyz" : "hello", "aaaab" : "world", "1" : "x", "aaaaa" : "y" }
持久化
持久化意思是说添加到字典中的数据在系统创建或处理完成后永远存在,这个概念和其他各种持久化存储系统并无不同,比如存放在文件系统中的文件一样。
分布式
HBase 和 BigTable 构建在分布式文件系统之上以便底层文件存储能够在独立机器阵列中分布存储。
HBase能够构建在Hadoop's Distributed File System (HDFS)或者Amazon的Simple Storage Service (S3)上,而BigTable能够在Google File System (GFS)中使用。
数据以一种类似于RAID系统的方式在多个参与节点中进行复制。
本文的目标并不关心分布式系统的实现方式,本文要说明的重点是HBase和BigTable是分布式的,提供了一个保护层,如集群中某一节点故障。
有序
不像大多数字典的实现,在HBase/BigTable中键值对保持严格的字典序。即关键字『aaaaa』之后紧挨着『aaaab』,并且与『zzzzz』距离很远。
考虑我们之前的例子,有序的版本看起来是这样的:
{ "1" : "x", "aaaaa" : "y", "aaaab" : "world", "xyz" : "hello", "zzzzz" : "woot" }
由于这些系统常常非常巨大而且是分布式的,有序特征实际上非常重要。相似的关键字的行紧密相邻,当你必须对表进行扫描时,你最感兴趣的条目之间彼此相邻。
那么,选择什么样的行关键字就显得十分重要。举个例子,考虑一个表的关键字是主机名,那么最好的办法就是使用主机名的逆序列出他们,例如使用com.jimbojw.www而不是www.jimbojw.com,以便相同子域的那些行都与父域名称相邻。
继续域名的例子,域名为mail.jimbojw.com的行将与名称为www.jimbojw.com的行紧邻,而不是mail.xyz.com。
在HBase/BigTable中的有序并不意味着值是有序的。除了关键字外并没有任何自动的索引方式,这里的实现就和旧有的字典实现一样。
多维
到目前为止,我们还没有提到任何有关『列』的概念,处理『表』,而不是普通意义上的哈希表。『列』这个词也像是『table』和『base』的概念一样,承载了太多的RDBMS的情感在内。
代替的,我们可以把它理解为一个多维字典——即字典中嵌套字典。在上面JSON示例中增加一维:
{ "1" : { "A" : "x", "B" : "z" }, "aaaaa" : { "A" : "y", "B" : "w" }, "aaaab" : { "A" : "world", "B" : "ocean" }, "xyz" : { "A" : "hello", "B" : "there" }, "zzzzz" : { "A" : "woot", "B" : "1337" } }
在上面的例子里,你注意到每个key都指向含有两个key:A和B的字典。从这里开始,我们将顶级的键值对称之为行。在BigTable/HBase概念中,A和B被称之为『列簇』。
表创建时列簇即被指定,之后不可能或者很难被修改。增加新的列簇也十分昂贵,因此最为明智的做法是在最开始就指定好列簇。
幸运的是,列簇可以有任意列,由『标识符』或『标签』指定。下面是JSON示例的子集,其中包含列标识符:
{ // ... "aaaaa" : { "A" : { "foo" : "y", "bar" : "d" }, "B" : { "" : "w" } }, "aaaab" : { "A" : { "foo" : "world", "bar" : "domination" }, "B" : { "" : "ocean" } }, // ... }
注意表示的两个行,A列簇有两个列:foo和bar,B列簇仅有一个列,它的标识符是空字符。
当向HBase/BigTable查询数据时,你必须提供全部列名,形式为:family:qualifier。对于这个例子,以上例子有三个列子:A:foo,A:bar和B:。
注意,尽管列簇是静态的,列本身不是,考虑下面展开的行:
{ // ... "zzzzz" : { "A" : { "catch_phrase" : "woot", } } }
在这个示例中,行zzzzz有一列『A:catch_phrase』。由于每一行都有任意数目不同的列,没有内建的方式查询所有行中的所有列的列表。为了获得信息,你需要做一次全表扫描。但是你可以查询所有列簇的列表,因为他们是不可变的。
在HBase/BigTable中的最后一维是时间。所有数据要么使用整型时间戳,要么使用自定义的整型数据去标识版本。客户端在插入数据时可以指定时间戳。
使用任意整型时间戳的例子:
{ // ... "aaaaa" : { "A" : { "foo" : { 15 : "y", 4 : "m" }, "bar" : { 15 : "d", } }, "B" : { "" : { 6 : "w" 3 : "o" 1 : "w" } } }, // ... }
每个列簇都有自己的规则规定了一个单元格最多能有多少个版本,在不给定时间戳的情况下,应用将请求被给定单元格数据。通常情况下,HBase/BigTable将返回最近的版本(即时间戳的值最大),因为它是按照时间逆序存储的。
如果应用程序请求给定时间戳的给定行,HBase将返回时间戳等于或小于给定时间戳的单元格数据。
使用我们想象的HBase表,请求"aaaaa"/"A:foo"返回"y",请求"aaaaa"/"A:foo"/10返回"m",请求"aaaaa"/"A:foo"/2返回空结果。
稀疏
最后一点是稀疏。上面提到过了,在每个列簇中可以有任意数量的列,或者没有。另一种类型的稀疏是基于行的间隙,这仅仅意味着键之间可能存在间隙。
当然,如果你以HBase/BigTable中基于字典的概念考虑的话就很好理解,而非RDBMS中相似的概念。
结语
希望本文能够帮助你从概念上理解HBase数据模型感觉是什么。
就像总结说的一样,我期待着你的想法、评论和建议。