Hbase分析报告

Hbase分析报告

本文基于环境hadoop-0.16.4 和 hbase-0.1.3 编写

Hbase是一个分散式开源资料库,基于Hadoop分散式文件系统,模仿并提供了基于Google文件系统的Bigtable资料库的所有功能。

Hbaes的目标是处理非常庞大的表,可以用普通的电脑处理超过10亿行资料,并且有数百万列元素组成的资料表。

Hbase可以直接使用本地文件系统或者Hadoop作爲资料存储方式,不过爲了提高资料可靠性和系统的健壮性,发挥Hbase处理大资料量等功能,需要使用Hadoop作爲文件系统,那麽我们就先要了解Hadoop文件系统的基本特性和原理,才能更好地理解Hbase的工作方式。

Hadoop文件系统

Hadoop文件系统是一个能够相容普通硬体环境的分散式文件系统, 和现有的分散式文件系统不同的地方是Hadoop更注重容错性和相容廉价的硬体设备,这样做是爲了用很小的预算甚至直接利用现有机器就实现大流量和大资料量的读取。

Hadoop 使用了POSIX的设计来实现对文件系统文件流的读取。HDFS(Hadoop FileSystem)原来是Apache Nutch搜索引擎(从Lucene发展而来)开发的一个部分,后来独立出来作爲一个Apache子专案。

Hadoop的假设与目标

1、 硬体出错,Hadoop假设硬体出错是一种正常的情况,而不是异常,爲的就是在硬体出错的情况下尽量保证资料完整性,HDFS设计的目标是在成百上千台伺服器中存储资料,并且可以快速检测出硬体错误和快速进行资料的自动恢复。

2、 流资料读写,不同于普通的文件系统,Hadoop是爲了程式批量处理资料而设计的,而不是与用户的交互或者随机读写,所以POSIX对程式增加了许多硬性限制,程式必须使用流读取来提高资料吞吐率。

3、 大资料集,HDFS上面一个典型的文件一般是用GB或者TB计算的,而且一个数百台机器组成的集群里面可以支援过千万这样的文件。

4、 简单的文件模型,HDFS上面的文件模型十分简单,就是一次写入多次读取的模型,文件一旦创建,写入并关闭了,之后就再也不会被改变了,只能被读取,这种模型刚好符合搜索引擎的需求,以后可能会实现追加写入资料这样的功能。

5、 强大的跨平台相容性,由于是基于java的实现,无论是硬体平台或者是软体平台要求都不高,只要是jdk支援的平台都可以相容。

Hadoop体系结构

目录节点(NameNode)和资料节点(DataNodes)

Hadoop文件系统是主从架构,一个Hadoop文件系统由唯一一个目录节点和数个资料节点组成。

Hadoop文件系统对外表现爲一个普通的文件系统,用户可以用档案名去存储和访问文件,而实际上文件是被分成不同的资料块,这些资料块就是存储在资料节点上面。

目录节点是集群里面的主节点,负责档案名的维护管理,也是用户端访问文件的入口。档案名的维护包括文件和目录的创建、删除、重命名等。同时也管理资料块和资料节点的映射关系,用户端需要访问目录节点才能知道一个文件的所有资料块都保存在哪些资料节点上。

资料节点一般就是集群里面的一台机器,负责资料的存储和读取。在写入时,由目录节点分配资料块的保存,然后用户端直接写到对应的资料节点。在读取时,当用户端从目录节点获得资料块的映射关系后,就会直接到对应的资料节点读取资料。资料节点也要根据目录节点的命令创建、删除资料块,和冗余复制。

一个典型的Hadoop文件系统集群部署,是由一台性能较好的机器运行目录节点,而集群里面的其他机器每台上面运行一个资料节点。当然一个机器可以运行任意多个资料节点,甚至目录节点和资料节点一起运行,不过这种模式在正式的应用部署中很少使用。

唯一的目录节点的设计大大简化了整个体系结构,目录节点负责Hadoop文件系统里面所有元资料的仲裁和存储。这样的设计使资料不会脱离目录节点的控制。

Hbase分析报告_第1张图片

Hadoop文件系统命名空间

Hadoop文件系统使用的是传统的分级文件体系,用户端程式可以创建目录并且在目录里面保存文件,类似与现在一般的文件系统。Hadoop允许用户创建、删除文件,在目录间转移文件,重命名文件等,但是还没有实现磁片配额和文件访问许可权等功能,也不支援文件的硬连接和软连接(快捷方式),这些功能在短期内不会实现。

目录节点负责存储和管理整个文件系统的命名空间,应用程式可以指定某一个文件需要在Hadoop文件系统中冗余多少份,这个在Hadoop中称爲冗余因素,保存在目录节点里面。

Hadoop存储原理

冗余数据保存

Hadoop文件系统是爲了大文件的可靠保存而设计的,一个文件被划分成一连串的资料块,除了文件的最后一块以外其他所有的资料块都是固定大小的,爲了资料容错性,每一个资料块都会被冗余存储起来,而每个文件的块大小和冗余因素都是可以设置的,程式可以设置文件的资料块要被复制多少份,而且这个冗余因素除了可以在创建的时候指定,还可以在之后改变。在Hadoop文件系统里面文件只会被写入一次,并且任何时间只会有一个程式在写入这个文件。

目录节点是根据资料块的冗余状况来作出处理决策的,资料节点会定期发送一个存在信号(Heartbeat)和资料块列表给目录节点,存在信号使目录节点认爲该资料节点还是有效的,而资料块列表包括了该资料节点上面的所有资料块编号。

资料存取策略

复制策略是hadoop文件系统最核心的部分,对读写性能影响很大,hadoop和其他分散式文件系统的最大区别就是可以调整冗余数据的位置,这个特性需要很多时间去优化和调整。

一、资料存放

目前hadoop采用以机柜爲基础的资料存放策略,这样做的目的是提高资料可靠性和充分利用网路带宽。当前具体实现了的策略只是这个方向的尝试,hadoop短期的研究目标之一就是在实际産品环境中观察系统读写的行爲,测试性能和研究更深入的规则。

