HBase
分布式存储

HBase 02:HBase命令行操作及Java API演示

简介:HBase是一个构建在HDFS上的分布式列存储系统,基于Google BigTable模型开发的,典型的key/value系统;HBase是Apache Hadoop生态系统中的重要一员,主要用于海量结构化数据存储;从逻辑上讲,HBase将数据按照表、行和列进行存储。与hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。

1. 配置外部Zookeeper

默认情况下,HBase是有内置的Zookeeper的,且由HBase自己管理,如果我们要使用外部Zookeeper,就需要让HBase放弃Zookeeper的管理权,这一点需要在hbase-env.sh文件中配置:

  • export HBASE_MANAGES_ZK=false

另外要让HBase连接到外部Zookeeper,我们还需要让HBase知道外部Zookeeper的Host和Port,这一点需要在hbase-sit.xml文件中配置,并且要分发该文件:

  • <configuration>
  • <property>
  • <name>hbase.cluster.distributed</name>
  • <value>true</value>
  • </property>
  • <property>
  • <name>hbase.rootdir</name>
  • <value>hdfs://s100:8020/hbase</value>
  • </property>
  • <property>
  • <name>hbase.zookeeper.property.dataDir</name>
  • <value>/home/ubuntu/hbase/zk</value>
  • </property>
  • <property>
  • <name>hbase.zookeeper.quorum</name>
  • <value>s101:2181,s103:2181,s103:2181</value>
  • </property>
  • <property>
  • <name>hbase.zookeeper.property.clientPort</name>
  • <value>2181</value>
  • </property>
  • </configuration>

做完以上配置,我们需要先启动Hadoop,再启动Zookeeper,然后启动HBase:

  • ubuntu@s100:/soft/hbase/conf$ start-hbase.sh
  • starting master, logging to /soft/hbase/logs/hbase-ubuntu-master-s100.out
  • Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
  • Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
  • s103: starting regionserver, logging to /soft/hbase/logs/hbase-ubuntu-regionserver-s103.out
  • s102: starting regionserver, logging to /soft/hbase/logs/hbase-ubuntu-regionserver-s102.out
  • s101: starting regionserver, logging to /soft/hbase/logs/hbase-ubuntu-regionserver-s101.out
  • s103: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
  • s103: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
  • s102: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
  • s102: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
  • s101: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
  • s101: Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0

启动成功后可以查看Hbase的WebUI,地址是http://s100:16010

1.Hbase的WebUI.png

进程信息:

  • ubuntu@s100:/soft/hbase/conf$ xcall jps
  • ----------- local execute ------------
  • 3090 HMaster
  • 1973 NameNode
  • 3435 Jps
  • 2252 ResourceManager
  • ----------- s101 execute -----------
  • 1810 QuorumPeerMain
  • 2391 HRegionServer
  • 2664 Jps
  • 2075 NodeManager
  • 1963 DataNode
  • ----------- s102 execute -----------
  • 2241 HRegionServer
  • 1681 QuorumPeerMain
  • 1924 NodeManager
  • 1812 DataNode
  • 2501 Jps
  • ----------- s103 execute -----------
  • 2307 HRegionServer
  • 1879 DataNode
  • 2552 Jps
  • 2008 NodeManager
  • 1754 QuorumPeerMain
  • ----------- s104 execute -----------
  • 2101 Jps
  • 1798 DataNode
  • 1912 NodeManager
  • ----------- s105 execute -----------
  • 1771 Jps
  • ----------- s106 execute -----------
  • 1793 SecondaryNameNode
  • 1917 Jps

以及Zookeeper的节点信息:

  • ubuntu@s100:/soft/hbase/conf$ zkCli.sh -server s101:2181
  • ...
  • [zk: s101:2181(CONNECTED) 0] ls /
  • [zookeeper, hbase]
  • [zk: s101:2181(CONNECTED) 2] ls /hbase
  • [replication, meta-region-server, rs, splitWAL, backup-masters, table-lock, flush-table-proc, region-in-transition, online-snapshot, master, running, recovering-regions, draining, namespace, hbaseid, table]

