HBase入门精要-百闻不如一Run

0.596字数 3283阅读 4715

零.导读

HBase,基于Google Bigtable实现的开源、分布式、可伸缩的列式存储数据库,诞生于Hadoop,也是Hadoop生态的重要一环,如今作为一个Apache顶级项目,早已经不能将其仅仅看作Hadoop的一部分,基于Storm,Spark等框架的数据处理方案中,都有它的身影,可以说它已经成为大数据工具箱中非常重要的一种数据存储工具,也因此必然会被很纳入很多人学习计划。
对于一个新技术的入门,我认为一种有效的学习方式是:

对其有简要认知后,通过Quick Start式的使用,获得直观的感知,消除距离感,然后再带着使用过程中的疑问去了解其背后的真相,最后支撑我们将其应用到实际工程。

我将消除距离感这一阶段,称之为百闻不如一Run

本文分三部分带你完成对HBase的百闻不如一Run:数据模型概述、环境部署和基本操作。

*版本:本文基于HBase 1.2.2 --Release date: 11/Jul/16 *

一. HBase数据模型

HBase是对Bigtable的开源实现,所以先来认识一下Bigtabl概念,引用Google's BigTable Paper中的精简描述:

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

The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes.

HBase的数据模型与此非常相似,用一张参考自上述论文的图来辅助理解:


图1 HBase某张表中某一行缩存储数据的一种可视化呈现

HBase的结构:

  • 命名空间(namespace):0.96版本开始支持,是对多个表的逻辑分组,类似于关系数据库的database,在本文暂不关心。
  • 表(table):一张表中包含若干行。
  • 行(row):一行包括一个行键(row key)和若干列族,一张表中的行按照行键排序,并用行键作为索引。图1中展示了一个行键为row1的行。
  • 列族(column family):每个列族包含若干个列,列族需要在建表时预定义,运行期间可以动态加入新的列。图1中的"data"、"meta"就是row1行中的两个列族。 在物理层面,HBase的数据存储是在列族这一层级进行组织,每个列族单独存储。
  • 列(column):每个列都归属于某个列族,以列族名作为前缀,通常使用列族名:修饰符的形式来标识一个列,可以将其中的修饰符部分看作列名。图1中的"meta:mimetype"和"meta:size"即是列族meta中的成员。
  • 单元格(cell):存储的每一个值存放在一个单元格中,由[行,列,版本号]来唯一指向一个单元格。图1中彩色标识的矩形块即可看作是一个单元格
  • 版本(version):版本号默认是时间戳形式,同一列中可能包含若干单元格,这些单元格由版本号唯一区分,根据版本号降序排列,HBase查询时,如果不指定版本号,默认返回最新的值。图1中的t3,t6等即代表版本号。版本是HBase多维特性的表现

Google论文中Bigtable描述为一个map,那么从Map的维度,用JSON格式,HBase的结构可以理解为:

{
  // ...
  "row1" : {
    "family1" : {
      "column1" : {
        timestamp2 : "value1",
        timestamp3 : "value2"
        },
      "column2" : {timestamp : "value3"}
    },
    "family2" : { ... }
  },
  "row2" : {
    "family3" : { ... }
  },
  // ...
}

而关于其稀疏这一特性,可以用下图来辅助理解:

图2 HBase的行和列所构成的更像标签,而不是表格

对于我们熟悉的关系型数据库,如MySQL,一张表中每一行都有相同的列,即使部分行的某些列不存储数据,也有消耗,如图中的NULL。而HBase,各行是相对独立的,可以有完全不同的列。

二.部署

如果最初阶段你需要HBase环境的主要目的是想熟悉对HBase的CRDU操作,那么看完独立部署后,可以直接跳到三.基本操作
如果希望在部署环境过程中对HBase的架构也做一个简要了解,那么建议进行伪分布式部署;如果伪分布式部署你能够很快完成,那么相信完全的分布式部署对你来说也并不困难,并且本文的主要目的是快速入门,因此不提供完全分布式部署的过程指引,如有需要,请参考官方指南quickstart_fully_distributed

0. 基础条件

  • 需要Java,支持JDK7和JDK8
  • 需要ssh,伪分布式部署需要ssh localhost能正常连接,分布式部署需要配置各节点间的无密码登陆(ssh passwordless login)

注:1.0.0版本开始,HBase内部组件(HMaster,HRegionServer)的默认端口从60xxx变更为16xxx

