部署与使用

Introduction

Hbase 是运行在 Hadoop 上的 NoSQL 数据库,它是一个分布式的和可扩展的大数据仓库,也就是说 HBase 能够利用 HDFS 的分布式处理模式,并 从 Hadoop 的 MapReduce 程序模型中获益。这意味着在一组商业硬件上存储许多具有数十亿行和上百万列的大表。除去 Hadoop 的优 势,HBase 本身就是十分强大的数据库,它能够融合 key/value 存储模式带来实时查询的能力,以及通过 MapReduce 进行离线处理或者批处理 的能力。总的来说,Hbase 能够让你在大量的数据中查询记录,也可以从中获得综合分析报告。HBase 的发展历史进程简介如下 :

  • 2006.11 G release paper on BigTable

  • 2007.2 inital HBase prototype created as Hadoop contrib

  • 2007.10 First useable Hbase

  • 2008.1 Hadoop become Apache top-level project and Hbase becomes subproject

  • 2008.10 Hbase 0.18,0.19 released

HBase 中的表一般有如下特点 :

  • 大:一个表可以有上亿行,上百万列

  • 面向列 : 面向列 ( 族 ) 的存储和权限控制,列 ( 族 ) 独立检索。

  • 稀疏 : 对于为空 (null) 的列,并不占用存储空间,因此,表可以设计的非常稀疏。

Logical View: 逻辑视图

HBase 介于 NoSQL 和 RDBMS 之间,仅能通过主键 (row key) 和主键的 range 来检索数据,仅支持单行事务 ( 可通过 hive 支持来实现多表 join 等复杂操作 )。主要用来存储非结构化和半结构化的松散数据。 HBase 实际上定义了一个四维数据模型,下面就是每一维度的定义:

  • 行键:每行都有唯一的行键,行键没有数据类型,它内部被认为是一个字节数组。

  • 列簇:数据在行中被组织成列簇,每行有相同的列簇,但是在行之间,相同的列簇不需要有相同的列修饰符。在引擎中,HBase 将列簇存储在它自己的数据文件中,所以,它们需要事先被定义,此外,改变列簇并不容易。

  • 列修饰符:列簇定义真实的列,被称之为列修饰符,你可以认为列修饰符就是列本身。

  • 版本:每列都可以有一个可配置的版本数量,你可以通过列修饰符的制定版本获取数据。

如上图所示,通过行键获取一个指定的行,它由一个或多个列簇构成,每个列簇有一个或多个列修饰符(也被称为列),每列又可以有一个或多个版本。为了获取指定数据,你 需要知道它的行键、列簇、列修饰符以及版本。当设计 HBase 数据模型时,对考虑数据是如何被获取是十分有帮助的。你可以通过以下两种方式获得 HBase 数据:

  • 通过他们的行键,或者一系列行键的表扫描。

  • 使用 map-reduce 进行批操作

Row Key

Row key 行键 (Row key) 可以是任意字符串 ( 最大长度是 64KB,实际应用中长度一般为 10-100bytes),在 hbase 内部,row key 保存为字节数组。存储时,数据按照 Row key 的字典序 (byte order) 排序存储。设计 key 时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。( 位置相关性 ) 注意:字典序对 int 排序的结果是 1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整形的自然序,行键必须用 0 作左填充。行的一次读写是原子操作 ( 不论一次读写多少列 )。这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。

列簇

HBase 表中的每个列,都归属与某个列簇。列簇是表的 Schema 的一部分 ( 而列不是 ),必须在使用表之前定义。列名都以列簇作为前缀。例如 courses:history,courses:math 都属于 courses 这个列簇。访问控制、磁盘和内存的使用统计都是在列簇层面进行的。实际应用中,列簇上的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。

时间戳

HBase 中通过 row 和 columns 确定的为一个存贮单元称为 cell。每个 cell 都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64 位整型。时间戳可以由 hbase( 在数据写入时自动 ) 赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。为了避免数据存在过多版本造成的的管理 ( 包括存贮和索引 ) 负担,hbase 提供了两种数据版本回收方式。一是保存数据的最后 n 个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。