我们对之前的测试打印所有节点路径的代码做一定的改进,在打印路径的同时打印该路径的数据:

  • /**
  • * 列出根下所有节点
  • * @throws Exception
  • */
  • @Test
  • public void listAllChildNode() throws Exception {
  • ZooKeeper zk = new ZooKeeper("s101:2181,s102:2181,s103:2181", 5000, null);
  • listChildNode(zk, "/");
  • }
  • public void listChildNode(ZooKeeper zk, String parentPath) throws Exception {
  • // 打印节点路径和节点数据
  • String nodeData = getData(zk, parentPath);
  • System.out.println(parentPath + " => " + nodeData);
  • List<String> children = zk.getChildren(parentPath, false);
  • if (children == null || children.isEmpty()) {
  • return;
  • }
  • for (String path : children) {
  • if ("/".equals(parentPath)) {
  • parentPath = "";
  • }
  • listChildNode(zk, parentPath + "/" + path);
  • }
  • }
  • public String getData(ZooKeeper zk, String path) {
  • try {
  • Stat stat = new Stat();
  • byte[] data = zk.getData(path, false, stat);
  • return new String(data);
  • } catch (Exception e) {
  • // TODO: handle exception
  • }
  • return null;
  • }

运行上述代码,可以将所有节点的信息打印出来,其中包括HBase在Zookeeper中的节点信息:

2. HBase进程管理

在上面的分布式HBase进程信息来看,HBase主要包括两个进程:HMaster和HRegionServer。它们的作用分别如下:

  • HMaster是HBase主/从集群架构中的中央节点,它的作用大致有两个:
  1. HMaster讲region分配给RegionServer,协调RegionServer的负载并维护集群的状态;
  2. 维护表和Region的元数据,不参与数据的输入/输出过程。
  • RegionServer负责以下工作:
  1. 维护HMaster分配给他的region,处理对这些region的请求;
  2. 负责切分正在运行过程中变得过大的region。
  • Zookeeper同时参与的HBase集群的工作:
  1. Zookeeper是集群的协调器;
  2. HMaster启动后会将系统表加载到Zookeeper;
  3. Zookeeper提供HBase RegionServer的状态信息(由RegionServer主动向Zookeeper进行注册)。

HBase的进程管理和Hadoop是相类似的,我们使用start-hbase.shstop-hbase.sh可以同时启动所有的节点进程,但是也可以通过hbase-daemon.shhbase-daemons.sh脚本来分别管理;HBase的HMaster进程只有一个,而HRegionServer进程是有多个,所以我们可以使用hbase-daemon.sh单独启动HMaster进程:

  • ubuntu@s100:/soft/hbase/bin$ hbase-daemon.sh start master

或者用hbase-daemons.sh启动所有的HRegionServer进程:

  • ubuntu@s100:/soft/hbase/bin$ hbase-daemons.sh start regionserver

也可以登录到HRegionServer进程所在的主机上通过hbase-daemon.sh单独管理该主机的HRegionServer进程:

  • ubuntu@s100:/soft/hbase/bin$ hbase-daemon.sh start regionserver
  • ubuntu@s100:/soft/hbase/bin$ hbase-daemon.sh stop regionserver

需要注意的是,我们在哪台主机上运行start-hbase.sh,那么这台主机就是HMaster进程所在的主机。

3. HBase命令行

