相关概念

HBase作为典型的非关系型数据库,常用于海量非结构化数据的存储。其特点如下所示:

  • 容量大:HBase的单张表在横向和纵向两个维度上都支持弹性扩展,而且可以扩展到百亿行、百万列的数量级。
  • 面向列:与关系型数据库表结构固定不同,HBase数据表的列可以在实际操作中动态增加而不必在设计表时确定,并且可以单独对某一列进行各种设置和操作。
  • 多版本:HBase中通过row和columns确定的为一个存贮单元称为cell。每个 cell都保存着同一份数据的多个版本,并通过时间戳来索引。
  • 稀疏性:对于为空(null)的列,并不占用存储空间
  • 扩展性:由于底层依赖于HDFS,当存储空间不足时,可以动态增加节点来扩充存储
  • 可靠性:由于以来HDFS,通过其节点集群和备份复制保证了数据的可靠性
  • 高性能:底层索引采用LSM数据结构提升了写入性能,采用region切分、主键切分提升读取性能。
HBase的架构体系

如图所示其内部有两个进程服务–主节点Master和从节点RegionServer。当一张表数据量过多时,HBase内部会自动将数据划分为多个Region并由RegionServer进行管理。Master用于管理RegionServer的状态以及存储数据的情况,RegionServer会将自己运行情况以及所管理的数据概况上报给Master以及Zookeeper。

外部依赖于HDFS文件存储系统和ZooKeeper分布式协同处理服务:对于多个分布式节点,每个节点需要安装Hadoop作为namenode或者datanode,并且每个节点都需要使用Zookeeper来进行节点管理与节点之间的联系。

HBase一列多大_HBase

HBase表结构

和关系型数据库只有一个列不同,HBase由多个具体的列Col组成列簇Column Family。在创建表时只需要确定列簇,然后在具体添加数据时再更具需要添加具体的列。列相当于属性,每个列存放对应的属性值。注意一张表的列簇不要超过5个,但是每个列簇具体的列数量没有限制。

Row Key为行主键,用于区分不同的个体。与关系型数据库支持各种复杂的条件查询不同,HBase只支持通过Row Key对数据进行检索。

TimeStamp为时间戳,用于对数据的不同版本进行索引。为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本。用户可以针对每个列族进行设置。

HBase一列多大_HBase_02

安装部署

HBase有三种运行模式:

  • 单机模式:HBase的相关守护进程Master、 RegionServers、 ZooKeeper运行在统一JVM进程,而且数据保存在./tmp文件夹。单节点模式只需要设置conf/hbase-env.sh文件中的JAVA_HOME路径即可通过bin/start-hbase.sh启动单节点模式。
  • 伪分布式:HBase相关进程仍然运行在一台主机上,但是在不同的进程。通过伪分布式,数据会保存在HDFS文件系统中(Hadoop Distributed File System),如下所示为伪分布式的安装
  • 分布式:将HBase运行在一台主机上并没有实际意义,在实际生产中集群会包括多个节点,每个节点上都会运行HBase进程,其中包括Master主机点和备份节点、Zookeeper节点、RegionServer节点。

由于HBase伪分布式依赖于HDFS文件系统和Zookeeper分布式管理,所以需要先安装Hadoop和Zookeeper

安装Hadoop

在Hadoop官网找到所需版本的链接,

HBase一列多大_zookeeper_03


点击进去后找到安装包镜像的链接,然后用wget工具下载到服务器,例如我下载了2.10版本的安装包

wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/hadoop-2.9.2/hadoop-2.9.2.tar.gz

将安装包解压到安装目录/opt/modules下

tar -zxvf hadoop-2.9.2.tar.gz -C /opt/modules

解压之后目录结构如下所示

HBase一列多大_HBase_04

Hadoop所有的配置文件存放在etc/hadoop目录下,其中.cmd结尾的是Windows下对应的配置文件,
修改其中的hadoop-env.sh配置文件中的JAVA_HOME为jdk的安装位置

# The only required environment variable is JAVA_HOME.  All others are
# optional.  When running a distributed configuration it is best to
# set JAVA_HOME in this file, so that it is correctly defined on
# remote nodes.

# The java implementation to use.
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk

修改core-site.xml文件如下,在其中设置hdfs文件访问地址为本机的9000端口

<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

接着修改hdfs-site.xml文件,在其中设置分节点数为1,并且取消节点权限

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

接下来初始化namenode节点,

hadoop-2.9.2/bin/hdfs namenode -format

在hadoop-2.9.2/sbin/start-dfs.sh启动hdfs系统,输入jps可以看到启动的节点

[root@localhost modules]# hadoop-2.9.2/sbin/start-dfs.sh
Starting namenodes on [localhost]
localhost: starting namenode, logging to /opt/modules/hadoop-2.9.2/logs/hadoop-root-namenode-localhost.localdomain.out
localhost: starting datanode, logging to /opt/modules/hadoop-2.9.2/logs/hadoop-root-datanode-localhost.localdomain.out
Starting secondary namenodes [0.0.0.0]
0.0.0.0: starting secondarynamenode, logging to /opt/modules/hadoop-2.9.2/logs/hadoop-root-secondarynamenode-localhost.localdomain.out
[root@localhost modules]# jps
31829 SecondaryNameNode
31557 DataNode
31353 NameNode
32061 Jps

接着访问浏览器的localhost:50070端口会显示Hadoop的基本情况

HBase一列多大_hadoop_05

安装Zookeeper

zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,需要逐台机器去修改,非常麻烦,现在把这些配置全部放到zookeeper上去,保存在 zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 zookeeper 的通知,然后从 zookeeper 获取新的配置信息应用到系统中。

从Zookeeper官网找到所需的版本,并点击到安装包的镜像页面,通过wget进行下载并解压到目标文件夹

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
tar -zxvf apache-zookeeper-3.5.8-bin.tar.gz -C /opt/modules/

修改zookeeper/conf/zoo_sample.cfg为zoo.cfg,并在其中设置zookeeper数据存放位置dataDir
ZooKeeper的AdminServer运行在8080端口,可能引起冲突,修改其端口号admin.serverPort为2180
由于需要配置集群模式的ZooKeeper,需要设置分布节点的信息,其格式为server.id=host:port1:port2 。 其中id为数字1代表第一个节点,之后在该节点的dataDir目录下新建一个myid文件并在其中写上1,如果有第二个分布式节点则为2以此类推。host是该zookeeper进程所在节点的IP地址,port1表示follower和leader交换消息所使用的端口,port2表示选举leader所使用的端口,多个分布式节点的这两个端口应该保持一致。

# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/modules/apache-zookeeper-3.5.8-bin/data
...
admin.serverPort=2180
...
server.1=localhost:2888:3888

接着在zookeeper/bin目录下启动zookeeper服务

./zkServer.sh start

运行成功后输入jps显示如下,其中DataNode、NameNode分别是Hadoop两个节点,QuorumPeerMain为Zookeeper服务

[root@localhost apache-zookeeper-3.5.8-bin]# jps
13504 Jps
24484 DataNode
24334 NameNode
13423 QuorumPeerMain

通过命令行工具zkCli可以对ZooKeeper管理的节点进行操作,如下所示启动zkCli

bin/zkCli.sh

通过ls查看根节点下的字节点,可以看到有hbase、zookeeper两个节点

[zk: localhost:2181(CONNECTED) 0] ls /
[hbase, zookeeper]

节点的增删改查操作如下

# 创建节点testNode内容为test
[zk: localhost:2181(CONNECTED) 1] create /testNode test
Created /testNode
# 创建临时节点
[zk: localhost:2181(CONNECTED) 3] create -e /testNode2 test2
Created /testNode2
# 获取节点内容
[zk: localhost:2181(CONNECTED) 4] get /testNode
test
# 修改节点内容
[zk: localhost:2181(CONNECTED) 5] set /testNode newValue
# 删除节点,节点不能有子节点
[zk: localhost:2181(CONNECTED) 6] delete /testNode2
# 递归删除节点及其子节点
rmr /testNode

在用Java API通过Zookeeper连接时每次报错如下,可以看到已经和服务器建立了连接但是访问hbase节点却出错。这有可能是zookeeper中保存了hbase节点错误的注册信息,因此通过rmr递归删除hbase节点并重新启动hbase让其重新自动注册可以解决这个问题。