1. 独立部署

如果想要最快速的搭建供你练习HBase数据库操作的环境,那么这可能是你想要的。
独立部署模式下,HBase的所有进程都运行在一个JVM中,数据直接存储在本地磁盘。

a. 下载安装包并解压

wget https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/1.2.2/hbase-1.2.2-bin.tar.gz
tar zxvf hbase-1.2.2-bin.tar.gz -C target-dir

b. 配置

  • /etc/hosts中配置localhost的地址:127.0.0.1 localhost
  • JAVA_HOME:在conf/hbase-env.sh中配置,例如:export JAVA_HOME=/usr/local/jdk
  • 配置HBase和zookeeper保存数据的位置:
    • 如果不配置,默认写在/tmp目录下
    • conf/hbase-site.xml.中配置,地址格式有两种,例如:
    <configuration>
        <property>
            <name>hbase.rootdir</name>
            <value>file:///home/hbase/hbase1.2.2</value>
        </property>
        <property>
            <name>hbase.zookeeper.property.dataDir</name>
            <value>/home/hbase/hbase1.2.2/zookeeper</value>
        </property>
    </configuration>
    

c. 启动和停止
可以直接在HBase安装目录运行bin/start-hbase.sh启动:

[hbase@iZ25n0dx8rxZh base]$ ./bin/start-hbase.sh 
starting master, logging to /usr/local/hbase/bin/../logs/hbase-hbase-master-iZ25n0dx8rxZ.out

启动日志默认位于./logs/hbase-[username]-master-[yourhostname].log,启动成功后,用jps命令可以看到名为HMaster的进程。接下来,你就可以使用hbase的shell来进行操作练习了。
要停止hbase,使用bin/stop-hbase.sh

d. UI访问
Hbase内建了一个用Jetty提供服务的web UI页面来查看该HBase环境的各种信息,默认端口16010,尝试用http://hostip:16010/来访问。

2. 伪分布式部署

伪分布式模式下,HBase的所有组件还是运行在同一台主机,不同的是,每个组件独立运行在不同的JVM。更重要的是,我们可以在该模式下启动多个Regionserver和master,构成一个虚拟的分布式架构以供学习,这是很多快速入门文章所略过的重点。
该模式下,可以对接HDFS,但那涉及hadoop的部署,为以更短的时间达到当前阶段的目的,本文仍存储在本地磁盘。

a. HBase架构概要

图3 HBase架构概要图

作为入门阶段,先从粗粒度对HBase的架构进行简单了解:
HMaster:主要负责监控集群、管理RegionServers的负责均衡等,可以用主-备形式部署多个Master。
HRegionServers:负责响应用户的I/O操作请求,客户端对HBase读写数据是与RegionServer交互。
Zookeeper:负责选举Master的主节点;服务注册;保存RegionServers的状态等。可以使用系统内建的zookeeper,也可以使用独立的zookeeper,只需要在配置文件中调整即可。
HDFS:真正的数据持久层,并非必须是HDFS文件系统,但搭配HDFS是最佳选择,也是目前应用最广泛的选择。

b. 开始部署
伪分布式模式下,需要保证ssh localhost能够成功连接(将HBase所属用户的publickey追加到其自身的authorized_keys中)。如果你跟随本文启动了独立模式的HBase,先将其停止。

  • 开启分布式配置
    最基本的伪分布式配置,只需要在独立模式的配置基础上,追加开启分布式模式的配置,即将hbase.cluster.distributed配置为true,例如:
    <configuration>
        <property>
          <name>hbase.rootdir</name>
          <value>/home/hbase/hbase1.2.2</value>
        </property>
        <property>
          <name>hbase.zookeeper.property.dataDir</name>
          <value>file:///home/hbase/hbase1.2.2/zookeeper</value>
        </property>
        <property>
          <name>hbase.cluster.distributed</name>
          <value>true</value>
        </property>
     </configuration>
    
  • 在安装目录运行bin/start-hbase.sh

[hbase@iZ25n0dx8rxZ hbase]$ ./bin/start-hbase.sh
localhost: starting zookeeper, logging to /usr/local/hbase/bin/../logs/hbase-hbase-zookeeper-iZ25n0dx8rxZ.out
starting master, logging to /usr/local/hbase/bin/../logs/hbase-hbase-master-iZ25n0dx8rxZ.out
starting regionserver, logging to /usr/local/hbase/bin/../logs/hbase-hbase-1-regionserver-iZ25n0dx8rxZ.out