在Shell命令行上输入hbase shell即可进入HBase的Shell环境:

  • ubuntu@s100:~$ hbase shell
  • SLF4J: Class path contains multiple SLF4J bindings.
  • SLF4J: Found binding in [jar:file:/soft/hbase-1.2.4/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
  • SLF4J: Found binding in [jar:file:/soft/hadoop-2.7.2/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
  • SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
  • SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
  • HBase Shell; enter 'help<RETURN>' for list of supported commands.
  • Type "exit<RETURN>" to leave the HBase Shell
  • Version 1.2.4, r67592f3d062743907f8c5ae00dbbe1ae4f69e5af, Tue Oct 25 18:10:20 CDT 2016
  • hbase(main):001:0>

我们可以查看HBase Shell的帮助:

注:HBase Shell下退格变为Ctrl+Backspace。

  • hbase(main):001:0> help
  • HBase Shell, version 1.2.4, r67592f3d062743907f8c5ae00dbbe1ae4f69e5af, Tue Oct 25 18:10:20 CDT 2016
  • Type 'help "COMMAND"', (e.g. 'help "get"' -- the quotes are necessary) for help on a specific command.
  • Commands are grouped. Type 'help "COMMAND_GROUP"', (e.g. 'help "general"') for help on a command group.
  • COMMAND GROUPS:
  • Group name: general
  • Commands: status, table_help, version, whoami
  • Group name: ddl
  • Commands: alter, alter_async, alter_status, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, locate_region, show_filters
  • Group name: namespace
  • Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables
  • Group name: dml
  • Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve
  • Group name: tools
  • Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, close_region, compact, compact_rs, flush, major_compact, merge_region, move, normalize, normalizer_enabled, normalizer_switch, split, trace, unassign, wal_roll, zk_dump
  • Group name: replication
  • Commands: add_peer, append_peer_tableCFs, disable_peer, disable_table_replication, enable_peer, enable_table_replication, list_peers, list_replicated_tables, remove_peer, remove_peer_tableCFs, set_peer_tableCFs, show_peer_tableCFs
  • Group name: snapshots
  • Commands: clone_snapshot, delete_all_snapshot, delete_snapshot, list_snapshots, restore_snapshot, snapshot
  • Group name: configuration
  • Commands: update_all_config, update_config
  • Group name: quotas
  • Commands: list_quotas, set_quota
  • Group name: security
  • Commands: grant, list_security_capabilities, revoke, user_permission
  • Group name: procedures
  • Commands: abort_procedure, list_procedures
  • Group name: visibility labels
  • Commands: add_labels, clear_auths, get_auths, list_labels, set_auths, set_visibility
  • SHELL USAGE:
  • Quote all names in HBase Shell such as table and column names. Commas delimit
  • command parameters. Type <RETURN> after entering a command to run it.
  • Dictionaries of configuration used in the creation and alteration of tables are
  • Ruby Hashes. They look like this:
  • {'key1' => 'value1', 'key2' => 'value2', ...}
  • and are opened and closed with curley-braces. Key/values are delimited by the
  • '=>' character combination. Usually keys are predefined constants such as
  • NAME, VERSIONS, COMPRESSION, etc. Constants do not need to be quoted. Type
  • 'Object.constants' to see a (messy) list of all constants in the environment.
  • If you are using binary keys or values and need to enter them in the shell, use
  • double-quote'd hexadecimal representation. For example:
  • hbase> get 't1', "key\x03\x3f\xcd"
  • hbase> get 't1', "key\003\023\011"
  • hbase> put 't1', "test\xef\xff", 'f1:', "\x01\x33\x40"
  • The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added.
  • For more on the HBase Shell, see http://hbase.apache.org/book.html

3.1. 普通语法

  1. 查看名称空间(show databases;)
  • hbase(main):002:0> list_namespace
  • NAMESPACE
  • default
  • hbase
  • 2 row(s) in 0.4370 seconds
  • default:默认名称空间,创建表时如果没有指定名称空间就会存放在该名称空间下;
  • hbase:存放元数据的名称空间,相当于Hive中的MySQL;
  1. 查看名称空间中的表(show tables;)
  • hbase(main):006:0> list_namespace_tables 'hbase'
  • TABLE
  • meta
  • namespace
  • 2 row(s) in 0.0390 seconds

hbase是存放元数据的名称空间,该名称空间中存放有meta表和namespace表,记录了元数据信息。

  1. 查看名称空间中的表的信息(select * from table;)

注:需要指定查看的名称空间,不指定默认查看default名称空间下的表。

  • hbase(main):020:0> scan 'hbase:namespace'
  • ROW COLUMN+CELL
  • default column=info:d, timestamp=1498656855130, value=\x0A\x07default
  • hbase column=info:d, timestamp=1498656855150, value=\x0A\x05hbase
  • 2 row(s) in 0.0160 seconds

3.2. DDL语法

  1. 创建名称空间
  • hbase(main):024:0> create_namespace 'ns1'
  • 0 row(s) in 0.0480 seconds
  • hbase(main):025:0> list_namespace
  • NAMESPACE
  • default
  • hbase
  • ns1
  • 3 row(s) in 0.0330 seconds
  1. 创建表
  • hbase(main):023:0> create 't1','f1','f2'
  • 0 row(s) in 1.5140 seconds
  • => Hbase::Table - t1

上述的语法的意义是,创建一个表t1,它有两个列族f1和f2,会默认放在default名称空间下。我们可以指定表存放的名称空间:

  • hbase(main):026:0> create 'ns1:t1','f1'
  • 0 row(s) in 2.3080 seconds
  • => Hbase::Table - ns1:t1
  1. 查看表信息
  • hbase(main):027:0> desc 'ns1:t1'
  • Table ns1:t1 is ENABLED
  • ns1:t1
  • COLUMN FAMILIES DESCRIPTION
  • {NAME => 'f1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRES
  • SION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
  • 1 row(s) in 0.0360 seconds
  • VERSIONS:指定每行可存放记录的最大数目,连续存入多个版本如果超过最大数目时低版本的数据会被删除;
  • MIN_VERSIONS:指定每行可存放记录的最小数目,即使TTL到期,也会保留相应数量的版本。超出了TTL指定的时间后的额外版本会被删除;
  • TTL:Time to Live,超出最小版本的额外版本的存活时间,默认值为Forever;
  • KEEP_DELETED_CELLS:控制是否保留除的版本。
  1. 删除表
  • hbase(main):028:0> drop 't1'
  • ERROR: Table t1 is enabled. Disable it first.
  • Here is some help for this command:
  • Drop the named table. Table must first be disabled:
  • hbase> drop 't1'
  • hbase> drop 'ns1:t1'

上述信息提示我们,删除表之前需要先禁用表:

  • hbase(main):029:0> disable 't1'
  • 0 row(s) in 2.3130 seconds
  • hbase(main):030:0> drop 't1'
  • 0 row(s) in 1.2940 seconds
  • hbase(main):032:0> list_namespace_tables 'default'
  • TABLE
  • 0 row(s) in 0.0060 seconds

这样就可以删除成功了。

  1. 删除名称空间

删除名称空间使用drop_namespace,需要名称空间为空才能删除。

3.3. DML语法

  1. 向表中插入行数据(put)
  • hbase(main):033:0> put 'ns1:t1','row1','f1:id','100'
  • 0 row(s) in 0.0520 seconds
  • hbase(main):034:0> scan 'ns1:t1'
  • ROW COLUMN+CELL
  • row1 column=f1:id, timestamp=1498992603965, value=100
  • 1 row(s) in 0.0110 seconds

使用put命令插入行数据,上面的语句的意义是,向ns1:t1表中插入行id为row1的数据,该数据处在f1列族下,数据名为id,数据值为'100';我们可以再插入一行:

  • hbase(main):035:0> put 'ns1:t1','row1','f1:name','Tom'
  • 0 row(s) in 0.0110 seconds
  • hbase(main):036:0> scan 'ns1:t1'
  • ROW COLUMN+CELL
  • row1 column=f1:id, timestamp=1498992603965, value=100
  • row1 column=f1:name, timestamp=1498992808455, value=Tom
  • 1 row(s) in 0.0110 seconds

可以看到,row1行的f1列族有两个列了。我们再插入一条行id为row2的数据:

  • hbase(main):037:0> put 'ns1:t1','row2','f1:age','20'
  • 0 row(s) in 0.0100 seconds
  • hbase(main):038:0> scan 'ns1:t1'
  • ROW COLUMN+CELL
  • row1 column=f1:id, timestamp=1498992603965, value=100
  • row1 column=f1:name, timestamp=1498992808455, value=Tom
  • row2 column=f1:age, timestamp=1498992906560, value=20
  • 2 row(s) in 0.0120 seconds

可以发现,row2行并没有id和name的数据。

  1. 查看行数据(get)

查看行所有数据:

  • hbase(main):039:0> get 'ns1:t1' ,'row1'
  • COLUMN CELL
  • f1:id timestamp=1498992603965, value=100
  • f1:name timestamp=1498992808455, value=Tom
  • 2 row(s) in 0.0440 seconds

查看行指定列族数据:

  • hbase(main):041:0> get 'ns1:t1' ,'row1','f1'
  • COLUMN CELL
  • f1:id timestamp=1498992603965, value=100
  • f1:name timestamp=1498992808455, value=Tom
  • 2 row(s) in 0.0070 seconds

查看行指定列数据:

  • hbase(main):040:0> get 'ns1:t1' ,'row1','f1:id'
  • COLUMN CELL
  • f1:id timestamp=1498992603965, value=100
  • 1 row(s) in 0.0080 seconds
  1. 删除数据:
  • hbase(main):043:0> delete 'ns1:t1','row1','f1:id'
  • 0 row(s) in 0.0060 seconds
  • hbase(main):044:0> scan 'ns1:t1'
  • ROW COLUMN+CELL
  • row1 column=f1:name, timestamp=1498992808455, value=Tom
  • row2 column=f1:age, timestamp=1498992906560, value=20
  • 2 row(s) in 0.0180 seconds

4. HBase的HDFS结构

在上面的操作之后,我们可以查看HBase元数据信息中的内容,首先查看meta表的内容:

2.HBase中hbase名称空间的meta表的数据信息.png

在meta表中,存放了每张表的regioninfo、seqnumDuringOpen、server、serverstartcode等信息,从上述的查询结果看,可以发现已经存放了hbase:namespacens1:t1两张表的信息。我们再查看namespace表的内容:

3.HBase中hbase名称空间的namespace表的数据信息.png

在namespace表中存放了所有namespace的信息。

我们继续查看HBase在HDFS中存放的数据:

  • ubuntu@s100:~$ hdfs dfs -ls -R /hbase/data/
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:46 /hbase/data/default
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/meta
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/meta/.tabledesc
  • -rw-r--r-- 3 ubuntu supergroup 398 2017-06-28 06:34 /hbase/data/hbase/meta/.tabledesc/.tableinfo.0000000001
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/meta/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 02:19 /hbase/data/hbase/meta/1588230740
  • -rw-r--r-- 3 ubuntu supergroup 32 2017-06-28 06:34 /hbase/data/hbase/meta/1588230740/.regioninfo
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:51 /hbase/data/hbase/meta/1588230740/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:51 /hbase/data/hbase/meta/1588230740/info
  • -rw-r--r-- 3 ubuntu supergroup 5981 2017-07-02 03:42 /hbase/data/hbase/meta/1588230740/info/14cef210dc7d445f88aa5f80d8fd3d2c
  • -rw-r--r-- 3 ubuntu supergroup 5477 2017-07-02 03:51 /hbase/data/hbase/meta/1588230740/info/95d8e59cca4842f8b73180866c60ff2d
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 02:14 /hbase/data/hbase/meta/1588230740/recovered.edits
  • -rw-r--r-- 3 ubuntu supergroup 0 2017-07-02 02:14 /hbase/data/hbase/meta/1588230740/recovered.edits/12.seqid
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/namespace
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/namespace/.tabledesc
  • -rw-r--r-- 3 ubuntu supergroup 312 2017-06-28 06:34 /hbase/data/hbase/namespace/.tabledesc/.tableinfo.0000000001
  • drwxr-xr-x - ubuntu supergroup 0 2017-06-28 06:34 /hbase/data/hbase/namespace/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:52 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7
  • -rw-r--r-- 3 ubuntu supergroup 42 2017-06-28 06:34 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/.regioninfo
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:52 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:52 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/info
  • -rw-r--r-- 3 ubuntu supergroup 4903 2017-07-02 03:52 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/info/141699653b7e4607877bc50a59478f0e
  • -rw-r--r-- 3 ubuntu supergroup 4963 2017-06-28 06:43 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/info/5e5196a4557541448fed5e74fabf6f8b
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 02:14 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/recovered.edits
  • -rw-r--r-- 3 ubuntu supergroup 0 2017-07-02 02:14 /hbase/data/hbase/namespace/32a3ddf03b33e8dfa2bf2aff0ac18fd7/recovered.edits/11.seqid
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1/t1
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1/t1/.tabledesc
  • -rw-r--r-- 3 ubuntu supergroup 276 2017-07-02 03:43 /hbase/data/ns1/t1/.tabledesc/.tableinfo.0000000001
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1/t1/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 04:53 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c
  • -rw-r--r-- 3 ubuntu supergroup 33 2017-07-02 03:43 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/.regioninfo
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 04:53 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/.tmp
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 04:53 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/f1
  • -rw-r--r-- 3 ubuntu supergroup 4969 2017-07-02 04:53 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/f1/96899d25a0f64c659088a4b56d7a451e
  • drwxr-xr-x - ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/recovered.edits
  • -rw-r--r-- 3 ubuntu supergroup 0 2017-07-02 03:43 /hbase/data/ns1/t1/86935a3ccf2742258ca61cd013a9c79c/recovered.edits/2.seqid

可以发现,在/hbase/data目录下,存放了表的数据信息,它的结构如下:

  • /hbase/data/NAMESAPACE/TABLE/REGION_NAME/COLUMN/TABLE_DATA:这些文件存放了表的数据;
  • /hbase/data/NAMESAPACE/TABLE/REGION_NAME/.regioninfo:该文件存放了表的区域信息;
  • /hbase/data/NAMESAPACE/TABLE/.tabledesc/.tableinfo.0000000001:该文件存放了表的信息。

注:在HBase中,当一张表过大时,会将表切分成多份,放置在多个RegionServer上,所以会有表的区域信息。

同时我们可以在WebUI中查看相关的信息:

4.HBase中用户表信息1.png

5.HBase中用户表信息2.png

5. HBase Java API

HBase提供了相应的Java API供我们操作HBase中的数据;我们需要创建一个Maven项目,添加以下的依赖:

  • <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
  • <dependency>
  • <groupId>org.apache.hbase</groupId>
  • <artifactId>hbase-client</artifactId>
  • <version>1.2.4</version>
  • </dependency>

另外,由于我们要连接HBase服务,所以还需要把服务器上相应的hbase-site.xml文件放置classpath下。

  1. 创建名称空间
  • /**
  • * 创建名称空间
  • * @throws Exception
  • */
  • @Test
  • public void createNamespace() throws Exception {
  • Configuration conf = HBaseConfiguration.create();
  • Connection connection = ConnectionFactory.createConnection(conf);
  • // 得到管理程序
  • Admin admin = connection.getAdmin();
  • // 创建名称空间描述符
  • NamespaceDescriptor nsd = NamespaceDescriptor.create("ns2").build();
  • admin.createNamespace(nsd);
  • System.out.println("create namespace over");
  • }

创建后查看HBase信息:

  • hbase(main):013:0> list_namespace
  • NAMESPACE
  • default
  • hbase
  • ns1
  • ns2
  • 4 row(s) in 0.0290 seconds
  1. 创建表
  • /**
  • * 创建表
  • * @throws Exception
  • */
  • @Test
  • public void createTable() throws Exception {
  • Configuration conf = HBaseConfiguration.create();
  • Connection connection = ConnectionFactory.createConnection(conf);
  • // 得到管理程序
  • Admin admin = connection.getAdmin();
  • TableName tabName = TableName.valueOf("t1");
  • // 创建表描述符
  • HTableDescriptor tabd = new HTableDescriptor(tabName);
  • // 创建列族描述符
  • HColumnDescriptor cld = new HColumnDescriptor("f1");
  • tabd.addFamily(cld);
  • admin.createTable(tabd);
  • System.out.println("create table over");
  • }

因为没有指定名称空间,默认会放在default名称空间下:

  • hbase(main):015:0> list_namespace_tables 'default'
  • TABLE
  • t1
  • 1 row(s) in 0.0080 seconds
  • hbase(main):017:0> desc 'default:t1'
  • Table default:t1 is ENABLED
  • default:t1
  • COLUMN FAMILIES DESCRIPTION
  • {NAME => 'f1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TT
  • L => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
  • 1 row(s) in 0.0210 seconds
  1. 插入表数据
  • /**
  • * 插入数据
  • * @throws Exception
  • */
  • @Test
  • public void put() throws Exception {
  • Configuration conf = HBaseConfiguration.create();
  • Connection connection = ConnectionFactory.createConnection(conf);
  • // 获得表
  • Table table = connection.getTable(TableName.valueOf("t1"));
  • // 创建需要put的数据
  • Put put = new Put(Bytes.toBytes("row1"));
  • put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("id"), Bytes.toBytes(1));
  • put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("name"), Bytes.toBytes("Tom"));
  • put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("age"), Bytes.toBytes(22));
  • // put数据并关闭表
  • table.put(put);
  • table.close();
  • System.out.println("insert table over");
  • }

运行后查看插入的数据:

  • hbase(main):018:0> scan 't1'
  • ROW COLUMN+CELL
  • row1 column=f1:age, timestamp=1499004214424, value=\x00\x00\x00\x16
  • row1 column=f1:id, timestamp=1499004214424, value=\x00\x00\x00\x01
  • row1 column=f1:name, timestamp=1499004214424, value=Tom
  • 1 row(s) in 0.0230 seconds
  1. 删除表数据
  • /**
  • * 删除数据
  • * @throws Exception
  • */
  • @Test
  • public void delete() throws Exception {
  • Configuration conf = HBaseConfiguration.create();
  • Connection connection = ConnectionFactory.createConnection(conf);
  • // 获得表
  • Table table = connection.getTable(TableName.valueOf("t1"));
  • // 指定删除的数据
  • Delete delete = new Delete(Bytes.toBytes("row1"));
  • delete.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("id"));
  • table.delete(delete);
  • System.out.println("delete table over");
  • }