Cell

由 {row key, column(= + ), version} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。

Reference

Quick Start

Installation: 安装

让我们开始用命令行操作 HBase,在 HBase bin 目录下执行下面命令:

./hbase shell

你应该看到如下类似的输出:

HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 0.98.5-hadoop2, rUnknown, Mon Aug 4 23:58:06 PDT 2014
hbase(main):001:0>

表创建

创建一个名为 PageViews 的表,并具有名为 info 的列簇:

hbase(main):002:0> create 'PageViews', 'info'
0 row(s) in 5.3160 seconds
=> Hbase::Table - PageViews

每张表至少要有一个列簇,因此我们创建了 info,现在,看看我们的表,执行下面 list 命令:

hbase(main):002:0> list
TABLE
PageViews
1 row(s) in 0.0350 seconds
=> ["PageViews"]

如你所见,list 命令返回一个名为 PageViews 的表,我们可以通过 describe 命令得到表的更多信息:

hbase(main):003:0> describe 'PageViews'
DESCRIPTION ENABLED
'PageViews', {NAME => 'info', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW',
REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE true
', MIN_VERSIONS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS => 'false',
BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
1 row(s) in 0.0480 seconds

Describe 命令返回表的详细信息,包括列簇的列表,这里我们创建的仅有一个:info 。

数据插入

现在为表添加以下数据,下面命令是在 info 中添加新的行:

hbase(main):004:0&gt; put ";PageViews", ";rowkey1", ";info:page", ";/mypage"
0 row(s) in 0.0850 seconds

Put 命令插入一条行键为 rowkey1 的新纪录,指定在 info 下的 page 列,插入值为 /mypage 的记录,我们随后可以通过 get 命令通过行键 rowkey1 查询到这条记录:

hbase(main):005:0> get 'PageViews', 'rowkey1'
COLUMN CELL
info:page timestamp=1410374788088, value=/mypage
1 row(s) in 0.0250 seconds

数据查询

让我们查询出 PageViews 表的所有记录:

hbase(main):007:0> scan 'PageViews'
ROW COLUMN+CELL
rowkey1 column=info:page, timestamp=1410374788088, value=/mypage
rowkey2 column=info:page, timestamp=1410374823590, value=/myotherpage
2 row(s) in 0.0350 seconds

如前面所提到的,我们不能查询本身,但是我们可以对表进行 scan 操作,如果你执行 scan table 命令,它会返回表中所有行,这很有可能不是你想要做的。你可以给出行的范围来限制返回的结果,让我们插入一带有 s 开头行键的新记录:

hbase(main):012:0> put 'PageViews', 'srowkey2', 'info:page', '/myotherpage'

现在,如果我增加点限制,想查询行键在 r 和 s 之间的记录,可以使用如下结构:

hbase(main):014:0> scan 'PageViews', { STARTROW => 'r', ENDROW => 's' }
ROW COLUMN+CELL
rowkey1 column=info:page, timestamp=1410374788088, value=/mypage
rowkey2 column=info:page, timestamp=1410374823590, value=/myotherpage
2 row(s) in 0.0080 seconds

这个 scan 返回了仅有 s 开头的记录,这个类比是基于全行键上的,所以 rowkey1 比 r 大,所有它被返回了。另外,scan 的结果包含了所指范围的 STARTROW,但不包含 ENDROW,注意,ENDROW 不是必须指定的,如果我们执行相同查询只给出了 STARTROW,那么我们会得到行键比 r 大 的所有记录。

hbase(main):013:0> scan 'PageViews', { STARTROW => 'r' }
ROW COLUMN+CELL
rowkey1 column=info:page, timestamp=1410374788088, value=/mypage
rowkey2 column=info:page, timestamp=1410374823590, value=/myotherpage
srowkey2 column=info:page, timestamp=1410375975965, value=/myotherpage
3 row(s) in 0.0120 seconds