Notes
Search…
HBase

1. 简介

  • 基于Google Bigtable的开源实现;
  • 分布式、可伸缩;
  • 列式储存(有争议,我认为不是,见下文);
  • 基于HDFS;
  • 不支持RDBMS的一些高级特性,如事务机制,第二索引,高级查询语言;
  • 强读写一致性;
  • 自动切分数据;
  • RegionServer自动失效备援;
  • 数十亿行 * 数百万列 * 数千个版本 = TB或PB级的储存
关于 Bigtable是否为列式存储有一定争议,我认为不是
维基百科定义:
  • 列式数据库是以列相关存储架构进行数据存储的数据库,主要适合于批量数据处理(OLAP)和即时查询。
  • 相对应的是行式数据库,数据以行相关的存储体系架构进行空间分配,主要适合于小批量的数据处理,常用于联机事务型数据处理(OLTP)。
HBase 有这么一个介绍:HBase is a column-oriented database management system that runs on top of Hadoop Distributed File System (HDFS)。由于翻译不当,所以被误认为 HBase 是列式存储数据库。应当翻译为 HBase 是运行在 HDFS 之上的面向列的数据库管理系统
HBase 底层存储的数据结构为 LSM(Log-Structured Merge-Tree),即不是列式存储,也不是行式存储,而是面向列族的。如下图:
那么,HBase 底层存储大致如下:
  • 不同的列族存在不同的文件中。
  • 整个数据是按照 Rowkey 进行字典排序的。
  • 每一列数据在底层 HFile 中是以 KV 形式存储的。
  • 相同的一行数据中,如果列族也一样,那么这些数据是顺序放在一起的。
  • 不同行相同的列族数据是相邻存储的,同一行不同列族的数据是存储在不同位置的。
列式存储的优点:
  • 更容易压缩:列式存储把一列的数据放在一起存储,同一列的数据往往类型是一样的,更容易压缩。
  • 更适合 OLAP:OLTP 通常对数据记录进行增删查改,所以行式存储更适合;但是 OLAP 一般对大量数据进行汇总和分析,更适合列式存储。

2. 数据模型

Google Bigtable Paper中对Bigtable的定义:
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的数据模型非常相似:
  • 表(table):一张表有多行。
  • 行(row):一行包括一个行健(row key),多个列族(column family),一张表中按照行健排序,以行健为索引。如,row1。
  • 列族(column family):每个列族包含多个列(column),需要在建表时定义好。如data、meta。
  • 列(column):每个列都属于某一个列族,以 列族名:列名(column qualifier) 表示。如 meta:minetype。
  • 版本(version):默认是时间戳。
  • 单元格(cell):由[行,列,版本号]来唯一确定。
{
// ...
"row1" : {
"family1" : {
"column1" : {
timestamp2 : "value1",
timestamp3 : "value2"
},
"column2" : {timestamp : "value3"}
},
"family2" : { ... }
},
"row2" : {
"family3" : { ... }
},
// ...
}
官方例子:
Row Key
Time Stamp
Family contents
Family anchor
Family people
"com.cnn.www"
t9
anchor:cnnsi.com = "CNN"
"com.cnn.www"
t8
anchor:my.look.ca = "CNN.com"
"com.cnn.www"
t6
contents:html = "<html>…
"com.cnn.www"
t5
contents:html = "<html>…
"com.cnn.www"
t3
contents:html = "<html>…
"com.example.www"
t5
contents:html = "<html>…
people:author: "John Doe"
表格中的空白单元不会占用物理存储空间,只是概念上存在

3. 安装

hbase有Standalone、Pseudo-Distributed(伪分布式)、Fully Distributed(分布式)三种部署方式;

3.1. Standalone安装

Standalone模式在单节点运行,且所有的daemon(HMaster, HRegionServer, and ZooKeeper)都在一个 JVM 进程中运行;
  • 下载 HBase,建议下载stable版本;
  • 解压并移动目录;
$ tar -xzvf hbase-1.2.6-bin.tar.gz
$ mv hbase-1.2.6 /usr/local/hbase
  • 配置JAVA_HOME环境变量
$ cd /usr/local/hbase
$ vi conf/hbase-env.sh
export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
  • 配置hbase-site.xml