运行后查看表数据:

  • hbase(main):018:0> scan 't1'
  • ROW COLUMN+CELL
  • row1 column=f1:age, timestamp=1499004214424, value=\x00\x00\x00\x16
  • row1 column=f1:name, timestamp=1499004214424, value=Tom
  • 1 row(s) in 0.0210 seconds
  1. 扫描数据
  • /**
  • * 扫描数据
  • *
  • * @throws Exception
  • */
  • @Test
  • public void scan() throws Exception {
  • Configuration conf = HBaseConfiguration.create();
  • Connection connection = ConnectionFactory.createConnection(conf);
  • // 获得表
  • Table table = connection.getTable(TableName.valueOf("t1"));
  • // 指定扫描的数据
  • Scan scan = new Scan();
  • ResultScanner scanner = table.getScanner(scan);
  • Iterator<Result> iterator = scanner.iterator();
  • while (iterator.hasNext()) {
  • Result next = iterator.next();
  • NavigableMap<byte[], byte[]> familyMap = next.getFamilyMap(Bytes.toBytes("f1"));
  • for (java.util.Map.Entry<byte[], byte[]> entry : familyMap.entrySet()) {
  • List<Cell> columnCells = next.getColumnCells(Bytes.toBytes("f1"), entry.getKey());
  • for (Cell cell : columnCells) {
  • byte[] row = cell.getRow();
  • byte[] family = cell.getFamily();
  • byte[] qualifier = cell.getQualifier();
  • byte[] value = cell.getValue();
  • Object valueV = null;
  • if ("id".equals(new String(qualifier)) || "age".equals(new String(qualifier))) {
  • valueV = new Integer(Bytes.toInt(value));
  • } else {
  • valueV = new String(value);
  • }
  • long timestamp = cell.getTimestamp();
  • System.out.println("row:" + new String(row) + ", family:" + new String(family) + ", qualifier:"
  • + new String(qualifier) + ", value:" + valueV + ", timestamp:" + timestamp);
  • }
  • }
  • }
  • scanner.close();
  • table.close();
  • System.out.println("scan table over");
  • }