可以看到依次启动了zookeeper、master和regionserver,启动日志为*./logs*路径下的*.log*文件。
- 查看启动的进程以及占用的端口:
```shell
[hbase@iZ25n0dx8rxZ logs]$ jps
4610 HRegionServer
4456 HQuorumPeer
5338 Jps
4522 HMaster
[hbase@iZ25n0dx8rxZ logs]$ netstat -lnp|grep 4522
tcp        0      0 172.16.5.23:16000           0.0.0.0:*                   LISTEN      4522/java           
tcp        0      0 0.0.0.0:16010               0.0.0.0:*                   LISTEN      4522/java           
[hbase@iZ25n0dx8rxZ logs]$ netstat -lnp|grep 4610
tcp        0      0 172.16.5.23:16201           0.0.0.0:*                   LISTEN      4610/java           
tcp        0      0 0.0.0.0:16301               0.0.0.0:*                   LISTEN      4610/java           
[root@iZ25n0dx8rxZ logs]$ netstat -lnp|grep 4456
tcp        0      0 0.0.0.0:2188                0.0.0.0:*                   LISTEN      4456/java 
  • HMaster占用16000(工作进程)和16010(Master的web UI服务端口)

  • HRegionServer占用16201(工作进程)和16301(Regionserver的web UI服务)

  • HQuorumPeer是HBase内建zookeeper进程,默认端口2181(即zookeeper的默认配置)。如果是独立的zookeeper,进程名是QuorumPeerxxx,没有第一个字母H。

  • 启动和停止备份Master节点(backup HMaster)**:

    • 运行./bin/local-master-backup.sh start n来启动一个备份节点,如:

[hbase@iZ25n0dx8rxZ hbase]$ ./bin/local-master-backup.sh start 1
starting master, logging to /usr/local/hbase/bin/../logs/hbase-hbase-1-master-iZ25n0dx8rxZ.out

启动成功后,*jps*命令可以看到总共有两个HMaster进程。

- 端口:**n**用来指定占用的端口号,规则为**[默认端口号+n]**,如例子中的*./bin/local-master-backup.sh start 1*所启动的HMaster占用16001(工作端口)和16011(web UI服务端口),以此类推。
- 日志:启动日志在*./logs/hbase-[username]-n-master-[hostname].log*,在上例的日志中,可以看到这样一行日志说明该节点目前是作为备用节点:
```shell
master.ActiveMasterManager: Another master is the active master, iz25n0dx8rxz,16000,1469262015657; waiting to become the next active master