一个大的hadoop集群经常横跨多个机柜,而不同机柜之间的资料通讯同经过交换机或者路由,所以同一个机柜中不同机器的通讯带宽是比不同机柜之间机器通讯时候的大。

Hadoop提供了一个api来决定资料机所属的机柜id,当文件系统啓动的时候,资料机就把自己所属的机柜id发给目录机,然后目录机管理这些分组。

Hadoop默认是每个资料机都是在不同的机柜上面,这种方法没有做任何性能优化,但是也有不少优点:

1、 资料可靠性是最高的。因爲这样可以防止机柜出错的时候资料丢失。

2、 在读取资料的时候充分利用不同机柜之间的带宽。

3、 而且这个策略可以很容易的完成负载平衡和错误处理。

缺点就是写入资料的时候并不能完全利用同一机柜里面机器的带宽。

在默认的配置下,hadoop的冗余复制因数是3,意思就是每一块文件资料一共有3个地方存放,hadoop目前的存放策略是其中两份放在同一个rack id的不同机器上面,另外一个放在不同rack id的机器上面,简单来说就是1/3的冗余数据在一个机柜里面,2/3的冗余数据在另外一个机柜里面,这样既可以防止机柜异常时候的资料恢复,又可以提高读写性能。

上面所说的策略目前还是在测试优化阶段。

二、资料读取

资料读取策略,根据前面所说的资料存放策略,资料读取的时候,用户端也有api确定自己的机柜id,读取的时候,如果有块资料和用户端的机柜id一样,就优先选择该资料节点,用户端直接和资料节点建立连接,读取资料。如果没有,就随机选取一个资料节点。

三、资料复制

主要是在资料写入和资料恢复的时候发生,资料复制是使用流水线复制的策略。

当用户端要在hadoop上面写一个文件,首先它先把这个文件写在本地,然后对文件进行分块,默认64m一块,每块资料都对hadoop目录伺服器请求,目录伺服器选择一个资料机列表,返回给用户端,然后用户端就把资料写入第一台资料机,并且把列表传给资料机,当资料机接收到4k资料的时候,写入本地并且发起连接到下一台资料机,把这个4k传过去,形成一条流水线。当最后文件写完的时候,资料复制也同时完成,这个就是流水线处理的优势。

通讯协定

hadoop的通讯协定基本是在tcp/ip的基础上开发的,用户端使用ClientProtocol和目录伺服器通讯,资料机使用DatanodeProtocol和目录伺服器通讯,而目录伺服器一般只是应答用户端和资料机的请求,不会主动发起通讯。

资料错误和异常

hadoop文件系统的主要目标就是在硬体出错的时候保证资料的完整性,它把磁片错误作爲肯定会出现的情况来对待,而不是异常。一般资料存储中出现的错误有几种,分别是目录伺服器错误,资料机错误,和网路传输异常。

1、 资料机出错,每个资料机会定时发送一个心跳资讯给目录伺服器,表明自己仍然存活,网路异常可能会导致一部分资料机无法和目录伺服器通讯,这时候目录伺服器收不到心跳资讯,就认爲这个资料机已经死机,从有效io列表中清除,而该资料机上面的所有资料块也会标记爲不可读。这个时候某些资料块的冗余份数有可能就低于它的冗余因数了,目录伺服器会定期检查每一个资料块,看看它是否需要进行资料冗余复制。

2、 出现资料异常,由于网路传输和磁片出错的原因,从资料机读取的资料有可能出现异常,用户端实现对资料块的校验,用md5和sha1进行校验,用户端在创建文件的时候,会对每一个文件块进行资讯摘录,并把这些资讯写入到同一个路径的隐藏文件里面。当用户端读取文件的时候,会先读取该资讯文件,然后对每个读取的资料块进行校验,如果校验出错,用户端就会请求到另外一个资料机读取该文件块,并且报告给目录伺服器这个文件块有错误,目录伺服器就会定期检查,并且重新复制这个块。

3、 目录伺服器出错,FsImage和Editlog是目录伺服器上面两个最核心的资料结构,如果其中一个文件出错的话,会造成目录伺服器不起作用,由于这两个文件如此重要,所以目录伺服器上面可以设置多个备份档案和辅助伺服器,当这两个文件有改变的时候,目录伺服器就会发起同步操作,虽然这样增加了系统的负担,但是在目前这个架构上面爲了实现资料的可靠性,这个同步操作是非常必要的。

Hadoop文件系统尚未实现的功能总结:

1、 文件追加写入,这个功能近期内不会实现,没有这个功能会引起当文件尚未关闭的时候,资料伺服器死机或者目录伺服器死机,会引起文件文件丢失,并且不可后续恢复写入。

2、 系统快照,一个全系统的快照功能,如果没有这个功能就不能实现文件系统的回滚操作。

3、 集群负载均衡,均衡策略暂时没有实现,有几个策略十分有用,比如在某台资料机可能磁片过低的时候,把该资料机上面的一些资料转移到还有很多空间剩余的资料机上;当某个文件突然被大量读写的时候,动态增加该文件的冗余因数,并且资料块复制到更多的资料机上面,以提高读取性能。

4、 文件系统的用户许可权,这个也是近期内不会实现的了。

5、访问许可权,现在是无限制访问的,没有访问许可权控制。

Hadoop文件系统性能分析

由于没办法建立大型的Hadoop文件系统,只能节选一些网上的性能分析,以表示一二。

1、 和Kosmos Filesystem的比较,Kosmos Filesystem也是一个类似Google 文件系统的具体实现,所以和Hadoop具有比较的意义。KFS是用c++编写的,在代码执行效率上面比java好不少。

资料插入测试:

测试环境:

· 1 1.8GHz Dual-core Opteron Processor 2210 

· 4 GB RAM 

· 4 7200 RPM SATA drives (mounted JBOD) 

测试使用Hypertable,这也是一个类似Google bigtable的具体实现,可以使用KFS和HDFS作爲文件系统,在插入测试后,表格含有75,274,825个资料单元,每一个键值是7位元组大小,每一个资料是15位元组大小。