运行上述代码,打印信息如下:

  • row:row1, family:f1, qualifier:age, value:22, timestamp:1499004214424
  • row:row1, family:f1, qualifier:name, value:Tom, timestamp:1499004214424
  • row:row2, family:f1, qualifier:age, value:22, timestamp:1499004601836
  • row:row2, family:f1, qualifier:id, value:1, timestamp:1499004601836
  • row:row2, family:f1, qualifier:name, value:Tom, timestamp:1499004601836
  • scan table over

6. HBase架构

当我们获取HBase的表数据时,HBase需要找到表所在的RegionServer,而RegionServer信息存放在hbase:meta表中,而hbase:meta也存放在某台RegionServer上,也就是说HBase需要首先知道hbase:meta所在的RegionServer的,这些信息其实包含在Zookeeper中:

  • [zk: localhost:2181(CONNECTED) 0] ls /
  • [zookeeper, hbase]
  • [zk: localhost:2181(CONNECTED) 1] ls /hbase
  • [replication, meta-region-server, rs, splitWAL, backup-masters, table-lock, flush-table-proc, region-in-transition, online-snapshot, master, running, recovering-regions, draining, namespace, hbaseid, table]
  • [zk: localhost:2181(CONNECTED) 2] get /hbase/meta-region-server
  • �regionserver:160200F��D�?�PBUF
  • 
  • s101�}��є�+
  • cZxid = 0x20000003b
  • ctime = Sun Jul 02 02:14:39 PDT 2017
  • mZxid = 0x20000003b
  • mtime = Sun Jul 02 02:14:39 PDT 2017
  • pZxid = 0x20000003b
  • cversion = 0
  • dataVersion = 0
  • aclVersion = 0
  • ephemeralOwner = 0x0
  • dataLength = 57
  • numChildren = 0