注意:如果使用1.2.2之前版本的安装包(如1.1.5),运行启动脚本后backup Master可能会因为端口被占用而无法启动,这是因为脚本里面,没有按照规则更改backup Master的工作端口,启动时仍然使用默认的16000,而该端口已经被前面启动的主节点占用。可以通过如下方法解决该问题:
手动在./bin/local-master-backup.sh脚本中为HBASE_MASTER_ARGS赋值这句话内添加-D hbase.master.port=\expr 16000 + $DN` \`来设置backup Master的工作端口,添加后这句话的完整内容如下:

HBASE_MASTER_ARGS="\
  -D hbase.master.port=`expr 16000 + $DN` \
  -D hbase.master.info.port=`expr 16010 + $DN` \
  -D hbase.regionserver.port=`expr 16020 + $DN` \
  -D hbase.regionserver.info.port=`expr 16030 + $DN` \
  --backup"
  • web UI访问地址:http://ip:1601n/

  • 主节点切换:要观察HBase的Master组件主节点切换,可以使用kill -9 PID停止当前主节点(即最初启动的HMaster),此时刚启动的备份节点将切换为主节点,可以在备份节点的日志(./logs/hbase-[username]-1-master-[hostname].log)中看到如下内容:

INFO  [iZ25n0dx8rxZ:16001.activeMasterManager] master.ActiveMasterManager: Deleting ZNode for /hbase/backup-masters/iz25n0dx8rxz,16001,1469267021567 from backup master directory
INFO  [iZ25n0dx8rxZ:16001.activeMasterManager] master.ActiveMasterManager: Registered Active Master=iz25n0dx8rxz,16001,1469267021567
  • 停止:使用./bin/local-master-backup.sh stop n来停止你的备份节点。
  • 多备:可以一次启动多个backup HMaster,命令类似于./bin/local-master-backup.sh start x y z
  • 启动和停止额外的RegionServer
    • 运行额外RegionServer的方式与backup HMaster类似,启动:./bin/local-regionservers.sh start n,停止:./bin/local-regionservers.sh stop n
    • web UI访问地址:http://ip:1630n/

三. 基本操作

本节介绍使用HBase shell在直接在服务器上对HBase进行基本操作,HBase shell是在(J)Ruby的IRB的基础上增加了HBase特有的命令,遵循IRB的操作。

  1. 连接:./bin/hbase shell
[hbase@iZ25n0dx8rxZ hbase]$ ./bin/hbase shell
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 1.2.2, r3f671c1ead70d249ea4598f1bbcc5151322b3a13, Fri Jul  1 08:28:55 CDT 2016
hbase(main):001:0>
  1. 建表:create 'test', 'cf1', 'cf2',即[create '表名', '列族名',..],列族名可以有多个,list用于查看有哪些表
hbase(main):008:0> create 'test','cf1','cf2'
0 row(s) in 1.2280 seconds
=> Hbase::Table - test
hbase(main):009:0>
  1. 写数据:put 'test', 'row1', 'cf1:c1', 'value1',即[put '表名','行键','列族名:列名','数据']
hbase(main):001:0> put 'test','row1','cf1:c1','value1'
0 row(s) in 0.3160 seconds
hbase(main):002:0> put 'test','row1','cf1:c1','value2'
0 row(s) in 0.3020 seconds
  1. 查看数据:
  • 全表数据:scan 'test',即[scan '表名']
hbase(main):001:0> scan 'test'
ROW                                            COLUMN+CELL                                                                                                                             
 row1                                          column=cf1:c1, timestamp=1469277197280, value=value2                                                                                    
1 row(s) in 0.2710 seconds
hbase(main):002:0> 

可以看到在put时指定的属性之外,有一个timestamp属性来作为版本标识,我们查看全表数据时,row1的cf1:c1列中展示的值是我们后一次写入的value2,sacn和get在不指定版本时,得到的是最近版本的数据

  • 指定行的数据:get 'test', 'row1',即[get '表名','行键']
  • 指定版本的数据:
hbase(main):005:0> get 'test','row1',{COLUMN=>'cf1:c1',TIMESTAMP=>1469277197280}
COLUMN                                         CELL                                                                                                                                    
cf1:c1                                        timestamp=1469277197280, value=value1                                                                                                   
1 row(s) in 0.0270 seconds
hbase(main):006:0>
  1. 版本数量:每个列族有一个单独的VERSIONS属性,默认为1,可以在建表时指定:create 'test1',{NAME=>'cf1',VERSIONS=>3},代表该列族的每个列最多保存最近3个版本的数据,也可以通过alter来更新:alter 'test1',NAME=>'cf1',VERSIONS=>3。查询数据时,可以通过设置VERSIONS来指定显示最近几个版本的数据(最大范围不超过该列族的VERSIONS属性值)get 'test','row1',{COLUMN=>'cf1:c1',VERSIONS=>2}
  2. 删除数据:
  • 删除指定单元格:delete 'test','row1','cf1:c1',1469277197280将删除指定版本以及比其更早的版本
  • 删除指定行的指定列:delete 'test','row1','cf1:c1'
  • 删除整行: deleteall 'test','row1'
  1. 禁用表:disable 'test',即[disable '表名'],在要删除表或者变更配置时,要先禁用该表。相应的,要重新启用该表,使用[enable '表名']
  2. 删除表:drop 'test',即[drop '表名']
  3. 退出HBase shell:exit或者quit
  4. 完整的命令列表,参考hbase-shell-commands

四. 尾声

本文简要介绍了HBase的数据模型、快速搭建基本操作环境的步骤以及基于HBase shell的HBase数据库基本操作,旨在协助想要学习HBase的朋友快速进入到对HBase的操作和使用阶段,消除陌生感和距离感。在这之后,我们可能想问,真正应用在工程上的操作HBase的方式有哪些,HBase存取数据的完整过程是怎样的,怎样去设计一个适合的表结构,等等,那么,请带着这些问题继续你的HBase之路。

References

Apache HBase ™ Reference Guide
Google's BigTable Paper
Understanding HBase and BigTable

推荐阅读更多精彩内容