测试结果:KFS基本大幅度胜出。

HDFS (no flush) 

 Elapsed time:  170.66 s
Avg value size:  15.25 bytes
 Avg key size:  7.10 bytes
   Throughput:  1792158.60 bytes/s
 Total inserts:  14825279
   Throughput:  86869.79 inserts/s

 Elapsed time:  167.44 s
Avg value size:  15.26 bytes
 Avg key size:  7.11 bytes
   Throughput:  1871062.70 bytes/s
 Total inserts:  15185349
   Throughput:  90690.84 inserts/s

 Elapsed time:  179.91 s
Avg value size:  15.20 bytes
 Avg key size:  7.03 bytes
   Throughput:  1737888.10 bytes/s
 Total inserts:  15208310
   Throughput:  84532.68 inserts/s

 Elapsed time:  169.57 s
Avg value size:  15.22 bytes
 Avg key size:  7.11 bytes
   Throughput:  1831688.52 bytes/s
 Total inserts:  15080926
   Throughput:  88937.45 inserts/s

KFS (no flush) 

 Elapsed time:  125.51 s
Avg value size:  15.25 bytes
 Avg key size:  7.10 bytes
   Throughput:  2436864.83 bytes/s
 Total inserts:  14825279
   Throughput:  118120.09 inserts/s

 Elapsed time:  126.25 s
Avg value size:  15.26 bytes
 Avg key size:  7.11 bytes
   Throughput:  2481447.59 bytes/s
 Total inserts:  15185349
   Throughput:  120276.33 inserts/s

 Elapsed time:  135.51 s
Avg value size:  15.20 bytes
 Avg key size:  7.03 bytes
   Throughput:  2307335.26 bytes/s
 Total inserts:  15208310
   Throughput:  112231.19 inserts/s

 Elapsed time:  127.66 s
Avg value size:  15.22 bytes
 Avg key size:  7.11 bytes
   Throughput:  2433069.68 bytes/s
 Total inserts:  15080926
   Throughput:  118137.45 inserts/s

2、 Hadoop读取测试,与本地文件系统比较

使用hadoop自带的FileBench程式,写入两个1g大小的文件,第一个是位元组流文件,随机生成,第二个是字元文件,随机字典生成。下面是本地文件系统和hadoop文件系统的比较,由于集群是在极端条件下测试,目录伺服器在广州网通机房,两台资料伺服器一台在北京电信机房,一台在北京网通机房,所以测试的瓶颈基本在网路传输,估计在局域网中表现应该好很多。

本地文件系统测试:

java -classpath hadoop-0.16.4-test.jar:hadoop-0.16.5-dev-core.jar:lib/commons-logging-api-1.0.4.jar:lib/log4j-1.2.13.jar:lib/commons-logging-1.0.4.jar:lib/commons-cli-2.0-SNAPSHOT.jar org.apache.hadoop.io.FileBench -dir /home/ssmax/test -nolzo -nozip

DIR: file:/home/ssmax/test

W SEQ_PLN: 42 seconds

W TXT_PLN: 31 seconds

R SEQ_PLN: 25 seconds

R TXT_PLN: 21 seconds

第一行是流文件写入,第二行是文字档案写入,第三行是流文件读取,第四行是文字档案读取。

Hadoop文件系统测试:

java -classpath build/hadoop-0.16.5-dev-test.jar:hadoop-0.16.5-dev-core.jar:lib/commons-logging-api-1.0.4.jar:lib/log4j-1.2.13.jar:lib/commons-logging-1.0.4.jar:lib/commons-cli-2.0-SNAPSHOT.jar org.apache.hadoop.io.FileBench -dir "hdfs://218.107.63.238:9000/user/ssmax" -now -nolzo -nozip

DIR: hdfs://218.107.63.238:9000/user/ssmax

W SEQ_PLN: 437 seconds

W TXT_PLN: 439 seconds

R SEQ_PLN: > 15分钟

R TXT_PLN: > 15 分钟

由于测试用户端上行比下行快很多,所以读取的时候很慢,超过了可以接受的时间,如果在资料机做读操作,读取速度会大大提高。

java -classpath hadoop-0.16.5-dev-test.jar:hadoop-0.16.5-dev-core.jar:lib/commons-logging-api-1.0.4.jar:lib/log4j-1.2.13.jar:lib/commons-logging-1.0.4.jar:lib/commons-cli-2.0-SNAPSHOT.jar org.apache.hadoop.io.FileBench -dir "hdfs://218.107.63.238:9000/user/ssmax" -now -nolzo -nozip    DIR: hdfs://218.107.63.238:9000/user/ssmax

R SEQ_PLN: 80 seconds

R TXT_PLN: 63 seconds

所以得出结论就是rack id的配置十分重要,需要区分机柜,传输的瓶颈主要在网路。

上面就是关于Hadoop文件系统的原理和测试,Hbase可以通过配置使用本地文件系统或者Hadoop文件系统。而测试的过程中也发现了一个更成熟的组合,也是开源专案的Hypertable和KFS,这两个也是类似Bigtable和GFS的实现,主要是使用c++实现的,这里先记录一下,以后再做研究。

Hypertable作者语:Hypertable与HBase的差别是,Hypertable是Bigtable的一个更高性能的实现(InfoQ同样采访了HBase的团队)。我开始的时候跟Jim Kellerman以及Hadoop团队的一些成员一起爲HBase工作。但我们对HBase应该变成什麽样子有不同意见,对实现语言的选择也有不同意见。他们坚持用Java,而我力推C++。于是我就分出来,开始了Hypertable专案。

Hbase分散式资料库

资料模型

Hbase是一个类似Bigtable的分散式资料库,大部分特性和Bigtable一样,是一个稀疏的,长期存储的{存在硬碟上},多维度的,排序的映射表。这张表的索引是行关键字,列关键字和时间戳。每个值是一个不解释的字元阵列,资料都是字串,没类型。

用户在表格中存储资料,每一行都有一个可排序的主键和任意多的列。由于是稀疏存储的,所以同一张表里面的每一行资料都可以有截然不同的列