可以隐约发现,hbase:meta所在的RegionServer是s101服务器,这和我们在WebUI中查看的是一样的:

6.HBase的hbase名称空间的meta数据所在区域.png

注:WAL即Write Ahead Log,写前日志。

HRegionServer即区域服务器,用于管理HRegion,HRegion创建Store(跟列族对应)实例,每个Store对应多个StoreFile,StoreFile是HFile的轻量级封装,HFile负责和底层HDFS进行交互的封装。

6.1. HBase写流程

  1. Client先访问Zookeeper,从Meta表获取相应Region信息,然后找到Meta表的数据;
  2. 根据NameSpace、表名和rowkey根据Meta表的数据找到写入数据对应的Region信息;
  3. 找到对应的RegionServer;
  4. 把数据分别写到HLog和MemStore上一份;
  5. MemStore达到一个阈值后则把数据刷成一个StoreFile文件(若MemStore中的数据有丢失,则可以总HLog上恢复);
  6. 当多个StoreFile文件达到一定的大小后,会触发Compact合并操作,合并为一个StoreFile(这里同时进行版本的合并和数据删除);
  7. 当Storefile大小超过一定阈值后,会把当前的Region分割为两个(Split),并由HMaster分配到相应的HRegionServer,实现负载均衡 。

6.2. HBase读流程

  1. Client先访问Zookeeper,从Meta表读取Region的位置,然后读取Meta表中的数据。Meta中又存储了用户表的Region信息;
  2. 根据NameSpace、表名和rowkey在Meta表中找到对应的Region信息;
  3. 找到这个Region对应的RegionServer;
  4. 查找对应的Region;
  5. 先从MemStore找数据,如果没有,再到StoreFile上读(为了读取的效率)。