配置属性 hbase.rootdir,file表示使用本地文件系统作为hbase的数据储存,指定hdfs://namenode.example.org:8020/hbase可以指定HDFS作为储存介质;
$ vi conf/hbase-site.xml
<configuration>
<property>
<name>hbase.rootdir</name>
<value>file:///usr/local/hbase/data</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/usr/local/hbase/zookeeper</value>
</property>
</configuration>
  • 启动hbase
$ bin/start-hbase.sh
quit可以退出shell;
$ bin/hbase shell
  • 关闭hbase服务
$ bin/stop-hbase.sh

3.2. 伪分布式安装

伪分布式模式也是在单个节点运行,但是daemon(HMaster, HRegionServer, and ZooKeeper)分别在不同的jvm进程中;
启动多个master
bin/local-master-backup.sh start 2 3 4
启动多个regionserver
bin/local-regionservers.sh start 2 3 4
查看端口占用
lsof -nP -iTCP -sTCP:LISTEN
webUI

3.3. 分布式安装

分布式模式在多个节点中运行,集群有多个节点,每个节点运行一个或多个HBase的daemon,包括主、副Master节点,多个Zookeeper节点,多个RegionServer节点;

4. 操作

HBase Shell是一个Ruby脚本,可以操作HBase数据;
HBase是java写的,所以提供了java API,是访问HBase最快的方式;

4.1. 连接

连接
$ cd /usr/local/hbase
$ bin/hbase shell
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/usr/local/hbase/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/usr/local/hadoop/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.6, rUnknown, Mon May 29 02:25:32 CDT 2017
hbase(main):001:0>
退出
hbase(main):001:0> exit
java API
Configuration conf = HBaseConfiguration.create();
conf.set(HConstants.ZOOKEEPER_QUORUM, "172.16.233.78");
try {
Connection conn = ConnectionFactory.createConnection(conf);
Admin admin = conn.getAdmin();
}finally {
if (conn!=null && !conn.isClosed()) {
conn.close();
}
}

4.2 General Command

status
hbase(main):002:0> status
1 active master, 0 backup masters, 1 servers, 0 dead, 3.0000 average load
version
hbase(main):005:0> version
1.2.6, rUnknown, Mon May 29 02:25:32 CDT 2017
table_help
hbase(main):007:0> table_help
whoami
hbase(main):008:0> whoami
zhaoyun (auth:SIMPLE)
groups: staff, everyone, localaccounts, _appserverusr, admin, _appserveradm, _lpadmin, com.apple.sharepoint.group.1, _appstore, _lpoperator, _developer, com.apple.access_ftp, com.apple.access_screensharing, com.apple.access_ssh-disabled

4.3 Table Operation