列名字的格式是"<family>:<label>",都是由字串组成,每一张表有一个family集合,这个集合是固定不变的,相当于表的结构,只能通过改变表结构来改变。但是label值相对于每一行来说都是可以改变的。

Hbase把同一个family里面的资料存储在同一个目录底下,而Hbase的写操作是锁行的,每一行都是一个原子元素,都可以加锁。

所有资料库的更新都有一个时间戳标记,每个更新都是一个新的版本,而hbase会保留一定数量的版本,这个值是可以设定的。用户端可以选择获取距离某个时间最近的版本,或者一次获取所有版本。

概念视图:

一个表可以想象成一个大的映射关系,通过主键,或者主键+时间戳,可以定位一行资料,由于是稀疏资料,所以某些列可以是空白的,下面就是资料的概念视图:

Row Key 

Time Stamp 

Column "contents:" 

Column "anchor:" 

Column "mime:" 

"com.cnn.www" 

t9 

 

"anchor:cnnsi.com" 

"CNN" 

 

t8 

 

"anchor:my.look.ca" 

"CNN.com" 

 

t6 

"<html>..." 

 

 

"text/html" 

t5 

"<html>..." 

 

 

 

t3 

"<html>..." 

 

 

 

上图是一个存储Web网页的范例列表片断。行名是一个反向URL{即com.cnn.www}。contents列族{原文用 family,译爲族,详见列族}存放网页内容,anchor列族存放引用该网页的锚链结文本。CNN的主页被Sports Illustrater{即所谓SI,CNN的王牌体育节目}和MY-look的主页引用,因此该行包含了名叫“anchor:cnnsi.com”和 “anchhor:my.look.ca”的列。每个锚链结只有一个版本{由时间戳标识,如t9,t8};而contents列则有三个版本,分别由时间 戳t3,t5,和t6标识。

物理视图

虽然从概念视图来看每个表格是由很多行组成,但是在物理存储上面,它是按照列来保存的,这点在资料设计和程式开发的时候必须牢记。

上面的概念视图在物理存储的时候应该表现成下面那样子:

Row Key 

Time Stamp 

Column "contents:" 

"com.cnn.www" 

t6 

"<html>..." 

t5 

"<html>..." 

t3 

"<html>..." 

 

Row Key 

Time Stamp 

Column "anchor:" 

"com.cnn.www" 

t9 

"anchor:cnnsi.com" 

"CNN" 

t8 

"anchor:my.look.ca" 

"CNN.com" 

 

Row Key 

Time Stamp 

Column "mime:" 

"com.cnn.www" 

t6 

"text/html" 

需要注意的是在概念视图上面有些列是空白的,这样的列实际上并不会被存储,当请求这些空白的单格的时候,会返回null值

如果在查询的时候不提供时间戳,那麽会返回距离现在最近的那一个版本的资料。因爲在存储的时候,资料会按照时间戳排序。

例子:

一个程式写9行资料,row[0-9],先写入 anchor:foo列,再写入 anchor:bar 列,最后重复写入 anchor:foo列,由于是同一个列族,写到同一个映射文件里面,最后写到文件里面是这个样子的:

row=row0, column=anchor:bar, timestamp=1174184619081

row=row0, column=anchor:foo, timestamp=1174184620720

row=row0, column=anchor:foo, timestamp=1174184617161

row=row1, column=anchor:bar, timestamp=1174184619081

row=row1, column=anchor:foo, timestamp=1174184620721

row=row1, column=anchor:foo, timestamp=1174184617167

row=row2, column=anchor:bar, timestamp=1174184619081

row=row2, column=anchor:foo, timestamp=1174184620724

row=row2, column=anchor:foo, timestamp=1174184617167

row=row3, column=anchor:bar, timestamp=1174184619081

row=row3, column=anchor:foo, timestamp=1174184620724

row=row3, column=anchor:foo, timestamp=1174184617168

row=row4, column=anchor:bar, timestamp=1174184619081

row=row4, column=anchor:foo, timestamp=1174184620724

row=row4, column=anchor:foo, timestamp=1174184617168

row=row5, column=anchor:bar, timestamp=1174184619082

row=row5, column=anchor:foo, timestamp=1174184620725

row=row5, column=anchor:foo, timestamp=1174184617168

row=row6, column=anchor:bar, timestamp=1174184619082

row=row6, column=anchor:foo, timestamp=1174184620725

row=row6, column=anchor:foo, timestamp=1174184617168

row=row7, column=anchor:bar, timestamp=1174184619082

row=row7, column=anchor:foo, timestamp=1174184620725

row=row7, column=anchor:foo, timestamp=1174184617168

row=row8, column=anchor:bar, timestamp=1174184619082

row=row8, column=anchor:foo, timestamp=1174184620725

row=row8, column=anchor:foo, timestamp=1174184617169

row=row9, column=anchor:bar, timestamp=1174184619083

row=row9, column=anchor:foo, timestamp=1174184620725

row=row9, column=anchor:foo, timestamp=1174184617169

其中anchor:foo被保存了两次,由于时间戳不同,是两个不同的版本,而最新的资料排在前面,所以最新那次更新会先被找到。

分散式资料库体系结构

Hbase的伺服器体系结构也是遵从简单的主从伺服器架构,又Hregion伺服器群和HBase Master主伺服器构成。

Hregion伺服器

对用户来说,每个表是一堆资料的集合,靠主键来区分。物理上,一张表是被拆分成多块,每一块就称呼爲一个Hregion。用表名+开始/结束主键,来区分一个Hregion,一个Hregion会保存一个表里面某段连续的资料,从开始主键到结束主键,一张完整的表格是保存在多个Hregion上面的。

所有的资料库资料一般是保存在Hadoop分散式文件系统上面,用户通过一系列Hregion伺服器获取这些资料,一般一台机器上面运行一个Hregion伺服器,而每一个区段Hregion只会被一个Hregion伺服器维护。