7. HBase数据存储过程

HBase在写入文件时,当MemStore达到一定大小会Flush数据到磁盘保存为HFile文件,当HFile小文件过多时会执行Compact操作进行合并,而过多的Compact操作会引起读的性能问题 ,HBase采用Compaction机制来解决这个问题,在HBase中Compaction分为两种:Minor Compaction和Major Compaction 。

  • Minor Compaction是指选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。一次Minor Compaction的结果是更少并且更大的StoreFile。
  • Major Compaction是指将所有的StoreFile合并成一个StoreFile,在这个过程中,标记为Deleted的Cell会被删除,而那些已经Expired的Cell会被丢弃,那些已经超过最多版本数的Cell会被丢弃。一次Major Compaction的结果是一个HStore只有一个StoreFile存在。Major Compaction可以手动或自动触发,然而由于它会引起很多的IO操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间。

Compact操作会在以下的情况下触发检查:

  • MemStore被Flush到磁盘时;
  • 用户执行shell命令compactmajor_compact或调用了相应的API时;
  • HBase后台线程周期性触发检查。

最初,一个Table只有一个HRegion,随着数据写入增加,如果一个HRegion到达一定的大小,就需要Split成两个HRegion,这个大小由hbase.hregion.max.filesize指定,默认为10GB。当Split操作时,两个新的HRegion会在同一个HRegionServer中创建,它们各自包含父HRegion一半的数据,当Split完成后,父HRegion会下线,而新的两个子HRegion会向HMaster注册上线,处于负载均衡的考虑,这两个新的HRegion可能会被HMaster分配到其他的HRegionServer中。