Create Table
创建一个名为element的表,含量两个列族,分别为base和ext;
hbase(main):021:0> create 'element' , 'base', 'ext'
java API
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("element"));
tableDescriptor.addFamily(new HColumnDescriptor("base"));
tableDescriptor.addFamily(new HColumnDescriptor("ext"));
admin.createTable(tableDescriptor);
List Table
hbase(main):021:0> list
java API
HTableDescriptor[] tables = admin.listTables();
Disable Table
若要删除或者修改一个表,先需要disable它;
hbase(main):021:0> disable 'element'
hbase(main):030:0> is_disabled 'element'
hbase(main):032:0> disable_all 'e.*'
java API
if (!admin.isTableDisabled(TableName.valueOf("element"))) {
admin.disableTable(TableName.valueOf("element"));
}
Enable Table
hbase(main):021:0> enable 'element'
hbase(main):021:0> is_enabled 'element'
java API
if (!admin.isTableEnabled(TableName.valueOf("element"))) {
admin.enableTable(TableName.valueOf("element"));
}
Describe Table
hbase(main):021:0> describe 'element'
Table element is ENABLED
element
COLUMN FAMILIES DESCRIPTION
{NAME => 'base', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0',
BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
{NAME => 'ext', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', B
LOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
Alter Table
# 把element表的base列族的VERSIONS属性设为5
hbase(main):021:0> alter 'element', NAME => 'base', VERSIONS => 5
hbase(main):021:0> alter 'element' , READONLY
hbase(main):021:0> alter 'element', METHOD => 'table_att_unset',NAME => 'MAX_FILESIZE'
# 删除列族ext
hbase(main):021:0> alter 'element', 'delete' => 'ext'
java API
admin.addColumn(TableName.valueOf("element"), new HColumnDescriptor("newColFamily"));
admin.deleteColumn(TableName.valueOf("element"), "newColFamily".getBytes());
Exists
hbase(main):021:0> exists 'element'
Table element does exist
java API
boolean exists = admin.tableExists(TableName.valueOf("element"));
Drop Table
在删除一个表时,先必须disable它;
hbase(main):021:0> drop 'element'
hbase(main):021:0> drop_all 'ele.*'
java API
if (!admin.isTableDisabled(TableName.valueOf("element"))) {
admin.disableTable(TableName.valueOf("element"));
}
admin.deleteTable(TableName.valueOf("element"));

4.4 Data Operation

Scan
hbase(main):021:0> scan 'element'
Create Data
向element表的第1行的base列族添加elementId列,值设为1234;
hbase(main):021:0> put 'element', '1', 'base:elementId','1234'
java API
Table table = conn.getTable(TableName.valueOf("element"));
Put put = new Put("1".getBytes());
put.addColumn("base".getBytes(), "elementId".getBytes(), "1234".getBytes());
table.put(put);
table.close();
Update Data
把element表的第1行的base列族的elementId列的值更新为12345;
hbase(main):021:0> put 'element', '1', 'base:elementId','12345'
java API
Table table = conn.getTable(TableName.valueOf("element"));
Put put = new Put("1".getBytes());
put.addColumn("base".getBytes(), "elementId".getBytes(), "12345".getBytes());
table.put(put);
table.close();
Read Data
hbase(main):021:0> get 'element' , '1'
COLUMN CELL
base:elementId timestamp=1501833865714, value=12345
java API
Table table = conn.getTable(TableName.valueOf("element"));
Get get = new Get("1".getBytes());
Result result = table.get(get);
table.close();
Delete Data
# delete a cell
hbase(main):021:0> delete 'element', '1','base:elementId'
# delete a row
hbase(main):021:0> delete 'element', '1'
java API
Table table = conn.getTable(TableName.valueOf("element"));
Delete delete = new Delete("1".getBytes());
delete.addColumn("base".getBytes(), "elementId".getBytes());
table.delete(delete);
table.close();
Count
hbase(main):021:0> count 'element'
Truncate
hbase(main):021:0> truncate 'element'

4.5 过滤器

HBase主要的数据读取函数是get()和scan(),它们都是指定行健来访问数据。可以在查询中添加更多的限制条件(过滤器)来减少查询得到的数据量。
过滤器在客户端创建,通过RPC传送到服务端,然后在服务端执行过滤操作。
比较过滤器、列族过滤器、列名过滤器、值过滤器、时间戳过滤器、自定义过滤器。。。

4.6 协处理器

coprocessor,可以让用户把一部分计算移动到数据存放端(RegionServer)。
数据的处理流程直接放到服务器上执行,然后返回一个小的处理结果集。类似于一个MapReduce框架,将工作分发到整个集群。
协处理器在每个region中按照顺序执行。

4.7 Phoenix

phoenix把SQL语句编译成HBase API,提供二级索引等功能。

5. 架构

  • HMaster:负责监控集群、管理RegionServers的负责均衡等,可以用主-备形式部署多个Master。
  • HRegionServer:负责响应用户的I/O操作请求,客户端对HBase读写数据是与RegionServer交互。
  • Zookeeper:负责选举Master的主节点;服务注册;保存RegionServers的状态等。可以使用系统内建的zookeeper,也可以使用独立的zookeeper。
  • HDFS:真正的数据持久层,并非必须是HDFS文件系统,但搭配HDFS是最佳选择,也是目前应用最广泛的选择。

5.1 hbase:meta

  • 所有的region信息;
  • 保存在Zookeeper中;

5.2 HMaster

  • 分配Region:1、启动时;2、RegionServer失效时;3、Region切分时;
  • 监控集群中的所有RegionServer,实现其负载均衡;
  • DDL:表格的创建、删除和更新-列族的更新;
  • HDFS上的垃圾文件回收;

5.3 RegionServer

HRegionServer是HBase中最主要的组件,负责table数据的实际读写,管理Region。
在分布式集群中,HRegionServer一般跟DataNode在同一个节点上,目的是实现数据的本地性,提高读写效率。
  • 响应client的读写请求,进行I/O操作(直接绕过HMaster);
  • 与HDFS交互,管理table数据;
  • 当Region的大小到达阀值时切分Region;

5.3.1 功能

  • 定期向Master汇报;
  • 管理Region,执行Flush、Compaction、Open、Close、Load等操作;
  • 管理WAL;
  • 执行数据插入、更新和删除操作;

5.3.2 组件

5.3.2.1 WAL: Write Ahead Log
  • 记录RegionServer上的所有编辑信息(Puts/Deletes操作,属于哪个Region),在写到memstore之前;
  • 用于RegionServer失效时,通过Replay恢复RegionServer上memstore中尚未持久化的数据;
5.3.2.2 MemStore
  • 是Region中的重要组成部分;
  • 写缓存;
  • 数据先写到MemStore,flush触发后刷新到磁盘;
  • KeyValue的形式;
5.3.2.3 BlockCache
  • 读缓存;
  • 每个RegionServer中只有一个BlockCache实例;
5.3.2.4 Region
  • HBase表格根据row key 划分成“Regions”;
  • 一个Region包含该表格中从起始key到结束key之间的所有行;
  • 当Region的大小达到指定的阀值时,RegionServer会执行Region的切分,分裂执行完毕后,会将子Region添加到hbase:meta并且汇报给Master;
可以预建分区以减少Region切分

5.3.3 流程

5.3.3.1 初始化
  • Client从ZooKeeper中读取hbase:meta表;
  • Clinet从hbase:meta获取想要操作的Region的位置信息,缓存;
  • Client向目标Region所在的RegionServer发送请求,执行操作;
  • 当一个region因为Master执行负载均衡或者RegionServer挂掉而执行的重定位之后,Client需要重新读取hbase:meta进行缓存;
5.3.3.2 写
  • Client提交一个Put请求到RegionServer,数据首先会写到WAL中;
  • 当数据写到WAL之后,数据会写到MemStore中,等待刷新到磁盘中;
  • 数据写到MemStore完成之后,RS会给Client发送确认信息;
5.3.3.3 读
  • 首先扫描BlockCache(读缓存)中寻找row cell;
  • 若没有,扫描MemStore(写缓存)中寻找row cell;
  • 若没有,HBase会使用BlockCache索引和bloom filters来加载那包含目标row cells的HFile到内存;

5.4 Zookeeper

  • 存储hbase:meta,即所有Region的位置信息;
  • 存储HBase中表格的元数据信息;
  • ZooKeeper集群本身使用一致性协议(PAXOS协议)保证每个节点状态的一致性;
  • 保证集群中有且只有一个HMaster为Active;

6. FAQ

HBase与Hive的对比

HBase
Hive
类型
列式数据库
数据仓库
内部机制
数据库引擎
MapReduce
增删改查
都支持
只支持导入和查询
Schema
只需要预先定义列族,不需要具体到列列可以动态修改
需要预先定义表格
应用场景
实时
离线处理
特点
以K-V形式存储
类SQL

HRegionServer宕机如何处理

  • ZooKeeper会监控HRegionServer的上下线情况,当ZK发现某个HRegionServer宕机之后会通知HMaster;
  • 该HRegionServer会停止对外提供服务,就是它所负责的region暂时停止对外提供服务;
  • HMaster会将该HRegionServer所负责的region转移到其他HRegionServer上,并且会对HRegionServer上存在memstore中还未持久化到磁盘中的数据进行恢复;
  • 这个恢复的工作是由WAL replay来完成;

总结

参考文献

Copy link
On this page
1. 简介
2. 数据模型
3. 安装
3.1. Standalone安装
3.2. 伪分布式安装
3.3. 分布式安装
4. 操作
4.1. 连接
4.2 General Command
4.3 Table Operation
4.4 Data Operation
4.5 过滤器
4.6 协处理器
4.7 Phoenix
5. 架构
5.1 hbase:meta
5.2 HMaster
5.3 RegionServer
5.4 Zookeeper
6. FAQ
HBase与Hive的对比
HRegionServer宕机如何处理
总结
参考文献