当用户需要更新资料的时候,他会被分配到对应的Hregion 伺服器提交修改,这些修改先是被写到Hmemcache 缓存和 伺服器的Hlog文件里面,Hmemcache是在记忆体中的缓存,保存最近更新的资料,Hlog是磁片上面的记录文件,它记录着所有的更新操作,当操作写入Hlog之后,commit()调用才会返回给用户端。

当读取资料的时候,Hregion伺服器会先访问Hmemcache缓存,如果缓存里面没有该资料,才回到Hstores磁片上面寻找,每一个列族都会有一个Hstore集合,每个Hstore集合包含很多HstoreFiles具体文件,这些文件都是B树结构的,方便快速读取。

系统会定期调用HRegion.flushcache() 把缓存里面的内容写到文件中,一般这样会增加一个新的HstoreFile文件,而此时快取记忆体就会被清空,并且写入一个标记到Hlog,表示上面的内容已经被写入到文件中保存。

在啓动的时候,每个Hregion伺服器都会检查自己的Hlog 文件,看看最近一次执行flushcache之后有没有新的更新写入操作。如果没有更新,就表示所有资料都已经更新到文件中了;如果有更新,伺服器就会先把这些更新写入快取记忆体,然后调用flushcache写入到文件。最后伺服器会删除旧的Hlog文件,并开始给用户访问资料。

因此,爲了节省时间可以很少调用flushcache,但是这样会增加记忆体占用,而且在伺服器重啓的时候会延长很多时间。如果可以定期调用flushcache,缓存大小会控制在一个较低的水平,而且Hlog文件也会很快地重构,但是调用flushcache的时候会造成系统负载瞬间增加。

Hlog会被定期回滚,回滚的时候是按照时间备份档案,每当回滚的时候,系统会删除那些已经被写到文件的更新,回滚Hlog只会占用很少的时间,建议经常回滚以减少文件尺寸大小。

每一次调用flushcache会生成一个新的HstoreFile文件,从一个Hstore里面获取一个值都需要访问所有的HstoreFile文件,这样十分耗时,所以我们要定期把这些分散的文件合并到一个大文件里面,HStore.compact()就可以完成这样的工作。这样的合并工作是十分占用资源的,当HstoreFile文件的数量超过一个设定值的时候才会触发。

Google的Bigtable有高级合并和低级合并的区别,但是Hbase没有这个概念,只要记住下面两点就可以了:

1、 flushcache会建立一个新的HstoreFile文件,并把缓存中所有需要更新的资料写到文件里面,flushcache之后,log的重建次数会清零。

2、 compact会把所有HstoreFile文件合并成一个大文件。

3、 和Bigtable不同的是,Hbase每个更新如果是被正确提交了,commit没有返回错误的话,它就一定是被写到记录文件里面了,这样不会造成资料丢失。

两个Hregion可以通过调用HRegion.closeAndMerge()合并成一个新的Hregion,当前版本这个操作是需要两台Hregion都停机才能操作。

当一个Hregion变得太过巨大的时候,超过了设定的阈值,HRegion伺服器会调用HRegion.closeAndSplit(),这个Hregion会被拆分爲两个,并且报告给主伺服器让它决定由哪个Hregion伺服器来存放新的Hregion。这个拆分过程是十分迅速的,因爲两个新的Hregion最初只是保留原来HregionFile文件的引用,而这个时候旧的Hregion会处于停止服务的状态,当新的Hregion合并完成并且把引用删除了以后,旧的Hregion才会删除。

最后总结几点:

1、 用户端以表格的形式读取资料

2、 一张表是被划分成多个Hregion区域

3、 Hregion是被Hregion伺服器管理的,当用户端需要访问某行资料的时候,需要访问对应的Hregion伺服器。

4、 Hregions伺服器里面有三种方式保存资料:

A、 Hmemcache快取记忆体,保留是最新写入的资料

B、 Hlog记录文件,保留的是提交成功了,但未被写入文件的资料

C、 Hstores文件,资料的物理存放形式。

Hbase主伺服器

每个Hregion伺服器都会和Hmaster伺服器通讯,Hmaster的主要任务就是要告诉每个Hregion伺服器它要维护哪些Hregion。

Hmaster伺服器会和每个Hregion伺服器保持一个长连接。如果这个连接超时或者断开,会导致:

A、 Hregion伺服器自动重啓。

B、 Hmaster认爲Hregion已经死机,同时把它负责的Hregion分配到其他Hregion伺服器。

和Google的Bigtable不同的是,当Bigtable的TabletServer和主伺服器通讯中断的情况下,它仍然能提供服务。而Hbase不能这麽做,因爲Hbase没有Bigtable那样额外的加锁系统,Bigtable是由主伺服器管理TabletServer,同时加锁伺服器提供资料访问的,而Hbase只有唯一一个接入点,就是Hmaster伺服器。

当一个新的Hregion伺服器登陆到Hmaster伺服器,Hmaster会告诉它先等待分配资料。而当一个Hregion死机的时候,Hmaster会把它负责的Hregion标记爲未分配,然后把它们分配到其他Hregion伺服器。

元资料表

之前我们说过Hregion是按照表名和主键范围区分的,由于主键范围是连续的,所以一般用开始主键就可以表达出来。

但是如果只要开始主键还是不够的,因爲我们有合并和分割操作,如果正好在执行这些操作的过程中出现死机,那麽就可能存在多份表名和开始主键一样的资料,这个就要通过Hbase的元资料资讯来区分哪一份才是正确的资料档案了,爲了区分这样的情况,每个Hregion都有一个'regionId'来标识它的唯一性。

所以一个Hregion的表达符最后是 表名+开始主键+唯一id(tablename + startkey + regionId)

举个例子:hbaserepository,w-nk5YNZ8TBb2uWFIRJo7V==,6890601455914043877

我们可以用这个识别符来区分不同的Hregion,这些资料就称呼爲元资料,而元资料本身也是被保存在Hregion里面的,我们称呼这个表爲元资料表,里面保存的就是Hregion识别字和实际Hregion伺服器的映射关系。

元资料表本身也会增长,并且可能被分割爲几个Hregion,爲了定位这些Hregion,有一个根资料表(ROOT table),保存了所有元资料表的位置,而根资料表是不能被分割的,永远之存在一个Hregion。

