1、Hbase 分析报告 本文基于环境 hadoop-0.16.4 和 hbase-0.1.3 编写 Hbase 是一个分布式开源数据库,基于 Hadoop 分布式文件系统,模仿并提供了基于 Google文件系统的 Bigtable 数据库的所有功能。 Hbaes 的目标是处理非常庞大的表,可以用普通的计算机处理 超过 10 亿行数据,并且有数百万列元素组成的数据表。 Hbase 可以直接使用本地文件系统或者 Hadoop 作为数据存储方式,不过为了提高数据可靠性和系统的健壮性,发挥 Hbase 处理大数据量等功能,需要使用 Hadoop 作 为文件系统,那么我们就先要了解 Hadoop 文件系统
2、的基本特性和原理,才能更好地理解 Hbase 的工作方式。 Hadoop 文件系统 Hadoop 文件系统是一个能够兼容普通硬件环境的分布式文件系统, 和现有的分布式文件系统不同的地方是 Hadoop 更注重容错性和 兼容廉价的硬件设备,这样做是为了用很小的预算甚至直接利用现有机器就实现大流量和大数据量的读取。 Hadoop 使用了 POSIX 的设计来实现对 文件系统文件流的读取。 HDFS( Hadoop FileSystem)原来是 Apache Nutch 搜索引擎 (从 Lucene 发展而来) 开发的一个部分,后来独立出来作为一个 Apache 子项目。 Hadoop 的假设与目标
3、 1、 硬件出错, Hadoop 假设硬件出错是一种正常的情况,而不是异常,为的就是在硬件出错的情况下尽量保证数据完整性, HDFS 设计的目标是在成百上千台服务器中存储数据,并且可以快速检测出硬件错误和快速 进行 数据 的 自动恢复。 2、 流数据读写, 不同于普通的文件系统, Hadoop 是为了 程序批量处理数据而设计的,而不是与用户的交互或者随机读写,所以 POSIX 对程序增加了许多硬性限制, 程序必须使用流读取来提高数据吞吐率。 3、 大数据集, HDFS 上面一个典型的文件一般是用 GB 或者 TB 计算的,而且一个 数百台机器组成的 集群里面 可以支持 过千万这样的文件 。 4
4、、 简单的文件模型, HDFS 上面的文件模型十分简单,就是一次写入多次读取的模型, 文件一旦创建,写入并关闭了,之后就再也不会被改变了,只能被读取,这种模型刚好符合搜索引擎的需求,以后可能会实现追加写入数据这样的功能。 5、 强大的跨平台兼容性,由于是基于 java 的实现,无论是硬件平台或者是软件平台要求都不高,只要是 jdk 支持的平台都可以兼容。 Hadoop 体系结构 目录节点 ( NameNode) 和数据节点 ( DataNodes) Hadoop 文件系统是主从架构, 一个 Hadoop 文件系统由唯一一个目录节点和数个数据节点组成。 Hadoop 文件系统对外表现为一个普通的
5、文件系统,用户可以用文件名去存储和访问文件,而实际上文件是被分成不同的数据块,这些数据块就是存储在数据节点上面。 目录节点是集群里面的主节点,负责文件名的维护管理,也是客户端访问文件的入口。 文件名的维护包括文件和目录的创建、删除、重命名等。同时也管理数据块和数据节点的映射关系,客户端需要访问目录节点才能知道一个文件的所有数据块都保 存在哪些数据节点上。 数据节点一般就是集群里面的一台机器,负责数据的存储和读取。 在写入时,由目录节点分配数据块的保存,然后客户端直接写到对应的数据节点。在读取时, 当客户端从目录节点获得数据块的映射关系后,就会直接到对应的数据节点读取数据 。 数据节点也要根据目
6、录节点的命令创建、删除数据块,和冗余复制。 一个典型的 Hadoop 文件系统集群部署,是由一台性能较好的机器运行目录节点,而集群里面的其它机器每台上面运行一个数据节点。当然一个机器可以运行任意多个数据节点,甚至目录节点和数据节点一起运行,不过这种模式在正 式的应用部署中很少使用。 唯一的目录节点的设计大大简化了整个体系结构,目录节点 负责 Hadoop 文件系统里面 所有元数据的仲裁和 存储。 这样的设计使数据不会脱离目录节点的控制。 Hadoop 文件系统命名空间 Hadoop 文件系统使用的是传统的分级文件体系, 客户端程序可以创建目录并且在目录里面保存文件,类似与现 在一般的文件系统。
7、 Hadoop 允许用户创建、删除文件,在目录间转移文件,重命名文件等,但是还没有实现磁盘配额和文件访问权限等功能,也不支持文件的硬连接和软连接(快捷方式),这些功能在短期内不会实现。 目录节点负责存储和管理整个文件系统的命名空间,应用程序可以指定某一个文件需要在Hadoop 文件系统中冗余多少份,这个在 Hadoop 中称为冗余因素, 保存在目录节点里面 。 Hadoop 存储原理 冗余数据保存 Hadoop 文件系统是为了大文件的可靠保存而设计的, 一个文件被划分成一连串的数据块,除了文件的最后一块 以外其它所有的数据块都是固定大小的, 为了数据容错性,每一个数据块都会被冗余存储起来,而每
8、个文件的块大小和冗余因素都是可以设置的,程序可以设置文件的数据块要被复制多少份, 而且这个冗余因素除了可以在创建的时候指定,还可以在之后改变。在 Hadoop 文件系统里面文件只会被写入一次,并且任何时间只会有一个程序在写入这个文件。 目录节点是根据数据块的冗余状况来作出处理决策的, 数据节点会定期发送一个存在信号( Heartbeat)和数据块列表给目录节点,存在信号使目录节点认为该数据节点还是有效的,而数据块列表包括了该数据 节点上面的所有数据块编号。 数据存取 策略 复制策略是 hadoop 文件系统最核心的部分,对读写性能影响很大, hadoop 和其它分布式文件系统的最大区别就是可以
9、调整冗余数据的位置,这个特性需要很多时间去优化和调整。 一、数据存放 目前 hadoop 采用以机柜为基础的数据存放策略,这样做的目的是提高数据可靠性和 充分利用网络带宽。当前具体实现了的策略只是这个方向的尝试, hadoop 短期的研究目标之一就是在实际产品环境中观察系统读写的行为,测试性能和研究更深入的规则。 一个大的 hadoop 集群经常横跨多个机柜,而不同机柜之间的数据通讯同经过交换机或者路由,所以同一个机柜中不同机器的通讯 带宽是比不同机柜之间机器通讯时候的大。 Hadoop 提供了一个 api 来决定数据机所属的机柜 id,当文件系统启动的时候,数据机就把自己所属的机柜 id 发
10、给目录机,然后目录机管理这些分组。 Hadoop 默 认是每个数据机都是在不同的机柜上 面,这种方法没有做任何性能优化, 但是 也有不少优点: 1、 数据可靠性是最高的。因为这样可以防止机柜出错的时候数据丢失。 2、 在读取数据的时候充分利用不同机柜之间的带宽。 3、 而且这个策略可以很容易的完成负载平衡和错误处理。 缺点就是写入数据的时候并不能完全利用同一机柜里面机器的带宽。 在默认的配置下, hadoop 的冗余复制因子是 3,意思就是每一块文件数据一共有 3 个地方存放, hadoop 目前的存放策略是其中两份放在同一个 rack id 的不同机器上面,另外一个放在不同 rack id
11、的机器上面,简单来说就是 1/3 的冗余数据 在一个机柜里面, 2/3 的冗余数据在另外一个机柜里面,这样既可以防止机柜异常时候的数据恢复,又可以提高读写性能。 上面所说的策略目前还是在测试优化阶段。 二、数据读取 数据读取策略,根据前面所说的数据存放策略,数据读取的时候,客户端也有 api 确定自己的机柜 id,读取的时候,如果有块数据和客户端的机柜 id 一样,就优先选择该数据节点,客户端直接和数据节点建立连接,读取数据。如果没有,就随机选取一个数据节点。 三、数据复制 主要是 在数据写入和数据恢复的时候发生,数据复制是使用流水线复制的策略。 当客户端要在 hadoop 上面写一个文件,首
12、先它先把这个文件写在本地,然后对文件进行分块 ,默认 64m 一块 ,每块数据都对 hadoop 目录服务器请求,目录服务器选择一个数据机列表,返回给客户端,然后客户端就把数据写入第一台数据机,并且把列表传给数据机,当数据机接收到 4k 数据的时候,写入本地并且发起连接到下一台数据机,把这个 4k 传过去,形成一条流水线。当最后文件写完的时候,数据复制也同时完成,这个就是流水线处理的优势。 通讯协议 hadoop 的通讯协议基本是在 tcp/ip 的基础上开发的,客户端使用 ClientProtocol 和 目录服务器通讯,数据机使用 DatanodeProtocol 和目录服务器通讯,而目录
13、服务器一般只是应答客户端和数据机的请求,不会主动发起通讯。 数据错误和异常 hadoop 文件系统的主要目标就是在硬件出错的时候保证数据的完整性,它把磁盘错误作为肯定会出现的情况来对待,而不是异常。 一般数据存储中出现的错误有几种,分别是目录服务器错误,数据机错误,和网络传输异常。 1、 数据机出错,每个数据机会定时发送一个心跳信息给目录服务器,表明自己仍然存活,网络异常可能会导致一部分数据机无法和目录服务器通讯,这时候目录服务器收 不到心跳信息,就认为这个数据机已经死机,从有效 io 列表中 清除 ,而该数据机上面的所有数据块也会标记为不可读。 这个时候某些数据块的冗余份数有可能就低于它的冗
14、余因子了,目录服务器会定期检查每一个数据块,看看它是否需要进行数据冗余复制。 2、 出现数据异常, 由于网络传输和磁盘出错的原因,从数据机读取的数据有可能出现异常,客户端实现对数据块的校验,用 md5 和 sha1 进行校验, 客户端在创建文件的时候,会对每一个 文件块进行信息摘录,并把这些信息写入到同一个路径的隐藏文件里面。当客户端读取文件的时候,会先读取该信息文件,然后对 每个读取的数据块进行校验,如果校验出错,客户端 就会请求到另外一个数据机读取该文件块,并且报告给目录服务器这个文件块有错误,目录服务器就会定期检查,并且重新复制这个块。 3、 目录服务器出错, FsImage 和 Edi
15、tlog 是目录服务器上面两个最核心的数据结构, 如果其中一个文件出错的话,会造成目录服务器不起作用,由于这两个文件如此重要,所以目录服务器上面可以设置多个备份文件和辅助服务器,当这两个文件有改变的时候,目录服务器就会发起同步操作,虽然这样增加了系统的负担,但是在目前这个架构上面为了实现数据的可靠性,这个 同步操作是非常必要的。 Hadoop 文件系统尚未实现的功能总结: 1、 文件追加写入,这个功能近期内不会实现,没有这个功能会引起当文件尚未关闭的时候,数据服务器死机或者目录服务器死机,会引起文件文件丢失,并且不可后续恢复写入。 2、 系统快照,一个全系统的快照功能,如果没有这个功能就不能实
16、现文件系统的回滚操作。 3、 集群负载均衡,均衡策略暂时没有实现,有几个策略十分有用,比如在某台数据机可能磁盘过低的 时候,把该数据机上面的一些数据转移到还有很多空间剩余的数据机上;当某个文件突然被大量读写的时候,动态增加该文件的冗余因 子,并且数据块复制到更多的数据机上面,以提高读取性能。 4、 文件系统的用户权限,这个也是近期内不会实现的了。 5、访问权限,现在是无限制访问的,没有访问权限控制。 Hadoop 文件系统性能分析 由于没办法建立大型的 Hadoop 文件系统,只能节选一些网上的性能分析,以表示一二。 1、 和 Kosmos Filesystem 的比较 , Kosmos Fi
17、lesystem 也是一个类似 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 字节
18、大小,每一个数据是 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 Throughp
19、ut: 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 s
20、ize: 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
21、 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 inse
22、rts/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 文件系统的比较,由于集群是在极端
23、条件下测试,目录服务器在广州网通机房,两台数据服务器一台在北京电信机房,一台在北京网通机房,所以测试的瓶颈基本在网络传输,估计在局域网中表现应该好很多。 本地 文件系统 测试: 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.FileBe
24、nch -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/common
25、s-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:
26、 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.FileB
27、ench -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和 KF
28、S,这两个也是类似 Bigtable 和 GFS 的实现,主要是使用 c+实现的,这里先记录一下,以后再做研究。 Hypertable 作者语 :Hypertable 与 HBase 的差别是, Hypertable 是 Bigtable 的一个更高性能的实现( InfoQ同样采访了 HBase 的团队)。我开始的时候跟 Jim Kellerman 以及 Hadoop 团队的一些成员一起为 HBase 工作。但我们对 HBase 应该变成什么样子有不同意见,对实现语言的选择也有不同意见。他们坚持用 Java,而我力推 C+。于是我就分出来,开始了 Hypertable 项目。 Hbase 分布
29、式数据库 数据模型 Hbase 是一个类似 Bigtable 的分布式数据库, 大部分特性和 Bigtable 一样,是 一个稀疏的,长期存储的存在硬盘上,多维度的,排序的映射表 。 这张表的索引是行关键字,列关键字和时间戳 。 每个值是一个不解释的字符数组 , 数据都是字符串,没类型 。 用户在表格中存储数据,每一行都有一个可排序的主键和任意多的列。 由于是稀疏存储的,所以同一张表里面的每一行数据都可以有截然不同的列。 列名字的格式是 “:“,都是由字符串组成,每一张表有一个 family 集合,这个集合是固定不变的,相当于表的结构,只能通过改变表结构来改变。 但是 label 值相对于每一
30、行来说都是可以改变的。 Hbase 把同一个 family 里面的数据存储在同一个目录底下,而 Hbase 的写操作是锁行的,每一行都是一个原子元素,都可以加锁。 所有数据库的更新都有一个时间戳标记, 每个更新都是一个新的版本, 而 hbase 会保留一定数量的版本,这个值是可以设定的。 客户端可以选择获取距离某个时间最近的版本,或者一次获取所有版本。 概念视图: 一个表可以想象成一个大的映射关系,通过主键,或者主键 +时间戳,可以定位一行数据,由于是稀疏数据,所以某些列可以是空白的,下面就是数据的概念视图: Row Key Time Stamp Column “contents:“ Colu
31、mn “anchor:“ Column “mime:“ “n.www“ t9 “anchor:“ “CNN“ t8 “anchor:my.look.ca“ “CNN.com“ t6 “.“ “text/html“ t5 “.“ t3 “.“ 上图是 一个存储 Web网页的范例列表片断。行名是一个反向 URL即 n.www。 contents列族原文用 family,译为族,详见 列族 存 放网页内容, anchor 列族存放引用该网页的锚链接文本。 CNN 的主页被 Sports Illustrater即所谓 SI, CNN 的王牌体育节目和 MY-look的主页引用,因此该行包含了名叫 “a
32、nchor:”和 “anchhor:my.look.ca”的列。每个锚链接只有一个版本由时间戳标识,如 t9, t8;而 contents 列则有三个版本,分别由时间 戳t3, t5,和 t6 标识。 物理视图 虽然从概念视图来看每个表格是由很多行组成,但是在物理存储上面, 它是按照列来保存的,这点在数据设计 和程序开发的时候必须牢记。 上面的概念视图在物理存储的时候应该表现成下面那样子: Row Key Time Stamp Column “contents:“ “n.www“ t6 “.“ t5 “.“ t3 “.“ Row Key Time Stamp Column “anchor:“
33、“n.www“ t9 “anchor:“ “CNN“ t8 “anchor:my.look.ca“ “CNN.com“ Row Key Time Stamp Column “mime:“ “n.www“ t6 “text/html“ 需要注意的是在概念视图上面有些列是空白的,这样的列实际上并不会被存储,当请求这些空白的单元格的时候,会返回 null 值。 如果在查询的时候不提供时间戳,那么会返回距离现在最近的那一个版本的数据。 因为在存储的时候,数据会按照时间戳排序。 例子: 一个程序写 9 行数据, row0-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