HBase一列多大_HBase_06

HBase的安装

从HBase官网找到合适的版本,如下所示为Hadoop和HBase版本支持关系,其中对勾的是可以使用的

HBase一列多大_HBase_07


找到对应的镜像并用wget下载,完成后解压安装包

wget https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/2.2.5/hbase-2.2.5-bin.tar.gz
tar -zxvf hbase-2.2.5-bin.tar.gz -C /opt/modules/

接下来配置hbase,首先修改hbase/conf/hbase-env.sh文件,在其中设置JAVA_HOME地址。并且设置HBASE_MANAGES为false代表不使用HBase自带的Zookeeper。

# This script sets variables multiple times over the course of starting an hbase process,
# so try to keep things idempotent unless you want to take an even deeper look
# into the startup scripts (bin/hbase, etc.)

# The java implementation to use.  Java 1.8+ required.
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
......
# Tell HBase whether it should manage it's own instance of ZooKeeper or not.
export HBASE_MANAGES_ZK=false

接着设置conf/hbase-site.xml文件,

  • 指定其hbase.rootdir为数据存放位置,这里放到localhost:9000即hadoop中
  • 将cluster.distributed设为true开启分布式模式,
  • zookeeper.quorum设置分布式的节点,当前只有本机一个节点localhost,当有多个分布式节点时可以用逗号隔开
  • 在hbase下创建/tmp作为临时文件夹目录tmp.dir
  • 将unsafe.stream.capability.enforce设为false,解决由于文件系统不支持hsync报错而造成启动失败的问题
<property>
    <name>hbase.rootdir</name>
    <value>hdfs://localhost:9000/hbase</value>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>localhost</value>
  </property>
  <property>
    <name>hbase.tmp.dir</name>
    <value>/opt/modules/hbase-2.2.5/tmp</value>
  </property>
  <property>
  	<name>hbase.unsafe.stream.capability.enforce</name>
  	<value>false</value>
  </property>

之后在regionservers文件中设置RegionServer服务器节点,当前为只有localhost

HBase一列多大_zookeeper_08


最后就可以启动HBase服务了,

hbase-2.2.5/bin/start-hbase.sh

成功之后通过jps命令可以看到HMaster进程在运行,并且在服务器的16010端口可以看到如下HBase的UI界面

HBase一列多大_hadoop_09


在启动HBase一会儿后通过jps命令查看HMaster进程挂掉,查看日志报错如下,可以看到是连接本机Hadoop出错导致,这里是由于我的hosts文件设置localhost出错导致

WARN [master/127.0.0.1:16000:becomeActiveMaster] ipc.Client: Failed to connect to server:localhost/0:0:0:0:0:0:0:1:9000: try once and fail.
 java.net.ConnectException: Connection refused

编辑/etc/hosts文件在其中增加一行,将localhost解析为127.0.0.1.如果是Windows系统则需要修改C:\Windows\System32\drivers\etc\hosts文件

127.0.0.1       localhost

通过Shell操作HBase

通过如下命令启动HBase的Shell交互脚本

hbase-2.2.5/bin/hbase shell

创建一个user表包含列簇info

hbase(main):001:0> create 'user','info'

通过list命令查看所有表

hbase(main):004:0> list
TABLE
user
1 row(s)

通过describe查看表的具体信息

hbase(main):005:0> describe 'user'
Table user is ENABLED
user
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS
=> 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLIC
ATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE =
> 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}

1 row(s)

通过put插入一条数据到user表的info列簇中的name列,key为1001,值为jack

put 'user','1001','info:name','jack'

通过scan命令扫描表的所有数据

scan 'user'
ROW                             COLUMN+CELL
 1001                           column=info:name, timestamp=1600173234002, value=jack
 1002                           column=info:name, timestamp=1600173633377, value=mike

通过get获取具体某一条数据,例如获取user表中键为1001的数据

get 'user','1001'
COLUMN                          CELL
 info:name                      timestamp=1600173234002, value=jack

通过delete删除user表键为1001的info列簇数据

delete 'user',1001,'info'

disable禁用表

disable 'user'

通过drop命令删除user表,注意删除之前需要先禁用表

drop 'user'