在Hbase啓动的时候,主伺服器先去扫描根资料表,因爲这个表只会有一个Hregion,所以这个Hregion的名字是被写死的。当然要把根资料表分配到一个Hregion伺服器需要一定的时间。

当根资料表被分配好之后,主伺服器就会去扫描根资料表,获取元资料表的名字和位置,然后把元资料表分配到不同的Hregion伺服器。

最后就是扫描元资料表,找到所有Hregion区域的资讯,然后把它们分配给不同的Hregion伺服器。

主伺服器在记忆体中保存着当前活跃的Hregion伺服器的资料,因此如果主伺服器死机的话,整个系统也就无法访问了,而伺服器的资讯也没有必要保存到文件里面。

元资料表和根资料表的每一行都包含一个列族,info列族:

1. info:regioninfo 包含了一个串列化的HregionInfo物件。

2. info:server 保存了一个字串,是伺服器位址HServerAddress.toString()

3. info:startcode 一个长整型的数位的字串,是Hregion伺服器啓动的时候传给主伺服器的,让主伺服器决定这个Hregion伺服器的资讯有没有更改。

因此,当一个用户端拿到根资料表位址以后,就没有必要再连接主伺服器了。主伺服器的负载相对就小了很多,它只会处理超时的Hregion伺服器,在啓动的时候扫描根资料表和元资料表,和返回根资料表的Hregion伺服器位址。

因此Hbase的用户端是十分复杂的,它经常需要浏览元资料表和根资料表,在查询表格的时候,如果一个Hregion伺服器死机或者它上面的资料更改了,用户端就会继续重试,用户端保留的映射关系并不会一直正确的。这里的机制还需要进一步完善。

总结:

1、 Hregion伺服器提供Hregion访问,一个Hregion只会保存在一个Hregion伺服器上面。

2、 Hregion会注册到主伺服器上面。

3、 如果主伺服器死机,那麽整个系统都会无效。

4、 当前的Hregion伺服器列表只有主伺服器知道。

5、 Hregion区域和Hregion伺服器的对应关系保存在两个特别的Hregion里面,它们像其他Hregion一样被分配到不同的伺服器。

6、 根资料表是最特别的一个表,主伺服器永远知道它的位置(在程式中写死)

7、 用户端需要自己浏览这些表,来找到资料在哪里。


Hbase和传统关联资料库的对比分析

Hbase是大大不同于以前的关联资料库,它是按照Bigtable来开发的,套用一个Bigtable的定义就是:

A Bigtable is a sparse, distributed, persistent multidimensional sorted map. 

Bigtable是一个稀疏的,分布的,持续多维度的排序映射阵列。

Hbase就是这样一个基于列模式的映射资料库,它只能表示很简单的键-资料的映射关系,它大大简化了传统的关联资料库。

1、 资料类型,Hbase只有简单的字串类型,所有类型都是交由用户自己处理,它只保存字串。而关联资料库有丰富的类型选择和存储方式。

2、 资料操作,Hbase操作只有很简单的插入、查询、删除、清空等,表和表之间是分离的,没有复杂的表和表之间的关系,所以也不能也没有必要实现表和表之间的关联等操作。而传统的关系资料通常有各种各样的函数、连接操作。

 

Hbase的操作列表:

alter     Alter column family schema;  pass table name and a dictionary

           specifying new column family schema. Dictionaries are described

           below in the GENERAL NOTES section.  Dictionary must include name

           of column family to alter.  For example, to change the 'f1' column

           family in table 't1' from defaults to instead keep a maximum of 5

           cell VERSIONS, do:


           hbase> alter 't1', {NAME => 'f1', VERSIONS => 5}

 

 count     Count the number of rows in a table. This operation may take a LONG

           time (Run '$HADOOP_HOME/bin/hadoop jar hbase.jar rowcount' to run a

           counting mapreduce job). Current count is shown every 1000 rows by

           default. Count interval may be optionally specified. Examples:

 

           hbase> count 't1'

           hbase> count 't1', 100000

 

 create    Create table; pass table name, a dictionary of specifications per

           column family, and optionally a dictionary of table configuration.

           Dictionaries are described below in the GENERAL NOTES section.

           Examples:

 

           hbase> create 't1', {NAME => 'f1', VERSIONS => 5}

           hbase> create 't1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}

           hbase> # The above in shorthand would be the following:

           hbase> create 't1', 'f1', 'f2', 'f3'

           hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, /

             BLOCKCACHE => true}

 describe  Describe the named table: e.g. "hbase> describe 't1'"

 

 delete    Put a delete cell value at specified table/row/column and optionally

           timestamp coordinates.  Deletes must match the deleted cell's

           coordinates exactly.  When scanning, a delete cell suppresses older

           versions. Takes arguments like the 'put' command described below

 

 deleteall Delete all cells in a given row; pass a table name, row, and optionally 

           a column and timestamp

 

 disable   Disable the named table: e.g. "hbase> disable 't1'"

 

 drop      Drop the named table. Table must first be disabled

 

 enable    Enable the named table

 

 exists    Does the named table exist? e.g. "hbase> exists 't1'"

 

 exit      Type "hbase> exit" to leave the HBase Shell

 

 get       Get row or cell contents; pass table name, row, and optionally

           a dictionary of column(s), timestamp and versions.  Examples:

 

           hbase> get 't1', 'r1'

           hbase> get 't1', 'r1', {COLUMN => 'c1'}

           hbase> get 't1', 'r1', {COLUMN => ['c1', 'c2', 'c3']}

           hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1}

           hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS = 4}

 

 list      List all tables in hbase

 

 put       Put a cell 'value' at specified table/row/column and optionally

           timestamp coordinates.  To put a cell value into table 't1' at

           row 'r1' under column 'c1' marked with the time 'ts1', do:

 

           hbase> put 't1', 'r1', 'c1', 'value', ts1

 

 scan      Scan a table; pass table name and optionally an array of column

           names OR an array of column names AND a dictionary of scanner 

           specifications.  If you wish to include scanner specifications, 

           you must also include an array of columns.  Scanner specifications 

           may include one or more of the following: LIMIT, STARTROW, STOPROW,

           or TIMESTAMP.  To scan all members of a column family, leave the 

           qualifier empty as in 'col_family:'.  Examples:

           

           hbase> scan '.META.'

           hbase> scan '.META.', ['info:regioninfo']

           hbase> scan 't1', ['c1', 'c2'], {LIMIT => 10, STARTROW => 'xyz'}

           

 version   Output this HBase version

3、 存储模式,Hbase是基于列存储的,每个列族都有几个文件保存,不同列族的文件是分离的。传统的关联资料库是基于表格结构和行模式保存的。

4、 资料维护,Hbase的更新正确来说应该不叫更新,而且一个主键或者列对应的新的版本,而它旧有的版本仍然会保留,所以它实际上是插入了新的资料,而不是传统关联资料库里面的替换修改。

5、 可伸缩性,Hbase和Bigtable这类分散式资料库就是直接爲了这个目的开发出来的,能够轻易的增加或者减少(在硬体错误的时候)硬体数量,而且对错误的相容性比较高。而传统的关联资料库通常需要增加中间层才能实现类似的功能。

当前的关联资料库基本都是从上世纪70年代发展而来的,它们基本都有一下的体系特点:

1、 面向磁片存储和索引结构

2、 多线程访问

3、 基于锁的同步访问机制

4、 基于log记录的恢复机制

而Bigtable和Hbase之类基于列模式的分散式资料库,更适应海量存储和互联网应用的需求,灵活的分散式架构可以使其利用廉价的硬体设备就组建一个大的资料仓库,而互联网应用就是以字元爲基础的,Bigtable和Hbase就针对这些应用而开发出来的资料库。

由于其中的时间戳特性,Bigtable和Hbase与生俱来就特别适合于开发wiki、archiveorg之类的服务,而Hbase直接就是作爲一个搜索引擎的一部分被开发出来的。

Bigtable的应用案例:

Google各个産品应用里面的大表:

1、 Google Analytics 网站流量分析(analytics.google.com)

这个服务主要提供给网站管理员两个资料,一个就是独立访问者的数量(cookie判定),另外一个就是页面浏览量(PageView),网站管理员只要在每一个需要统计的页面加上google提供javascript代码,就可以每天在后台看到相关的统计资讯了。

这个服务的资料保存主要由两个打表实现:

第一个是原始点击表,记录了用户点击页面的原始资料,这个表的列包括:网站名称,url和用户点击时间、ip等资料,按用户点击时间排序,大小控制在200TB左右,定期需要做压缩备份等操作。

第二个表就是统计资料表,这个表是从原始点击表中计算而来,定期运行批量计算任务生成资料(使用Map/Reduce程式),这个表大概在20TB左右。

2、 Google Earth 地图(maps.google.com)

这个服务包括网页版的google地图和用户端版的google地球。用户通过这些服务,能选择不同的解析度浏览地图、卫星照片等资料。

这个系统主要包括一个资料处理表,和一系列的资料服务表(用户读取时候用)。

原始的图片资讯通过程式批量输入到资料处理表,形成格式化资料。这个资料处理表的每一行表示了物理地图上面的每一块,而键值的命名确保这些地理块是连续的,由于地理的资讯很多,所以有很多列族,基本上每个列族都有图片资料,多列族确保资料是稀疏的,单个存储文件不会太大。后台处理程式定期处理这些资料,把它们整理并录入资料服务表,并清空处理过的原始资料。

资料服务表主要由一个索引表和数个资料表组成,索引表保证了用户请求资料的时候不需要遍历所有资料表。

3、 网路历史记录(www.google.com/psearch)

主要功能:

· 查看并搜索您过去曾访问过的网页,包括 Google 的搜索记录。 

· 查找有关网路活动的搜索趋势,如最常访问的网站和热门搜索等。 

· 根据您搜索的内容以及曾访问过的网站,获取更具个性化的搜索结果。 

这个服务中的网路历史记录是需要安装Google工具栏并在浏览器中啓用才能搜集资料。

这个服务把每个用户的资料保存在同一个大表里面,每个用户有一个唯一的用户id,而每种类型的操作(搜索关键字、浏览网页等)都有一个不同列族,用户搜索记录是通过后台程式从搜索引擎端批量生成并插入的,而网页浏览记录是通过用户的Google工具栏定期上传资料并插入的。

这个服务一开始是设计成在用户端保存个人资料备份的方式,最新改进了以后是按照不同地区的用户再建立多个大表集群,让用户可以就近访问,加快传输速度。

爲了保证用户之间的共用不会占用太多资源,我们爲每个用户加上了简单的配额机制,分别在用户端和大表集群上面实现。

结论:

Hbase就是一个分散式存储的简单键值对应的表。

因爲Hbase的结构与传统关联资料库大爲不同,所以到目前爲止,基于它的应用都是直接从头开始就使用Hbase设计的,还没有见到过从关联资料库转移到Hbase的应用,而且它们之间的api差距太大,而Hbase基于JDBC的驱动开发还是遥遥无期。

而且最重要的一点是当前hbase还是处于开发阶段,所有api都没有稳定,比如伺服器上面装的时候是hbase-0.1.3,一个月后版本发展到hbase-0.2.0,所有API基本都改变了,连HQL的语法都重新定义了一次,而且升级hbase要同时升级hadoop,这样升级所需要的维护工作量太大。

当然,据闻Hbase和Hypertable的目的就是在web应用市场上面取代Mysql。


资料库读写性能分析

1、 单机类比集群测试

测试环境:

由3台伺服器组成的hadoop集群组成分散式文件系统

由一台单独的机器单机类比Hbase集群

由一台机器单机测试Mysql

测试规模:50万条记录以上,单线程、多线程测试

测试结果:

 

 

HBase

Mysql

单线程

插入 100 条记录

155 ms / 154ms

243 ms / 198ms

插入 1000 条记录

740 ms / 884ms

1506 ms / 1554ms

插入 10000 条记录

8304ms/ 6610ms

14110ms/ 12839m

插入 100000 条记录

43090ms /64715ms

108082ms /110664ms

读取 500000左右的条记录

640 ms / 721ms

2779 ms / 2794ms?

100线程

插入 100 条记录

5929ms / 3825ms / 4134ms

15352ms / 12912ms / 12853ms

插入 1000 条记录

35684ms / 52677ms / 34208ms 

135839ms / 161711ms / 119909ms

读取 500000左右的条记录

最快的 1104 ms/最慢的 110897 ms

如果不加limit,资料库连接超时

1000线程

插入 100 条记录

325907ms / 322465ms / 342163ms

17455ms / 18953ms / 15169ms

读取500000左右的条记录

最快的 717 ms

如果不加limit,资料库连接超时


HBase不同于一般的关联资料库,它是一个适合于非结构化资料存储的资料库.另一个不同的是HBase基于列的而不是基于行的模式(栏位内容都是char).
上面两种 特性,导致HBase的资料表结构非常松散,栏位内容单一,表与表之前没有任何关联,正因爲这样在查询的时候效率非常高

HBase资料表的性能选项:
MAX_VERSIONS:每一个单元保存多少版本的资料(默认是3)
MAX_LENGTH:每个单元中的版本能够保存多少位元组的资料(默认位元组数是32位元有符号整数最大值)
COMPRESSION:资料压缩,有BLOCK压缩和RECORD压缩
IN_MEMORY:将这个列组装载资料到记忆体,加快读写速度,缺点耗费记忆体和干预HDFS的备份
BLOOMFILTER:如果这个列组支援布隆筛检程式(BLOOMFILTER),那麽在记忆体中有个索引来快速地判断要查找的列是否存在这个行中,减少磁片IO操作.如
果在这个列组你拥有大量的列,每一个列的资料包含的资料非常小,你可能需要在这个列组中应用布隆筛检程式(BLOOMFILTER)

 

2、 列族测试:

测试目标:测试列族增长对性能的影响

测试资料:建表时候定义列族数量,每个列族写入1000位元组资料,读取5000次,随机读取任意一列。

测试结果:

单机集群

列族数量

10

100

500

1000

建表时间

12.3

19.2

45.9

Timeout

每秒写入

164

323

419

Timeout

每秒读取

99

139

122

Timeout

5机器集群

列族数量

10

100

500

1000

建表时间

12.2

18.7

46.4

Timeout

每秒写入

29

153

376

Timeout

每秒读取

119

111

120

Timeout

测试结论:

Hbase建表时间过长,对大列族的时候支援不好

写入速度在多机集群的时候提高较快

 

3、 排序测试

测试目标:Hbase的行排序是根据主键排序,测试动态或者反序插入时候的性能。

测试资料:动态生成字母资料,zzzzz-aaaaa,还有随机插入

测试结果:

单机集群(每秒多少行)

写入行

10,000

100,000

1,000,000

顺序

485

432

334

反序

451

477

354

随机

462

421

334

 

5机集群(每秒多少行)

写入行

10,000

100,000

1,000,000

顺序

488

440

346

反序

522

387

343

随机

468

441

370

 

测试结论:

采用B树存储和写入缓存,写入数量和顺序对速度影响并不大,应该只是cpu占用的不同。

主要瓶颈还是在网路传输速度上。

 

4、 随机读写测试

测试目标:测试大表下的读取性能

测试资料:在一定规模的表中随机读取5000条记录的时间

测试资料:

单机集群(每秒多少行)

规模(行)

10,000

100,000

1,000,000

读取

95

42

24

 

5机集群(每秒多少行)

规模(行)

10,000

100,000

1,000,000

读取

95

34

35

 

测试结论:

读取速度随着表规模的增加而降低,集群模式下面读取表现比较优秀。

估计如果加上资料合并以后集群的读取能力会更加强劲。

总结:

Hypertable和Hbase尚在开发阶段,还不适合産品化,暂时没有对应的産品案例,但是照这个方向发展下去在web应用端有很好的前景。

Hadoop文件系统相对较稳定,可以考虑在新産品中试验。

 

参考文献:

The Hadoop Distributed File System: Architecture and Design 

http://hadoop.apache.org/core/docs/r0.16.4/hdfs_design.html

HDFS Under the Hood Presentation 1

http://assets.en.oreilly.com/1/event/12/HDFS%20Under%20the%20Hood%20Presentation%201.pdf

Kosmos File System (KFS) is a New High End Google File System Option

http://highscalability.com/kosmos-file-system-kfs-new-high-end-google-file-system-option

Hadoop HBase Performance Evaluation Introduction

http://www.cs.duke.edu/~kcd/hadoop/kcd-hadoop-report.pdf

Hbase/HbaseArchitecture

http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture

Hbase/DataModel

http://wiki.apache.org/hadoop/Hbase/DataModel

Understanding HBase and BigTable

http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable

The End of an Architectural Era (It's Time for a Complete Rewrite)

http://www.vldb.org/conf/2007/papers/industrial/p1150-stonebraker.pdf

HBase Leads Discuss Hadoop, BigTable and Distributed Databases

http://www.infoq.com/news/2008/04/hbase-interview

Validating the Real-time Performance of Hbase

http://wiki.apache.org/hadoop/Hbase/HbaseRTDS

Hadoop Summit and Data-Intensive Computing Symposium Videos and Slides

http://research.yahoo.com/node/2104

Google Datastore and the shift from a RDBMS 

http://groovie.org/2008/04/13/google-datastore-and-the-shift-from-a-rdbms

One Size Fits All? - Part 2: Benchmarking Results

http://nms.csail.mit.edu/~stavros/pubs/osfa.pdf

Bigtable: A Distributed Storage System for Structured Data

http://labs.google.com/papers/bigtable-osdi06.pdf

Hypertable领导者:Hadoop和分散式资料库

http://www.builder.com.cn/2008/0506/847804.shtml


你可能感兴趣的:(hadoop,集群,测试,Google,hbase,存储)