欢迎投稿

今日深度:

HBase官方文档中文版,hbase官方文档

HBase官方文档中文版,hbase官方文档


from:  http://abloz.com/hbase/book.html

Apache HBase™ 参考指南

HBase 官方文档中文版

Revision History
Revision 0.97.0-SNAPSHOT 2013-04-07T14:59
中文版翻译整理 周海汉


译者:HBase新版 0.97 文档和0.90版相比,变化较大,文档补充更新了很多内容,章节调整较大。本翻译文档的部分工作基于颜开工作。英文原文地址在此处。旧版0.90版由颜开翻译文档在此处。0.97版翻译最后更新请到此处( http://abloz.com/hbase/book.html )浏览。反馈和参与请到此处 (https://github.com/ablozhou/hbasedoc_cn/)或访问我的blog(http://abloz.com),或给我发email。

可以通过浏览器直接存储为pdf或本地文件。

贡献者:

周海汉邮箱:ablozhou@gmail.com, QQ:7268188 网址:http://abloz.com/
颜开邮箱: yankaycom@gmail.com, 网址:http://www.yankay.com/


摘要

这是 Apache HBase (TM)的官方文档。 HBase是一个分布式,版本化,面向列的数据库,构建在 Apache Hadoop和 Apache ZooKeeper之上。

 


目录

1. 入门
1.1. 介绍
1.2. 快速开始
2. Apache HBase (TM)配置
2.1. 基础条件
2.2. HBase 运行模式: 独立和分布式
2.3. 配置文件
2.4. 配置示例
2.5. 重要配置
 
3. 升级
3.1. 从 0.94.x 升级到 0.96.x
3.2. 从 0.92.x 升级到 0.94.x
3.3. 从 0.90.x 升级到 0.92.x
3.4. 从0.20x或0.89x升级到0.90.x
 
4. HBase Shell
4.1. 使用脚本
4.2. Shell 技巧
5. 数据模型
5.1. 概念视图
5.2. 物理视图
5.3. 表
5.4. 行
5.5. 列族
5.6. Cells
5.7. Data Model Operations
5.8. 版本
5.9. 排序
5.10. 列元数据
5.11. Joins
5.12. ACID
6. HBase 和 Schema 设计
6.1. Schema 创建
6.2. column families的数量
6.3. Rowkey 设计
6.4. 版本数量
6.5. 支持的数据类型
6.6. Joins
6.7. 生存时间 (TTL)
6.8. 保留删除的单元
6.9. 第二索引和替代查询路径
6.10. 限制
6.11. 模式设计用例
6.12. 操作和性能配置选项
 
7. HBase 和 MapReduce
7.1. Map-Task 分割
7.2. HBase MapReduce 示例
7.3. 在MapReduce工作中访问其他 HBase 表
7.4. 推测执行
8. HBase安全
8.1. 安全客户端访问 HBase
8.2. 访问控制
8.3. 安全批量加载
9. 架构
9.1. 概述
9.2. 目录表
9.3. 客户端
9.4. 客户请求过滤器
9.5. Master
9.6. RegionServer
9.7. 分区(Regions)
9.8. 批量加载
9.9. HDFS
10. 外部 APIs
10.1. 非Java语言和 JVM交互
10.2. REST
10.3. Thrift
10.4. C/C++ Apache HBase Client
11. 性能调优
11.1. 操作系统
11.2. 网络
11.3. Java
11.4. HBase 配置
11.5. ZooKeeper
11.6. Schema 设计
11.7. HBase General Patterns
11.8. 写到 HBase
11.9. 从 HBase读取
11.10. 从 HBase删除
11.11. HDFS
11.12. Amazon EC2
11.13. 案例
12. 故障排除和调试 HBase
12.1. 通用指引
12.2. Logs
12.3. 资源
12.4. 工具
12.5. 客户端
12.6. MapReduce
12.7. NameNode
12.8. 网络
12.9. RegionServer
12.10. Master
12.11. ZooKeeper
12.12. Amazon EC2
12.13. HBase 和 Hadoop 版本相关
12.14. 案例
13. 案例研究
13.1. 概要
13.2. Schema 设计
13.3. 性能/故障排除
14. HBase 运维管理
14.1. HBase 工具和实用程序
14.2. 分区管理
14.3. 节点管理
14.4. HBase 度量(Metrics)
14.5. HBase 监控
14.6. Cluster 复制
14.7. HBase 备份
14.8. 容量规划
15. 创建和开发 HBase
15.1. HBase 仓库
15.2. IDEs
15.3. 创建 HBase
15.4. 添加 Apache HBase 发行版到Apache的 Maven Repository
15.5. 生成HBase 参考指南
15.6. 更新 hbase.apache.org
15.7. 测试
15.8. Maven 创建命令
15.9. 加入
15.10. 开发
15.11. 提交补丁
16. ZooKeeper
16.1. 和已有的ZooKeeper一起使用
16.2. 通过ZooKeeper 的SASL 认证
17. 社区
17.1. 决策
17.2. 社区角色
A. FAQ
B. 深入hbck
B.1. 运行 hbck 以查找不一致
B.2. 不一致(Inconsistencies)
B.3. 局部修补
B.4. 分区重叠修补
C. HBase中的压缩
C.1. CompressionTest 工具
C.2. hbase.regionserver.codecs
C.3. LZO
C.4. GZIP
C.5. SNAPPY
C.6. 修改压缩 Schemes
D. YCSB: Yahoo! 云服务评估和 HBase
 
E. HFile 格式版本 2
E.1. Motivation
E.2. HFile 格式版本 1 概览
E.3. HBase 文件格式带 inline blocks (version 2)
F. HBase的其他信息
F.1. HBase 视频
F.2. HBase 展示 (Slides)
F.3. HBase 论文
F.4. HBase 网站
F.5. HBase 书籍
F.6. Hadoop 书籍
G. HBase 历史
 
H. HBase 和 Apache 软件基金会(ASF)
H.1. ASF开发进程
H.2. ASF 报告板
I. Enabling Dapper-like Tracing in HBase
I.1. SpanReceivers
I.2. Client Modifications
J. 0.95 RPC Specification
J.1. Goals
J.2. TODO
J.3. RPC
J.4. Notes
词汇表

表索引

2.1. Hadoop version support matrix
5.1. Table webtable
5.2. ColumnFamily anchor
5.3. ColumnFamily contents
8.1. Operation To Permission Mapping
 

这本书是 HBase 的官方指南。 版本为 0.95-SNAPSHOT 。可以在HBase官网上找到它。也可以在 javadoc, JIRA和 wiki 找到更多的资料。

此书正在编辑中。 可以向 HBase 官方提供补丁JIRA.

这个版本系译者水平限制,没有理解清楚或不需要翻译的地方保留英文原文。

最前面的话

若这是你第一次踏入分布式计算的精彩世界,你会感到这是一个有趣的年代。分布式计算是很难的,做一个分布式系统需要很多软硬件和网络的技能。你的集群可以会因为各式各样的错误发生故障。比如HBase本身的Bug,错误的配置(包括操作系统),硬件的故障(网卡和磁盘甚至内存) 如果你一直在写单机程序的话,你需要重新开始学习。这里就是一个好的起点: 分布式计算的谬论.

Chapter 1. 入门

Table of Contents

1.1. 介绍
1.2. 快速开始
1.2.1. 下载解压最新版本
1.2.2. 启动 HBase
1.2.3. Shell 练习
1.2.4. 停止 HBase
1.2.5. 下一步该做什么
 

1.1. 介绍

Section 1.2, “快速开始”会介绍如何运行一个单机版的HBase.他运行在本地磁盘上。 Section 2, “配置” 会介绍如何运行一个分布式的HBase。他运行在HDFS上

1.2. 快速开始

本指南介绍了在单机安装HBase的方法。会引导你通过shell创建一个表,插入一行,然后删除它,最后停止HBase。只要10分钟就可以完成以下的操作。

1.2.1. 下载解压最新版本

选择一个 Apache 下载镜像,下载 HBase Releases. 点击 stable目录,然后下载后缀为 .tar.gz 的文件; 例如 hbase-0.95-SNAPSHOT.tar.gz.

解压缩,然后进入到那个要解压的目录.

$ tar xfz hbase-0.95-SNAPSHOT.tar.gz
$ cd hbase-0.95-SNAPSHOT

现在你已经可以启动HBase了。但是你可能需要先编辑 conf/hbase-site.xml 去配置hbase.rootdir,来选择HBase将数据写到哪个目录 .

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>hbase.rootdir</name>
    <value>file:///DIRECTORY/hbase</value>
  </property>
</configuration>

DIRECTORY 替换成你期望写文件的目录. 默认 hbase.rootdir 是指向 /tmp/hbase-${user.name} ,也就说你会在重启后丢失数据(重启的时候操作系统会清理/tmp目录)

1.2.2. 启动 HBase

现在启动HBase:

$ ./bin/start-hbase.sh
starting Master, logging to logs/hbase-user-master-example.org.out

现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在logs目录,当你启动出问题的时候,可以检查这个日志。

是否安装了 java ?

你需要确认安装了Oracle的1.6 版本的java.如果你在命令行键入java有反应说明你安装了Java。如果没有装,你需要先安装,然后编辑conf/hbase-env.sh,将其中的JAVA_HOME指向到你Java的安装目录。

1.2.3. Shell 练习

shell连接你的HBase

$ ./bin/hbase shell
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version: 0.90.0, r1001068, Fri Sep 24 13:55:42 PDT 2010

hbase(main):001:0>

输入 help 然后 <RETURN> 可以看到一列shell命令。这里的帮助很详细,要注意的是表名,行和列需要加引号。

创建一个名为 test 的表,这个表只有一个 列族 为 cf。可以列出所有的表来检查创建情况,然后插入些值。

hbase(main):003:0> create 'test', 'cf'
0 row(s) in 1.2200 seconds
hbase(main):003:0> list 'table'
test
1 row(s) in 0.0550 seconds
hbase(main):004:0> put 'test', 'row1', 'cf:a', 'value1'
0 row(s) in 0.0560 seconds
hbase(main):005:0> put 'test', 'row2', 'cf:b', 'value2'
0 row(s) in 0.0370 seconds
hbase(main):006:0> put 'test', 'row3', 'cf:c', 'value3'
0 row(s) in 0.0450 seconds

以上我们分别插入了3行。第一个行key为row1, 列为 cf:a, 值是 value1。HBase中的列是由 列族前缀和列的名字组成的,以冒号间隔。例如这一行的列名就是a.

检查插入情况.

Scan这个表,操作如下

hbase(main):007:0> scan 'test'
ROW        COLUMN+CELL
row1       column=cf:a, timestamp=1288380727188, value=value1
row2       column=cf:b, timestamp=1288380738440, value=value2
row3       column=cf:c, timestamp=1288380747365, value=value3
3 row(s) in 0.0590 seconds

Get一行,操作如下

hbase(main):008:0> get 'test', 'row1'
COLUMN      CELL
cf:a        timestamp=1288380727188, value=value1
1 row(s) in 0.0400 seconds

disable 再 drop 这张表,可以清除你刚刚的操作

hbase(main):012:0> disable 'test'
0 row(s) in 1.0930 seconds
hbase(main):013:0> drop 'test'
0 row(s) in 0.0770 seconds 

关闭shell

hbase(main):014:0> exit

1.2.4. 停止 HBase

运行停止脚本来停止HBase.

$ ./bin/stop-hbase.sh
stopping hbase...............

1.2.5. 下一步该做什么

以上步骤仅仅适用于实验和测试。接下来你可以看 Section 2., “配置” ,我们会介绍不同的HBase运行模式,运行分布式HBase中需要的软件 和如何配置。

2. 配置

 

本章是慢速开始配置指导。

HBase有如下需要,请仔细阅读本章节以确保所有的需要都被满足。如果需求没有能满足,就有可能遇到莫名其妙的错误甚至丢失数据。

HBase使用和Hadoop一样配置系统。要配置部署,编辑conf/hbase-env.sh文件中的环境变量——该配置文件主要启动脚本用于获取已启动的集群——然后增加配置到XML文件,如同覆盖HBase缺省配置,告诉HBase用什么文件系统, 全部ZooKeeper位置 [1] 。

在分布模式下运行时,在编辑HBase配置文件之后,确认将conf目录复制到集群中的每个节点。HBase不会自动同步。使用rsync.

[1] 小心编辑XML。确认关闭所有元素。采用 xmllint 或类似工具确认文档编辑后是良好格式化的。

2.1. 基础条件

This section lists required services and some required system configuration.

2.1.1 java

和Hadoop一样,HBase需要Oracle版本的Java6.除了那个有问题的u18版本其他的都可以用,最好用最新的。

2.1. 操作系统

2.1.2.1. ssh

必须安装ssh sshd 也必须运行,这样Hadoop的脚本才可以远程操控其他的Hadoop和HBase进程。ssh之间必须都打通,不用密码都可以登录,详细方法可以Google一下 ("ssh passwordless login").

2.1.2.2. DNS

HBase使用本地 hostname 才获得IP地址. 正反向的DNS都是可以的.

如果你的机器有多个接口,HBase会使用hostname指向的主接口.

如果还不够,你可以设置 hbase.regionserver.dns.interface 来指定主接口。当然你的整个集群的配置文件都必须一致,每个主机都使用相同的网络接口

还有一种方法是设置 hbase.regionserver.dns.nameserver来指定nameserver,不使用系统带的.

2.1.2.3. Loopback IP

HBase expects the loopback IP address to be 127.0.0.1. Ubuntu and some other distributions, for example, will default to 127.0.1.1 and this will cause problems for you.

/etc/hosts should look something like this:

            127.0.0.1 localhost
            127.0.0.1 ubuntu.ubuntu-domain ubuntu

2.1.2.4. NTP

集群的时钟要保证基本的一致。稍有不一致是可以容忍的,但是很大的不一致会造成奇怪的行为。 运行 NTP 或者其他什么东西来同步你的时间.

如果你查询的时候或者是遇到奇怪的故障,可以检查一下系统时间是否正确!

2.1.2.5.  ulimit nproc

HBase是数据库,会在同一时间使用很多的文件句柄。大多数linux系统使用的默认值1024是不能满足的,会导致FAQ: Why do I see "java.io.IOException...(Too many open files)" in my logs?异常。还可能会发生这样的异常

      2010-04-06 03:04:37,542 INFO org.apache.hadoop.hdfs.DFSClient: Exception increateBlockOutputStream java.io.EOFException
      2010-04-06 03:04:37,542 INFO org.apache.hadoop.hdfs.DFSClient: Abandoning block blk_-6935524980745310745_1391901
      

所以你需要修改你的最大文件句柄限制。可以设置到10k。大致的数学运算如下:每列族至少有1个存储文件(StoreFile) 可能达到5-6个如果区域有压力。将每列族的存储文件平均数目和每区域服务器的平均区域数目相乘。例如:假设一个模式有3个列族,每个列族有3个存储文件,每个区域服务器有100个区域,JVM 将打开3 * 3 * 100 = 900 个文件描述符(不包含打开的jar文件,配置文件等)

你还需要修改 hbase 用户的 nproc,在压力下,如果过低会造成 OutOfMemoryError异常[3] [4]。

需要澄清的,这两个设置是针对操作系统的,不是HBase本身的。有一个常见的错误是HBase运行的用户,和设置最大值的用户不是一个用户。在HBase启动的时候,第一行日志会现在ulimit信息,确保其正确。[5]

2.1.2.5.1. 在Ubuntu上设置ulimit

如果你使用的是Ubuntu,你可以这样设置:

在文件 /etc/security/limits.conf 添加一行,如:

hadoop  -       nofile  32768

可以把 hadoop 替换成你运行HBase和Hadoop的用户。如果你用两个用户,你就需要配两个。还有配nproc hard 和 soft limits. 如:

hadoop soft/hard nproc 32000

.

/etc/pam.d/common-session 加上这一行:

session required  pam_limits.so

否则在 /etc/security/limits.conf上的配置不会生效.

还有注销再登录,这些配置才能生效!

2.1.2.6. Windows

HBase没有怎么在Windows下测试过。所以不推荐在Windows下运行.

如果你实在是想运行,需要安装Cygwin 并虚拟一个unix环境.详情请看 Windows 安装指导 . 或者 搜索邮件列表找找最近的关于windows的注意点

2.1.3. hadoop

选择 Hadoop 版本对HBase部署很关键。下表显示不同HBase支持的Hadoop版本信息。基于HBase版本,应该选择合适的Hadoop版本。我们没有绑定 Hadoop 发行版选择。可以从Apache使用 Hadoop 发行版,或了解一下Hadoop发行商产品: http://wiki.apache.org/hadoop/Distributions%20and%20Commercial%20Support

Table 2.1. Hadoop version support matrix

  HBase-0.92.x HBase-0.94.x HBase-0.96
Hadoop-0.20.205 S X X
Hadoop-0.22.x S X X
Hadoop-1.0.x S S S
Hadoop-1.1.x NT S S
Hadoop-0.23.x X S NT
Hadoop-2.x X S S


S = supported and tested,支持
X = not supported,不支持
NT = not tested enough.可以运行但测试不充分

由于 HBase 依赖 Hadoop,它配套发布了一个Hadoop jar 文件在它的 lib 下。该套装jar仅用于独立模式。在分布式模式下,Hadoop版本必须和HBase下的版本一致。用你运行的分布式Hadoop版本jar文件替换HBase lib目录下的Hadoop jar文件,以避免版本不匹配问题。确认替换了集群中所有HBase下的jar文件。Hadoop版本不匹配问题有不同表现,但看起来都像挂掉了。

2.1.3.1. Apache HBase 0.92 and 0.94

HBase 0.92 and 0.94 versions can work with Hadoop versions, 0.20.205, 0.22.x, 1.0.x, and 1.1.x. HBase-0.94 can additionally work with Hadoop-0.23.x and 2.x, but you may have to recompile the code using the specific maven profile (see top level pom.xml)

2.1.3.2. Apache HBase 0.96

Apache HBase 0.96.0 requires Apache Hadoop 1.x at a minimum, and it can run equally well on hadoop-2.0. As of Apache HBase 0.96.x, Apache Hadoop 1.0.x at least is required. We will no longer run properly on older Hadoops such as 0.20.205 or branch-0.20-append. Do not move to Apache HBase 0.96.x if you cannot upgrade your Hadoop[6].

2.1.3.3. Hadoop versions 0.20.x - 1.x

HBase will lose data unless it is running on an HDFS that has a durable sync implementation. DO NOT use Hadoop 0.20.2, Hadoop 0.20.203.0, and Hadoop 0.20.204.0 which DO NOT have this attribute. Currently only Hadoop versions 0.20.205.x or any release in excess of this version -- this includes hadoop-1.0.0 -- have a working, durable sync[7]. Sync has to be explicitly enabled by setting dfs.support.append equal to true on both the client side -- in hbase-site.xml -- and on the serverside in hdfs-site.xml (The sync facility HBase needs is a subset of the append code path).

  <property>      <name>dfs.support.append</name>      <value>true</value>    </property>          

You will have to restart your cluster after making this edit. Ignore the chicken-little comment you'll find in the hdfs-default.xml in the description for thedfs.support.append configuration.

2.1.3.4. Hadoop 安全性

HBase运行在Hadoop 0.20.x上,就可以使用其中的安全特性 -- 只要你用这两个版本0.20S 和CDH3B3,然后把hadoop.jar替换掉就可以了.

2.1.3.5. dfs.datanode.max.xcievers

一个 Hadoop HDFS Datanode 有一个同时处理文件的上限. 这个参数叫 xcievers (Hadoop的作者把这个单词拼错了). 在你加载之前,先确认下你有没有配置这个文件conf/hdfs-site.xml里面的xceivers参数,至少要有4096:

      <property>
        <name>dfs.datanode.max.xcievers</name>
        <value>4096</value>
      </property>
      

对于HDFS修改配置要记得重启.

如果没有这一项配置,你可能会遇到奇怪的失败。你会在Datanode的日志中看到xcievers exceeded,但是运行起来会报 missing blocks错误。例如: 10/12/08 20:10:31 INFO hdfs.DFSClient: Could not obtain block blk_XXXXXXXXXXXXXXXXXXXXXX_YYYYYYYY from any node: java.io.IOException: No live nodes contain current block. Will get new block locations from namenode and retry... [5]

See also Section 13.3.4, “Case Study #4 (xcievers Config)”

2.2. HBase运行模式:单机和分布式

HBase有两个运行模式: Section 2.4.1, “单机模式” 和 Section 2.4.2, “分布式模式”. 默认是单机模式,如果要分布式模式你需要编辑 conf 文件夹中的配置文件.

不管是什么模式,你都需要编辑 conf/hbase-env.sh来告知HBase java的安装路径.在这个文件里你还可以设置HBase的运行环境,诸如 heapsize和其他 JVM有关的选项, 还有Log文件地址,等等. 设置 JAVA_HOME指向 java安装的路径.

2.21. 单机模式

这是默认的模式,在 Section 1.2, “快速开始” 一章中介绍的就是这个模式. 在单机模式中,HBase使用本地文件系统,而不是HDFS ,所有的服务和zooKeeper都运作在一个JVM中。zookeep监听一个端口,这样客户端就可以连接HBase了。

2.2.2. 分布式模式

分布式模式分两种。伪分布式模式是把进程运行在一台机器上,但不是一个JVM.而完全分布式模式就是把整个服务被分布在各个节点上了 [6].

分布式模式需要使用 Hadoop Distributed File System (HDFS).可以参见 HDFS需求和指导来获得关于安装HDFS的指导。在操作HBase之前,你要确认HDFS可以正常运作。

在我们安装之后,你需要确认你的伪分布式模式或者 完全分布式模式的配置是否正确。这两个模式可以使用同一个验证脚本Section 2.2.3, “运行和确认你的安装”。

2.2.2.1. 伪分布式模式

伪分布式模式是一个相对简单的分布式模式。这个模式是用来测试的。不能把这个模式用于生产环节,也不能用于测试性能。

你确认HDFS安装成功之后,就可以先编辑 conf/hbase-site.xml。在这个文件你可以加入自己的配置,这个配置会覆盖 Section 2.6.1.1, “HBase 默认配置” and Section 2.2.2.2.3, “HDFS客户端配置”. 运行HBase需要设置hbase.rootdir 属性.该属性是指HBase在HDFS中使用的目录的位置。例如,要想 /hbase 目录,让namenode 监听locahost的9000端口,只有一份数据拷贝(HDFS默认是3份拷贝)。可以在 hbase-site.xml 写上如下内容

<configuration>
  ...
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://localhost:9000/hbase</value>
    <description>The directory shared by RegionServers.
    </description>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
    <description>The replication count for HLog & HFile storage. Should not be greater than HDFS datanode count.
    </description>
  </property>
  ...
</configuration>

Note

让HBase自己创建 hbase.rootdir

Note

上面我们绑定到 localhost. 也就是说除了本机,其他机器连不上HBase。所以你需要设置成别的,才能使用它。

现在可以跳到 Section 2.2.3, “运行和确认你的安装” 来运行和确认你的伪分布式模式安装了。 [7]

2.2.2.1.1. 伪分布模式配置文件

下面是伪分布模式设置的配置文件示例。

hdfs-site.xml
<configuration>    ...    <property>      <name>dfs.name.dir</name>      <value>/Users/local/user.name/hdfs-data-name</value>    </property>    <property>      <name>dfs.data.dir</name>      <value>/Users/local/user.name/hdfs-data</value>    </property>    <property>      <name>dfs.replication</name>      <value>1</value>    </property>    ...  </configuration>  
hbase-site.xml
<configuration>    ...    <property>      <name>hbase.rootdir</name>      <value>hdfs://localhost:8020/hbase</value>    </property>    <property>      <name>hbase.zookeeper.quorum</name>      <value>localhost</value>    </property>    <property>      <name>hbase.cluster.distributed</name>      <value>true</value>    </property>    ...  </configuration>  
2.2.2.1.2. 伪分布模式附加
2.2.2.1.2.1. 启动

启动初始 HBase 集群...

% bin/start-hbase.sh

在同一服务器启动额外备份主服务器

% bin/local-master-backup.sh start 1

... '1' 表示使用端口 60001 & 60011, 该备份主服务器及其log文件放在logs/hbase-${USER}-1-master-${HOSTNAME}.log.

启动多个备份主服务器...

% bin/local-master-backup.sh start 2 3

可以启动到 9 个备份服务器 (总数10 个).

启动更多 regionservers...

% bin/local-regionservers.sh start 1

'1' 表示使用端口 60201 & 60301 ,log文件在 logs/hbase-${USER}-1-regionserver-${HOSTNAME}.log.

在刚运行的regionserver上增加 4 个额外 regionservers ...

% bin/local-regionservers.sh start 2 3 4 5

支持到 99 个额外regionservers (总100个).

2.2.2.1.2.2. 停止

假设想停止备份主服务器 # 1, 运行...

% cat /tmp/hbase-${USER}-1-master.pid |xargs kill -9

注意 bin/local-master-backup.sh 停止 1 会尝试停止主服务器相关集群。

停止单独 regionserver, 运行...

% bin/local-regionservers.sh stop 1  	                

2.2.2.2. 完全分布式模式

要想运行完全分布式模式,你要进行如下配置,先在 hbase-site.xml, 加一个属性 hbase.cluster.distributed 设置为 true 然后把 hbase.rootdir 设置为HDFS的NameNode的位置。 例如,你的namenode运行在namenode.example.org,端口是9000 你期望的目录是 /hbase,使用如下的配置

<configuration>
  ...
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://namenode.example.org:9000/hbase</value>
    <description>The directory shared by RegionServers.
    </description>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
    <description>The mode the cluster will be in. Possible values are
      false: standalone and pseudo-distributed setups with managed Zookeeper
      true: fully-distributed with unmanaged Zookeeper Quorum (see hbase-env.sh)
    </description>
  </property>
  ...
</configuration>
2.2.2.2.1. regionservers

完全分布式模式的还需要修改conf/regionservers. 在 Section 2.7.1.2, “regionservers” 列出了你希望运行的全部 HRegionServer,一行写一个host (就像Hadoop里面的 slaves 一样). 列在这里的server会随着集群的启动而启动,集群的停止而停止.

2.2.2.2.2. ZooKeeper 和 HBase

 

2.2.2.2.3. HDFS客户端配置

如果你希望Hadoop集群上做HDFS 客户端配置 ,例如你的HDFS客户端的配置和服务端的不一样。按照如下的方法配置,HBase就能看到你的配置信息:

  • hbase-env.sh里将HBASE_CLASSPATH环境变量加上HADOOP_CONF_DIR

  • ${HBASE_HOME}/conf下面加一个 hdfs-site.xml (或者 hadoop-site.xml) ,最好是软连接

  • 如果你的HDFS客户端的配置不多的话,你可以把这些加到 hbase-site.xml上面.

例如HDFS的配置 dfs.replication.你希望复制5份,而不是默认的3份。如果你不照上面的做的话,HBase只会复制3份。

2.2.3. 运行和确认你的安装

首先确认你的HDFS是运行着的。你可以运行HADOOP_HOME中的 bin/start-hdfs.sh 来启动HDFS.你可以通过put命令来测试放一个文件,然后有get命令来读这个文件。通常情况下HBase是不会运行mapreduce的。所以比不需要检查这些。

如果你自己管理ZooKeeper集群,你需要确认它是运行着的。如果是HBase托管,ZoopKeeper会随HBase启动。

用如下命令启动HBase:

bin/start-hbase.sh
这个脚本在HBASE_HOME目录里面。

你现在已经启动HBase了。HBase把log记在 logs 子目录里面. 当HBase启动出问题的时候,可以看看Log.

HBase也有一个界面,上面会列出重要的属性。默认是在Master的60010端口上H (HBase RegionServers 会默认绑定 60020端口,在端口60030上有一个展示信息的界面 ).如果Master运行在 master.example.org,端口是默认的话,你可以用浏览器在 http://master.example.org:60010看到主界面. .

一旦HBase启动,参见Section 1.2.3, “Shell 练习”可以看到如何建表,插入数据,scan你的表,还有disable这个表,最后把它删掉。

可以在HBase Shell停止HBase

$ ./bin/stop-hbase.sh
stopping hbase...............

停止操作需要一些时间,你的集群越大,停的时间可能会越长。如果你正在运行一个分布式的操作,要确认在HBase彻底停止之前,Hadoop不能停.



 

2.3. 配置文件

 

HBase的配置系统和Hadoop一样。在conf/hbase-env.sh配置系统的部署信息和环境变量。 -- 这个配置会被启动shell使用 -- 然后在XML文件里配置信息,覆盖默认的配置。告知HBase使用什么目录地址,ZooKeeper的位置等等信息。 [10] .

当你使用分布式模式的时间,当你编辑完一个文件之后,记得要把这个文件复制到整个集群的conf 目录下。HBase不会帮你做这些,你得用 rsync.

2.3.1. hbase-site.xmlhbase-default.xml

正如Hadoop放置HDFS的配置文件hdfs-site.xml,HBase的配置文件是 conf/hbase-site.xml. 你可以在 Section 2.3.1.1, “HBase 默认配置”找到配置的属性列表。你也可以看有代码里面的hbase-default.xml文件,他在src/main/resources目录下。

不是所有的配置都在 hbase-default.xml出现.只要改了代码,配置就有可能改变,所以唯一了解这些被改过的配置的办法是读源代码本身。

要注意的是,要重启集群才能是配置生效。

2.3.1.1. HBase 默认配置

HBase 默认配置

该文档是用hbase默认配置文件生成的,文件源是 hbase-default.xml

hbase.rootdir

这个目录是region server的共享目录,用来持久化HBase。URL需要是'完全正确'的,还要包含文件系统的scheme。例如,要表示hdfs中的'/hbase'目录,namenode 运行在namenode.example.org的9090端口。则需要设置为hdfs://namenode.example.org:9000/hbase。默认情况下HBase是写到/tmp的。不改这个配置,数据会在重启的时候丢失。

默认: file:///tmp/hbase-${user.name}/hbase

hbase.master.port

HBase的Master的端口.

默认: 60000

hbase.cluster.distributed

HBase的运行模式。false是单机模式,true是分布式模式。若为false,HBase和Zookeeper会运行在同一个JVM里面。

默认: false

hbase.tmp.dir

本地文件系统的临时文件夹。可以修改到一个更为持久的目录上。(/tmp会在重启时清楚)

默认:${java.io.tmpdir}/hbase-${user.name}

hbase.local.dir

作为本地存储,位于本地文件系统的路径。

默认: ${hbase.tmp.dir}/local/


hbase.master.info.port

HBase Master web 界面端口. 设置为-1 意味着你不想让他运行。

默认: 60010

hbase.master.info.bindAddress

HBase Master web 界面绑定的端口

默认: 0.0.0.0

hbase.client.write.buffer

HTable客户端的写缓冲的默认大小。这个值越大,需要消耗的内存越大。因为缓冲在客户端和服务端都有实例,所以需要消耗客户端和服务端两个地方的内存。得到的好处是,可以减少RPC的次数。可以这样估算服务器端被占用的内存: hbase.client.write.buffer * hbase.regionserver.handler.count

默认: 2097152

hbase.regionserver.port

HBase RegionServer绑定的端口

默认: 60020

hbase.regionserver.info.port

HBase RegionServer web 界面绑定的端口 设置为 -1 意味这你不想与运行 RegionServer 界面.

默认: 60030

hbase.regionserver.info.port.auto

Master或RegionServer是否要动态搜一个可以用的端口来绑定界面。当hbase.regionserver.info.port已经被占用的时候,可以搜一个空闲的端口绑定。这个功能在测试的时候很有用。默认关闭。

默认: false

hbase.regionserver.info.bindAddress

HBase RegionServer web 界面的IP地址

默认: 0.0.0.0

hbase.regionserver.class

RegionServer 使用的接口。客户端打开代理来连接region server的时候会使用到。

默认: org.apache.hadoop.hbase.ipc.HRegionInterface

hbase.client.pause

通常的客户端暂停时间。最多的用法是客户端在重试前的等待时间。比如失败的get操作和region查询操作等都很可能用到。

默认: 1000

hbase.client.retries.number

最大重试次数。所有需重试操作的最大值。例如从root region服务器获取root region,Get单元值,行Update操作等等。这是最大重试错误的值。  默认: 10.

默认: 10

hbase.bulkload.retries.number

最大重试次数。 原子批加载尝试的迭代最大次数。 0 永不放弃。默认: 0.

默认: 0

 

hbase.client.scanner.caching

当调用Scanner的next方法,而值又不在缓存里的时候,从服务端一次获取的行数。越大的值意味着Scanner会快一些,但是会占用更多的内存。当缓冲被占满的时候,next方法调用会越来越慢。慢到一定程度,可能会导致超时。例如超过了hbase.regionserver.lease.period。

默认: 100

hbase.client.keyvalue.maxsize

一个KeyValue实例的最大size.这个是用来设置存储文件中的单个entry的大小上界。因为一个KeyValue是不能分割的,所以可以避免因为数据过大导致region不可分割。明智的做法是把它设为可以被最大region size整除的数。如果设置为0或者更小,就会禁用这个检查。默认10MB。

默认: 10485760

hbase.regionserver.lease.period

客户端租用HRegion server 期限,即超时阀值。单位是毫秒。默认情况下,客户端必须在这个时间内发一条信息,否则视为死掉。

默认: 60000

hbase.regionserver.handler.count

RegionServers受理的RPC Server实例数量。对于Master来说,这个属性是Master受理的handler数量

默认: 10

hbase.regionserver.msginterval

RegionServer 发消息给 Master 时间间隔,单位是毫秒

默认: 3000

hbase.regionserver.optionallogflushinterval

将Hlog同步到HDFS的间隔。如果Hlog没有积累到一定的数量,到了时间,也会触发同步。默认是1秒,单位毫秒。

默认: 1000

hbase.regionserver.regionSplitLimit

region的数量到了这个值后就不会在分裂了。这不是一个region数量的硬性限制。但是起到了一定指导性的作用,到了这个值就该停止分裂了。默认是MAX_INT.就是说不阻止分裂。

默认: 2147483647

hbase.regionserver.logroll.period

提交commit log的间隔,不管有没有写足够的值。

默认: 3600000

hbase.regionserver.hlog.reader.impl

HLog file reader 的实现.

默认: org.apache.hadoop.hbase.regionserver.wal.SequenceFileLogReader

hbase.regionserver.hlog.writer.impl

HLog file writer 的实现.

默认: org.apache.hadoop.hbase.regionserver.wal.SequenceFileLogWriter

 
 
hbase.regionserver.nbreservationblocks

储备的内存block的数量(译者注:就像石油储备一样)。当发生out of memory 异常的时候,我们可以用这些内存在RegionServer停止之前做清理操作。

默认: 4

hbase.zookeeper.dns.interface

当使用DNS的时候,Zookeeper用来上报的IP地址的网络接口名字。

默认: default

hbase.zookeeper.dns.nameserver

当使用DNS的时候,Zookeepr使用的DNS的域名或者IP 地址,Zookeeper用它来确定和master用来进行通讯的域名.

默认: default

hbase.regionserver.dns.interface

当使用DNS的时候,RegionServer用来上报的IP地址的网络接口名字。

默认: default

hbase.regionserver.dns.nameserver

当使用DNS的时候,RegionServer使用的DNS的域名或者IP 地址,RegionServer用它来确定和master用来进行通讯的域名.

默认: default

hbase.master.dns.interface

当使用DNS的时候,Master用来上报的IP地址的网络接口名字。

默认: default

hbase.master.dns.nameserver

当使用DNS的时候,RegionServer使用的DNS的域名或者IP 地址,Master用它来确定用来进行通讯的域名.

默认: default

hbase.balancer.period

Master执行region balancer的间隔。

默认: 300000

hbase.regions.slop

当任一区域服务器有average + (average * slop)个分区,将会执行重新均衡。默认 20% slop .

默认:0.2

hbase.master.logcleaner.ttl

Hlog存在于.oldlogdir 文件夹的最长时间, 超过了就会被 Master 的线程清理掉.

默认: 600000

hbase.master.logcleaner.plugins

LogsCleaner服务会执行的一组LogCleanerDelegat。值用逗号间隔的文本表示。这些WAL/HLog cleaners会按顺序调用。可以把先调用的放在前面。你可以实现自己的LogCleanerDelegat,加到Classpath下,然后在这里写下类的全称。一般都是加在默认值的前面。

默认: org.apache.hadoop.hbase.master.TimeToLiveLogCleaner

hbase.regionserver.global.memstore.upperLimit

单个region server的全部memtores的最大值。超过这个值,一个新的update操作会被挂起,强制执行flush操作。

默认: 0.4

hbase.regionserver.global.memstore.lowerLimit

当强制执行flush操作的时候,当低于这个值的时候,flush会停止。默认是堆大小的 35% . 如果这个值和 hbase.regionserver.global.memstore.upperLimit 相同就意味着当update操作因为内存限制被挂起时,会尽量少的执行flush(译者注:一旦执行flush,值就会比下限要低,不再执行)

默认: 0.35

hbase.server.thread.wakefrequency

service工作的sleep间隔,单位毫秒。 可以作为service线程的sleep间隔,比如log roller.

默认: 10000

hbase.server.versionfile.writeattempts

退出前尝试写版本文件的次数。每次尝试由 hbase.server.thread.wakefrequency 毫秒数间隔。

默认: 3

 
hbase.hregion.memstore.flush.size

当memstore的大小超过这个值的时候,会flush到磁盘。这个值被一个线程每隔hbase.server.thread.wakefrequency检查一下。

默认:134217728

hbase.hregion.preclose.flush.size

当一个region中的memstore的大小大于这个值的时候,我们又触发了close.会先运行“pre-flush”操作,清理这个需要关闭的memstore,然后将这个region下线。当一个region下线了,我们无法再进行任何写操作。如果一个memstore很大的时候,flush操作会消耗很多时间。"pre-flush"操作意味着在region下线之前,会先把memstore清空。这样在最终执行close操作的时候,flush操作会很快。

默认: 5242880

hbase.hregion.memstore.block.multiplier

如果memstore有hbase.hregion.memstore.block.multiplier倍数的hbase.hregion.flush.size的大小,就会阻塞update操作。这是为了预防在update高峰期会导致的失控。如果不设上界,flush的时候会花很长的时间来合并或者分割,最坏的情况就是引发out of memory异常。(译者注:内存操作的速度和磁盘不匹配,需要等一等。原文似乎有误)

默认: 2

hbase.hregion.memstore.mslab.enabled

体验特性:启用memStore分配本地缓冲区。这个特性是为了防止在大量写负载的时候堆的碎片过多。这可以减少GC操作的频率。(GC有可能会Stop the world)(译者注:实现的原理相当于预分配内存,而不是每一个值都要从堆里分配)

默认: true

hbase.hregion.max.filesize

最大HStoreFile大小。若某个列族的HStoreFile增长达到这个值,这个Hegion会被切割成两个。 默认: 10G.

默认:10737418240

hbase.hstore.compactionThreshold

当一个HStore含有多于这个值的HStoreFiles(每一个memstore flush产生一个HStoreFile)的时候,会执行一个合并操作,把这HStoreFiles写成一个。这个值越大,需要合并的时间就越长。

默认: 3

hbase.hstore.blockingStoreFiles

当一个HStore含有多于这个值的HStoreFiles(每一个memstore flush产生一个HStoreFile)的时候,会执行一个合并操作,update会阻塞直到合并完成,直到超过了hbase.hstore.blockingWaitTime的值

默认: 7

hbase.hstore.blockingWaitTime

hbase.hstore.blockingStoreFiles所限制的StoreFile数量会导致update阻塞,这个时间是来限制阻塞时间的。当超过了这个时间,HRegion会停止阻塞update操作,不过合并还有没有完成。默认为90s.

默认: 90000

hbase.hstore.compaction.max

每个“小”合并的HStoreFiles最大数量。

默认: 10

hbase.hregion.majorcompaction

一个Region中的所有HStoreFile的major compactions的时间间隔。默认是1天。 设置为0就是禁用这个功能。

默认: 86400000

hbase.storescanner.parallel.seek.enable

允许 StoreFileScanner 并行搜索 StoreScanner, 一个在特定条件下降低延迟的特性。

默认: false

 
hbase.storescanner.parallel.seek.threads

并行搜索特性打开后,默认线程池大小。

默认: 10

 

hbase.mapreduce.hfileoutputformat.blocksize

MapReduce中HFileOutputFormat可以写 storefiles/hfiles. 这个值是hfile的blocksize的最小值。通常在HBase写Hfile的时候,bloocksize是由table schema(HColumnDescriptor)决定的,但是在mapreduce写的时候,我们无法获取schema中blocksize。这个值越小,你的索引就越大,你随机访问需要获取的数据就越小。如果你的cell都很小,而且你需要更快的随机访问,可以把这个值调低。

默认: 65536

hfile.block.cache.size

分配给HFile/StoreFile的block cache占最大堆(-Xmx setting)的比例。默认0.25意思是分配25%,设置为0就是禁用,但不推荐。

默认:0.25

hbase.hash.type

哈希函数使用的哈希算法。可以选择两个值:: murmur (MurmurHash) 和 jenkins (JenkinsHash). 这个哈希是给 bloom filters用的.

默认: murmur

hfile.block.index.cacheonwrite

这允许在写入索引时将非根多级索引块写入到块缓存。

默认: false

hfile.index.block.max.size

当多层次块索引中的叶级、中间层或根级索引块的大小增长到这个大小时,该块将被写入,并开始一个新的块。

默认: 131072

hfile.format.version

新文件使用的HFile格式版本。设置为1来测试向后兼容性。此选项的默认值应符合FixedFileTrailer.MAX_VERSION.

默认: 2

io.storefile.bloom.block.size

复合Bloom滤波器的单个块(“chunk”)的字节大小。这个大小是近似的,因为Bloom块只能在数据块边界中插入,并且每个数据块的键数是不同的。

默认: 131072

hfile.block.bloom.cacheonwrite

允许对复合布鲁姆过滤器的内联块写缓存.

默认: false

hbase.rs.cacheblocksonwrite

块结束时,是否 HFile块应添加块缓存。

默认: false

hbase.rpc.server.engine

用于服务器的RPC调用编组的org.apache.hadoop.hbase.ipc.RpcServerEngine 实现.

默认: org.apache.hadoop.hbase.ipc.ProtobufRpcServerEngine

hbase.ipc.client.tcpnodelay

设置RPC套接字连接不延迟。参考 http://docs.oracle.com/javase/1.5.0/docs/api/java/net/Socket.html#getTcpNoDelay()

默认: true

 
hbase.master.keytab.file

HMaster server验证登录使用的kerberos keytab 文件路径。(译者注:HBase使用Kerberos实现安全)

默认:

hbase.master.kerberos.principal

例如. "hbase/_HOST@EXAMPLE.COM". HMaster运行需要使用 kerberos principal name. principal name 可以在: user/hostname@DOMAIN 中获取. 如果 "_HOST" 被用做hostname portion,需要使用实际运行的hostname来替代它。

默认:

hbase.regionserver.keytab.file

HRegionServer验证登录使用的kerberos keytab 文件路径。

默认:

hbase.regionserver.kerberos.principal

例如. "hbase/_HOST@EXAMPLE.COM". HRegionServer运行需要使用 kerberos principal name. principal name 可以在: user/hostname@DOMAIN 中获取. 如果 "_HOST" 被用做hostname portion,需要使用实际运行的hostname来替代它。在这个文件中必须要有一个entry来描述 hbase.regionserver.keytab.file

默认:

hadoop.policy.file

RPC服务器使用策略配置文件对客户端请求做出授权决策。仅当HBase启用了安全设置可用.

默认: hbase-policy.xml

hbase.superuser

拥有完整的特权用户或组的列表(逗号分隔), 不限于本地存储的 ACLs, 或整个集群. 仅当HBase启用了安全设置可用.

默认:

hbase.auth.key.update.interval

The update interval for master key for authentication tokens in servers in milliseconds. Only used when HBase security is enabled.

默认: 86400000

hbase.auth.token.max.lifetime

The maximum lifetime in milliseconds after which an authentication token expires. Only used when HBase security is enabled.

默认: 604800000

 
zookeeper.session.timeout

ZooKeeper 会话超时.HBase把这个值传递改zk集群,向他推荐一个会话的最大超时时间。详见http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#ch_zkSessions "客户端发送请求超时,服务器响应它可以给客户端的超时"。 单位是毫秒

默认: 180000

zookeeper.znode.parent

ZooKeeper中的HBase的根ZNode。所有的HBase的ZooKeeper会用这个目录配置相对路径。默认情况下,所有的HBase的ZooKeeper文件路径是用相对路径,所以他们会都去这个目录下面。

默认: /hbase

zookeeper.znode.rootserver

ZNode 保存的 根region的路径. 这个值是由Master来写,client和regionserver 来读的。如果设为一个相对地址,父目录就是 ${zookeeper.znode.parent}.默认情形下,意味着根region的路径存储在/hbase/root-region-server.

默认: root-region-server


zookeeper.znode.acl.parent

Root ZNode for access control lists.

默认: acl

hbase.coprocessor.region.classes

一个逗号分隔的协处理器,通过在所有表的默认加载列表。对于任何重写协处理器方法,这些类将按顺序调用。实现你自己的协处理器后,就把它放在HBase的路径,在此添加完全限定名称。协处理器也可以装上设置HTableDescriptor需求。

默认:

hbase.coprocessor.master.classes

一个逗号分隔的org.apache.hadoop.hbase.coprocessor.MasterObserver 微处理器列表,主HMaster进程默认加载。对于任何实现的协处理器方法,列出的类将按顺序调用。实现你自己的MasterObserver后,就把它放在HBase的路径,在此处添加完全限定名称(fully qualified class name)。

默认:

 
hbase.zookeeper.quorum

Zookeeper集群的地址列表,用逗号分割。例如:"host1.mydomain.com,host2.mydomain.com,host3.mydomain.com".默认是localhost,是给伪分布式用的。要修改才能在完全分布式的情况下使用。如果在hbase-env.sh设置了HBASE_MANAGES_ZK,这些ZooKeeper节点就会和HBase一起启动。

默认: localhost

hbase.zookeeper.peerport

ZooKeeper节点使用的端口。详细参见:http://hadoop.apache.org/zookeeper/docs/r3.1.1/zookeeperStarted.html#sc_RunningReplicatedZooKeeper

默认: 2888

hbase.zookeeper.leaderport

ZooKeeper用来选择Leader的端口,详细参见:http://hadoop.apache.org/zookeeper/docs/r3.1.1/zookeeperStarted.html#sc_RunningReplicatedZooKeeper

默认: 3888

hbase.zookeeper.useMulti

指示HBase使用ZooKeeper的多更新功能。这让某些管理员操作完成更迅速,防止一些问题与罕见的复制失败的情况下(见例hbase-2611版本说明)。重要的是:只有设置为true,如果集群中的所有服务器上的管理员3.4版本不会被降级。ZooKeeper管理员3.4之前的版本不支持多更新不优雅地失败如果多更新时把它放在HBase的路径,在这里添加完全限定名称。 (参考 ZOOKEEPER-1495).

默认: false

 
hbase.zookeeper.property.initLimit

ZooKeeper的zoo.conf中的配置。 初始化synchronization阶段的ticks数量限制

默认: 10

hbase.zookeeper.property.syncLimit

ZooKeeper的zoo.conf中的配置。 发送一个请求到获得承认之间的ticks的数量限制

默认: 5

hbase.zookeeper.property.dataDir

ZooKeeper的zoo.conf中的配置。 快照的存储位置

默认: ${hbase.tmp.dir}/zookeeper

hbase.zookeeper.property.clientPort

ZooKeeper的zoo.conf中的配置。 客户端连接的端口

默认: 2181

hbase.zookeeper.property.maxClientCnxns

ZooKeeper的zoo.conf中的配置。 ZooKeeper集群中的单个节点接受的单个Client(以IP区分)的请求的并发数。这个值可以调高一点,防止在单机和伪分布式模式中出问题。

默认: 300

hbase.rest.port

HBase REST server的端口

默认: 8080

hbase.rest.readonly

定义REST server的运行模式。可以设置成如下的值: false: 所有的HTTP请求都是被允许的 - GET/PUT/POST/DELETE. true:只有GET请求是被允许的

默认: false

hbase.defaults.for.version.skip

设置为true,跳过 'hbase.defaults.for.version' 检查。 设置为 true 相对maven 生成很有用,例如ide环境. 你需要将该布尔值设为 true,避免看到RuntimException 抱怨: "hbase-default.xml file seems to be for and old version of HBase (\${hbase.version}), this version is X.X.X-SNAPSHOT"

默认: false

hbase.coprocessor.abortonerror

Set to true to cause the hosting server (master or regionserver) to abort if a coprocessor throws a Throwable object that is not IOException or a subclass of IOException. Setting it to true might be useful in development environments where one wants to terminate the server as soon as possible to simplify coprocessor failure analysis.

默认: false

hbase.online.schema.update.enable

Set true to enable online schema changes. This is an experimental feature. There are known issues modifying table schemas at the same time a region split is happening so your table needs to be quiescent or else you have to be running with splits disabled.

默认: false

hbase.table.lock.enable

Set to true to enable locking the table in zookeeper for schema change operations. Table locking from master prevents concurrent schema modifications to corrupt table state.

默认: true

dfs.support.append

Does HDFS allow appends to files? This is an hdfs config. set in here so the hdfs client will do append support. You must ensure that this config. is true serverside too when running hbase (You will have to restart your cluster after setting it).

默认: true

hbase.thrift.minWorkerThreads

The "core size" of the thread pool. New threads are created on every connection until this many threads are created.

默认: 16

hbase.thrift.maxWorkerThreads

The maximum size of the thread pool. When the pending request queue overflows, new threads are created until their number reaches this number. After that, the server starts dropping connections.

默认: 1000

hbase.thrift.maxQueuedRequests

The maximum number of pending Thrift connections waiting in the queue. If there are no idle threads in the pool, the server queues requests. Only when the queue overflows, new threads are added, up to hbase.thrift.maxQueuedRequests threads.

默认: 1000

hbase.offheapcache.percentage

The amount of off heap space to be allocated towards the experimental off heap cache. If you desire the cache to be disabled, simply set this value to 0.

默认: 0

hbase.data.umask.enable

Enable, if true, that file permissions should be assigned to the files written by the regionserver

默认: false

hbase.data.umask

hbase.data.umask.enable为真时,用于写数据文件文件的权限

默认: 000

hbase.metrics.showTableName

Whether to include the prefix "tbl.tablename" in per-column family metrics. If true, for each metric M, per-cf metrics will be reported for tbl.T.cf.CF.M, if false, per-cf metrics will be aggregated by column-family across tables, and reported for cf.CF.M. In both cases, the aggregated metric M across tables and cfs will be reported.

默认: true

hbase.metrics.exposeOperationTimes

是否报告有关在区域服务器上执行操作的时间的度量。 Get, Put, Delete, Increment, 及 Append 都可以有他们的时间,每CF和每个区域都可以通过Hadoop metrics暴露出来。

默认: true

hbase.master.hfilecleaner.plugins

一个以逗号分隔的HFileCleanerDelegate HFileCleaner调用的服务。这些HFile清除服务被按顺序调用,所以把清除大部分文件的清除服务放在最前面。实现自己的HFileCleanerDelegate,需把它放在HBase的类路径,并在此添加完全限定类名。总是添加上述日志清除服务,因为会被hbase-site.xml的配置覆盖。

默认: org.apache.hadoop.hbase.master.cleaner.TimeToLiveHFileCleaner

hbase.regionserver.catalog.timeout

Catalog Janitor从regionserver到 META的超时值.

默认: 600000

hbase.master.catalog.timeout

Catalog Janitor从master到 META的超时值.

默认: 600000

hbase.config.read.zookeeper.config

设置为true,允许HBaseConfiguration读取 zoo.cfg 文件,获取ZooKeeper配置。切换为true是不推荐的,因为从zoo.cfg文件读取ZK配置功能已废弃。

默认: false

hbase.snapshot.enabled

设置为true允许快照被取/恢复/克隆。

默认: true

hbase.rest.threads.max

REST服务器线程池的最大线程数。池中的线程被重用以处理REST请求。该值控制同时处理的最大请求数。这可能有助于控制REST服务器内存,避免OOM问题。如果线程池已满,传入的请求将被排队等待一些空闲线程。默认值是100.

默认: 100

hbase.rest.threads.min

REST服务器线程池的最小线程数。线程池始终具有该数量的线程,以便REST服务器准备好为传入的请求服务。默认值是 2.

默认: 2

 

2.3.2. hbase-env.sh

在这个文件里面设置HBase环境变量。比如可以配置JVM启动的堆大小或者GC的参数。你还可在这里配置HBase的参数,如Log位置,niceness(译者注:优先级),ssh参数还有pid文件的位置等等。打开文件conf/hbase-env.sh细读其中的内容。每个选项都是有详尽的注释的。你可以在此添加自己的环境变量。

这个文件的改动系统HBase重启才能生效。

2.3.3. log4j.properties

编辑这个文件可以改变HBase的日志的级别,轮滚策略等等。

这个文件的改动系统HBase重启才能生效。 日志级别的更改会影响到HBase UI

2.3.4. 连接HBase集群的客户端配置和依赖

因为HBase的Master有可能转移,所有客户端需要访问ZooKeeper来获得现在的位置。ZooKeeper会保存这些值。因此客户端必须知道Zookeeper集群的地址,否则做不了任何事情。通常这个地址存在 hbase-site.xml 里面,客户端可以从CLASSPATH取出这个文件.

如果你是使用一个IDE来运行HBase客户端,你需要将conf/放入你的 classpath,这样 hbase-site.xml就可以找到了,(或者把hbase-site.xml放到 src/test/resources,这样测试的时候可以使用).

HBase客户端最小化的依赖是 hbase, hadoop, log4j, commons-logging, commons-lang, 和 ZooKeeper ,这些jars 需要能在 CLASSPATH 中找到。

下面是一个基本的客户端 hbase-site.xml 例子:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>example1,example2,example3</value>
    <description>The directory shared by region servers.
    </description>
  </property>
</configuration>
          

2.3.4.1. Java客户端配置

Java是如何读到hbase-site.xml 的内容的

Java客户端使用的配置信息是被映射在一个HBaseConfiguration 实例中. HBaseConfiguration有一个工厂方法, HBaseConfiguration.create();,运行这个方法的时候,他会去CLASSPATH,下找hbase-site.xml,读他发现的第一个配置文件的内容。 (这个方法还会去找hbase-default.xml ; hbase.X.X.X.jar里面也会有一个an hbase-default.xml). 不使用任何hbase-site.xml文件直接通过Java代码注入配置信息也是可以的。例如,你可以用编程的方式设置ZooKeeper信息,只要这样做:

Configuration config = HBaseConfiguration.create();
config.set("hbase.zookeeper.quorum", "localhost");  // Here we are running zookeeper locally

如果有多ZooKeeper实例,你可以使用逗号列表。(就像在hbase-site.xml 文件中做得一样). 这个 Configuration 实例会被传递到 HTable, 之类的实例里面去.



2.4. 配置示例

2.4.1. 简单的分布式HBase安装

这里是一个10节点的HBase的简单示例,这里的配置都是基本的,节点名为 example0, example1... 一直到 example9 . HBase Master 和 HDFS namenode 运作在同一个节点 example0上. RegionServers 运行在节点 example1-example9. 一个 3-节点 ZooKeeper 集群运行在example1, example2, 和 example3,端口保持默认. ZooKeeper 的数据保存在目录 /export/zookeeper. 下面我们展示主要的配置文件-- hbase-site.xml, regionservers, 和 hbase-env.sh -- 这些文件可以在 conf目录找到.

2.4.1.1. hbase-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>example1,example2,example3</value>
    <description>The directory shared by RegionServers.
    </description>
  </property>
  <property>
    <name>hbase.zookeeper.property.dataDir</name>
    <value>/export/zookeeper</value>
    <description>Property from ZooKeeper's config zoo.cfg.
    The directory where the snapshot is stored.
    </description>
  </property>
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://example0:9000/hbase</value>
    <description>The directory shared by RegionServers.
    </description>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
    <description>The mode the cluster will be in. Possible values are
      false: standalone and pseudo-distributed setups with managed Zookeeper
      true: fully-distributed with unmanaged Zookeeper Quorum (see hbase-env.sh)
    </description>
  </property>
</configuration>
      </pre>

2.4.1.2. regionservers

这个文件把RegionServer的节点列了下来。在这个例子里面我们让所有的节点都运行RegionServer,除了第一个节点 example1,它要运行 HBase Master 和 HDFS namenode

    example1
    example3
    example4
    example5
    example6
    example7
    example8
    example9
          

2.4.1.3. hbase-env.sh

下面我们用diff 命令来展示 hbase-env.sh 文件相比默认变化的部分. 我们把HBase的堆内存设置为4G而不是默认的1G.

    
$ git diff hbase-env.sh
diff --git a/conf/hbase-env.sh b/conf/hbase-env.sh
index e70ebc6..96f8c27 100644
--- a/conf/hbase-env.sh
+++ b/conf/hbase-env.sh
@@ -31,7 +31,7 @@ export JAVA_HOME=/usr/lib//jvm/java-6-sun/
 # export HBASE_CLASSPATH=

The maximum amount of heap to use, in MB. Default is 1000.

-# export HBASE_HEAPSIZE=1000
+export HBASE_HEAPSIZE=4096

Extra Java runtime options.

Below are what we set by default. May only work with SUN JVM.

      </pre>

你可以使用 rsync 来同步 conf 文件夹到你的整个集群.



2.5. 重要的配置

下面我们会列举重要 的配置. 这个章节讲述必须的配置和那些值得一看的配置。(译者注:淘宝的博客也有本章节的内容,HBase性能调优,很详尽)。

2.5.1. 必须的配置

参考 Section 2.2, “操作系统” 和 Section 2.3, “Hadoop” 节.

2.5.2. 推荐配置

2.5.2.1. zookeeper.session.timeout

这个默认值是3分钟。这意味着一旦一个server宕掉了,Master至少需要3分钟才能察觉到宕机,开始恢复。你可能希望将这个超时调短,这样Master就能更快的察觉到了。在你调这个值之前,你需要确认你的JVM的GC参数,否则一个长时间的GC操作就可能导致超时。(当一个RegionServer在运行一个长时间的GC的时候,你可能想要重启并恢复它).

要想改变这个配置,可以编辑 hbase-site.xml, 将配置部署到全部集群,然后重启。

我们之所以把这个值调的很高,是因为我们不想一天到晚在论坛里回答新手的问题。“为什么我在执行一个大规模数据导入的时候Region Server死掉啦”,通常这样的问题是因为长时间的GC操作引起的,他们的JVM没有调优。我们是这样想的,如果一个人对HBase不很熟悉,不能期望他知道所有,打击他的自信心。等到他逐渐熟悉了,他就可以自己调这个参数了。

2.5.2.2.  ZooKeeper 实例个数

参考 Section 2.5, “ZooKeeper”.

2.5.2.3. hbase.regionserver.handler.count

这个设置决定了处理用户请求的线程数量。默认是10,这个值设的比较小,主要是为了预防用户用一个比较大的写缓冲,然后还有很多客户端并发,这样region servers会垮掉。有经验的做法是,当请求内容很大(上MB,如大puts, 使用缓存的scans)的时候,把这个值放低。请求内容较小的时候(gets, 小puts, ICVs, deletes),把这个值放大。

当客户端的请求内容很小的时候,把这个值设置的和最大客户端数量一样是很安全的。一个典型的例子就是一个给网站服务的集群,put操作一般不会缓冲,绝大多数的操作是get操作。

把这个值放大的危险之处在于,把所有的Put操作缓冲意味着对内存有很大的压力,甚至会导致OutOfMemory.一个运行在内存不足的机器的RegionServer会频繁的触发GC操作,渐渐就能感受到停顿。(因为所有请求内容所占用的内存不管GC执行几遍也是不能回收的)。一段时间后,集群也会受到影响,因为所有的指向这个region的请求都会变慢。这样就会拖累集群,加剧了这个问题。

你可能会对handler太多或太少有感觉,可以通过 Section 12.2.2.1, “启用 RPC级 日志” ,在单个RegionServer启动log并查看log末尾 (请求队列消耗内存)。

2.5.2.4. 大内存机器的配置

HBase有一个合理的保守的配置,这样可以运作在所有的机器上。如果你有台大内存的集群-HBase有8G或者更大的heap,接下来的配置可能会帮助你. TODO.

2.5.2.5. 压缩

应该考虑启用ColumnFamily 压缩。有好几个选项,通过降低存储文件大小以降低IO,降低消耗且大多情况下提高性能。

参考 Appendix C,  HBase压缩  获取更多信息.

2.5.2.6. 较大 Regions

更大的Region可以使你集群上的Region的总数量较少。 一般来言,更少的Region可以使你的集群运行更加流畅。(你可以自己随时手工将大Region切割,这样单个热点Region就会被分布在集群的更多节点上)。

较少的Region较好。一般每个RegionServer在20到小几百之间。 调整Region大小以适合该数字。

 

0.90.x 版本, 默认情况下单个Region是256MB。Region 大小的上界是 4Gb. 0.92.x 版本, 由于 HFile v2 已经将Region大小支持得大很多, (如, 20Gb).

可能需要实验,基于硬件和应用需要进行配置。

可以调整hbase-site.xml中的 hbase.hregion.max.filesize属性. RegionSize 也可以基于每个表设置:  HTableDescriptor.

2.5.2.7. 管理 Splitting

除了让HBase自动切割你的Region,你也可以手动切割。 [12] 随着数据量的增大,splite会被持续执行。如果你需要知道你现在有几个region,比如长时间的debug或者做调优,你需要手动切割。通过跟踪日志来了解region级的问题是很难的,因为他在不停的切割和重命名。data offlineing bug和未知量的region会让你没有办法。如果一个 HLog 或者 StoreFile由于一个奇怪的bug,HBase没有执行它。等到一天之后,你才发现这个问题,你可以确保现在的regions和那个时候的一样,这样你就可以restore或者replay这些数据。你还可以调优你的合并算法。如果数据是均匀的,随着数据增长,很容易导致split / compaction疯狂的运行。因为所有的region都是差不多大的。用手的切割,你就可以交错执行定时的合并和切割操作,降低IO负载。

为什么我关闭自动split呢?因为自动的splite是配置文件中的 hbase.hregion.max.filesize决定的. 你把它设置成Long.MAX_VALUE是不推荐的做法,要是你忘记了手工切割怎么办.推荐的做法是设置成100GB,一旦到达这样的值,至少需要一个小时执行 major compactions。

那什么是最佳的在pre-splite regions的数量呢。这个决定于你的应用程序了。你可以先从低的开始,比如每个server10个pre-splite regions.然后花时间观察数据增长。有太少的region至少比出错好,你可以之后再rolling split.一个更复杂的答案是这个值是取决于你的region中的最大的storefile。随着数据的增大,这个也会跟着增大。 你可以当这个文件足够大的时候,用一个定时的操作使用Store的合并选择算法(compact selection algorithm)来仅合并这一个HStore。如果你不这样做,这个算法会启动一个 major compactions,很多region会受到影响,你的集群会疯狂的运行。需要注意的是,这样的疯狂合并操作是数据增长造成的,而不是手动分割操作决定的。

如果你 pre-split 导致 regions 很小,你可以通过配置HConstants.MAJOR_COMPACTION_PERIOD把你的major compaction参数调大

如果你的数据变得太大,可以使用org.apache.hadoop.hbase.util.RegionSplitter 脚本来执行针对全部集群的一个网络IO安全的rolling split操作。

 

2.5.2.8. 管理 Compactions

通常管理技术是手动管理主紧缩(major compactions), 而不是让HBase 来做。 缺省HConstants.MAJOR_COMPACTION_PERIOD 是一天。主紧缩可能强行进行,在你并不太希望发生的时候——特别是在一个繁忙系统。关闭自动主紧缩,设置该值为0.

重点强调,主紧缩对存储文件(StoreFile)清理是绝对必要的。唯一变量是发生的时间。可以通过HBase shell进行管理,或通过 HBaseAdmin.

更多信息关于紧缩和紧缩文件选择过程,参考 Section 9.7.5.5, “紧缩”

2.5.2.9.  预测执行 (Speculative Execution)

MapReduce任务的预测执行缺省是打开的,HBase集群一般建议在系统级关闭预测执行,除非在某种特殊情况下需要打开,此时可以每任务配置。设置mapred.map.tasks.speculative.execution 和 mapred.reduce.tasks.speculative.execution 为 false.

2.5.3. 其他配置

2.5.3.1. 负载均衡

负载均衡器(LoadBalancer)是在主服务器上运行的定期操作,以重新分布集群区域。通过hbase.balancer.period 设置,缺省值300000 (5 分钟).

参考 Section 9.5.4.1, “负载均衡” 获取关于负载均衡器( LoadBalancer )的更多信息。

2.5.3.2. 禁止块缓存(Blockcache)

不要关闭块缓存 (通过hbase.block.cache.size 为 0 来设置)。当前如果关闭块缓存会很不好,因为区域服务器会花很多时间不停加载hfile指数。如果工作集如此配置块缓存没有好处,最少应保证hfile指数保存在块缓存内的大小(可以通过查询区域服务器的UI,得到大致的数值。可以看到网页的上方有块指数值统计).

2.5.3.3. 

Scan scan = new Scan();
scan.addColumn(Bytes.toBytes(“cf”),Bytes.toBytes(“attr”));
scan.setStartRow( Bytes.toBytes(“row”)); // start key is inclusive
scan.setStopRow( Bytes.toBytes(“row” + (char)0)); // stop key is exclusive
ResultScanner rs = htable.getScanner(scan);
try {
for (Result r = rs.next(); r != null; r = rs.next()) {
// process result…
} finally {
rs.close(); // always close the ResultScanner!
}

5.7.4. Delete

Delete 从表中删除一行. 删除通过HTable.delete 执行。

HBase 没有修改数据的合适方法。所以通过创建名为墓碑(tombstones)的新标志进行处理。这些墓碑和死去的值,在主紧缩时清除。

参考 Section 5.8.1.5, “Delete” 获取删除列版本的更多信息。参考Section 9.7.5.5, “Compaction” 获取更多有关紧缩的信息。

5.8. 版本

一个 {row, column, version} 元组是HBase中的一个单元(cell).但是有可能会有很多的单元的行和列是相同的,可以使用版本来区分不同的单元.

rows和column key是用字节数组表示的,version则是用一个长整型表示。这个long的值使用 java.util.Date.getTime() 或者 System.currentTimeMillis()产生的。这就意味着他的含义是“当前时间和1970-01-01 UTC的时间差,单位毫秒。”

在HBase中,版本是按倒序排列的,因此当读取这个文件的时候,最先找到的是最近的版本。

有些人不是很理解HBase单元(cell)的意思。一个常见的问题是:

  • 如果有多个包含版本写操作同时发起,HBase会保存全部还是会保持最新的一个?[16]

  • 可以发起包含版本的写操作,但是他们的版本顺序和操作顺序相反吗?[17]

下面我们介绍下在HBase中版本是如何工作的。[18].

5.8.1. HBase的操作(包含版本操作)

在这一章我们来仔细看看在HBase的各个主要操作中版本起到了什么作用。

5.8.1.1. Get/Scan

Gets实在Scan的基础上实现的。可以详细参见下面的讨论 Get 同样可以用 Scan来描述.

默认情况下,如果你没有指定版本,当你使用Get操作的时候,会返回最近版本的Cell(该Cell可能是最新写入的,但不能保证)。默认的操作可以这样修改:

  • 如果想要返回返回两个以上的把版本,参见Get.setMaxVersions()

  • 如果想要返回的版本不只是最近的,参见 Get.setTimeRange()

    要向查询的最新版本要小于或等于给定的这个值,这就意味着给定的'最近'的值可以是某一个时间点。可以使用0到你想要的时间来设置,还要把max versions设置为1.

5.8.1.2. 默认 Get 例子

下面的Get操作会只获得最新的一个版本。

        Get get = new Get(Bytes.toBytes("row1"));
        Result r = htable.get(get);
        byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns current version of value          

5.8.1.3. 含有的版本的Get例子

下面的Get操作会获得最近的3个版本。

        Get get = new Get(Bytes.toBytes("row1"));
        get.setMaxVersions(3);  // will return last 3 versions of row
        Result r = htable.get(get);
        byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns current version of value
        List<KeyValue> kv = r.getColumn(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns all versions of this column       
                    

5.8.1.4. Put

一个Put操作会给一个cell,创建一个版本,默认使用当前时间戳,当然你也可以自己设置时间戳。这就意味着你可以把时间设置在过去或者未来,或者随意使用一个Long值。

要想覆盖一个现有的值,就意味着你的row,column和版本必须完全相等。

5.8.1.4.1. 不指明版本的例子

下面的Put操作不指明版本,所以HBase会用当前时间作为版本。

          Put put = new Put(Bytes.toBytes(row));
          put.add(Bytes.toBytes("cf"), Bytes.toBytes("attr1"), Bytes.toBytes( data));
          htable.put(put);
                    
5.8.1.4.2. 指明版本的例子

下面的Put操作,指明了版本。

          Put put = new Put( Bytes.toBytes(row ));
          long explicitTimeInMs = 555;  // just an example
          put.add(Bytes.toBytes("cf"), Bytes.toBytes("attr1"), explicitTimeInMs, Bytes.toBytes(data));
          htable.put(put);
                    

5.8.1.5. Delete

有三种不同类型的内部删除标记  [19]:

  • Delete: 删除列的指定版本.

  • Delete column: 删除列的所有版本.

  • Delete family: 删除特定列族所有列

当删除一行,HBase将内部对每个列族创建墓碑(非每个单独列)。

删除操作的实现是创建一个墓碑标记。例如,我们想要删除一个版本,或者默认是currentTimeMillis。就意味着“删除比这个版本更早的所有版本”.HBase不会去改那些数据,数据不会立即从文件中删除。他使用删除标记来屏蔽掉这些值。[20]若你知道的版本比数据中的版本晚,就意味着这一行中的所有数据都会被删除。

参考 Section 9.7.5.4, “KeyValue” 获取内部 KeyValue 格式更多信息。

5.8.2. 现有的限制

关于版本还有一些bug(或者称之为未实现的功能),计划在下个版本实现。

5.8.2.1. 删除标记误标新Put 的数据

删除标记操作可能会标记其后put的数据。[21]记住,当写下一个墓碑标记后,只有下一个主紧缩操作发起之后,墓碑才会清除。假设你删除所有<= 时间T的数据。但之后,你又执行了一个Put操作,时间戳<= T。就算这个Put发生在删除操作之后,他的数据也打上了墓碑标记。这个Put并不会失败,但你做Get操作时,会注意到Put没有产生影响。只有一个主紧缩执行后,一切才会恢复正常。如果你的Put操作一直使用升序的版本,这个问题不会有影响。但是即使你不关心时间,也可能出现该情况。只需删除和插入迅速相互跟随,就有机会在同一毫秒中遇到。

5.8.2.2. 主紧缩改变查询的结果

“设想一下,你一个cell有三个版本t1,t2和t3。你的maximun-version设置是2.当你请求获取全部版本的时候,只会返回两个,t2和t3。如果你将t2和t3删除,就会返回t1。但是如果在删除之前,发生了major compaction操作,那么什么值都不好返回了。[22]


5.9. 排序

所有数据模型操作 HBase 返回排序的数据。先是行,再是列族,然后是列修饰(column qualifier), 最后是时间戳(反向排序,所以最新的在前).

5.10. 列的元数据

对列族,没有内部的KeyValue之外的元数据保存。这样,HBase不仅在一行中支持很多列,而且支持行之间不同的列。 由你自己负责跟踪列名。

唯一获取列族的完整列名的方法是处理所有行。HBase内部保存数据更多信息,请参考 Section 9.7.5.4, “KeyValue”.

5.11. 联合查询(Join)

HBase是否支持联合是一个网上常问问题。简单来说 : 不支持。至少不想传统RDBMS那样支持(如 SQL中带 equi-joins 或 outer-joins). 正如本章描述的,读数据模型是 Get 和 Scan.

但并不表示等价联合不能在应用程序中支持,只是必须自己做。 两种方法,要么指示要写到HBase的数据,要么查询表并在应用或MapReduce代码中做联合(如 RDBMS所展示,有几种步骤来实现,依赖于表的大小。如 nested loops vs. hash-joins). 哪个更好?依赖于你准备做什么,所以没有一个单一的回答适合所有方面。

5.12. ACID

参考 ACID Semantics. Lars Hofhansl 也在 ACID in HBase上写了说明.

 


[16] 目前,只有最新的那个是可以获取到的。.

[17] 可以

[18] 参考 HBASE-2406 for discussion of HBase versions. Bending time in HBase makes for a good read on the version, or time, dimension in HBase. It has more detail on versioning than is provided here. As of this writing, the limiitation Overwriting values at existing timestamps mentioned in the article no longer holds in HBase. This section is basically a synopsis of this article by Bruno Dumon.

[19] 参考 Lars Hofhansl's blog for discussion of his attempt adding another, Scanning in HBase: Prefix Delete Marker

[20] 当HBase执行一次major compaction,标记删除的数据会被实际的删除,删除标记也会被删除。

[21] HBASE-2256

[22] 参考垃圾收集: Bending time in HBase

 

Chapter 6. HBase 的 Schema 设计

Table of Contents

6.1. Schema 创建
6.2. 列族的数量
6.3. Rowkey 设计
6.4. 版本数量
6.5. 支持的数据类型
6.6. 联合
6.7. 存活时间 (TTL)
6.8. 保存删除的单元
6.9. 第二索引和改变查询路径
6.10. 模式设计对决
6.11. 业务和性能配置选项
6.12. 常量

一份关于各种NSQL数据库的优点和缺点的通用介绍,就是 Ian Varley的博士论文, No Relation: The Mixed Blessings of Non-Relational Databases。 推荐。也可阅读 Section 9.7.5.4, “KeyValue” ,了解HBase如何内部保存数据。

6.1.  模式(Schema) 创建

可以使用Chapter 4, HBase Shell 或Java API的HBaseAdmin来创建和编辑HBase的模式。

表必须禁用以修改列族,如:

Configuration config = HBaseConfiguration.create();  
HBaseAdmin admin = new HBaseAdmin(conf);    
String table = "myTable";

admin.disableTable(table);

HColumnDescriptor cf1 = …;
admin.addColumn(table, cf1); // adding new ColumnFamily
HColumnDescriptor cf2 = …;
admin.modifyColumn(table, cf2); // modifying existing ColumnFamily

admin.enableTable(table);

参考 Section 2.3.4, “Client configuration and dependencies connecting to an HBase cluster” ,获取更多配置客户端连接的信息。

注意: 0.92.x 支持在线修改模式, 但 0.90.x 需要禁用表。

6.1.1. 模式更新

当表或列族改变时(如 region size, block size), 当下次存在主紧缩及存储文件重写时起作用。

参考 Section 9.7.5, “Store” 获取存储文件的更多信息。


6.2.  列族的数量

现在HBase并不能很好的处理两个或者三个以上的列族,所以尽量让你的列族数量少一些。目前,flush和compaction操作是针对一个Region。所以当一个列族操作大量数据的时候会引发一个flush。那些不相关的列族也有进行flush操作,尽管他们没有操作多少数据。Compaction操作现在是根据一个列族下的全部文件的数量触发的,而不是根据文件大小触发的。当很多的列族在flush和compaction时,会造成很多没用的I/O负载(要想解决这个问题,需要将flush和compaction操作只针对一个列族) 。 更多紧缩信息, 参考Section 9.7.5.5, “Compaction”.

尽量在你的应用中使用一个列族。只有你的所有查询操作只访问一个列族的时候,可以引入第二个和第三个列族.例如,你有两个列族,但你查询的时候总是访问其中的一个,从来不会两个一起访问。

6.2.1. 列族的基数

一个表存在多列族,注意基数(如, 行数). 如果列族A有100万行,列族B有10亿行,列族A可能被分散到很多很多区(及区服务器)。这导致扫描列族A低效。

 

6.3.  行键(RowKey)设计

6.3.1. 单调递增行键/时序数据

在Tom White的Hadoop: The Definitive Guide一书中,有一个章节描述了一个值得注意的问题:在一个集群中,一个导入数据的进程一动不动,所有的client都在等待一个region(就是一个节点),过了一会后,变成了下一个region...如果使用了单调递增或者时序的key就会造成这样的问题。详情可以参见IKai画的漫画monotonically increasing values are bad。使用了顺序的key会将本没有顺序的数据变得有顺序,把负载压在一台机器上。所以要尽量避免时间戳或者(e.g. 1, 2, 3)这样的key。

如果你需要导入时间顺序的文件(如log)到HBase中,可以学习OpenTSDB的做法。他有一个页面来描述他的schema.OpenTSDB的Key的格式是[metric_type][event_timestamp],乍一看,似乎违背了不将timestamp做key的建议,但是他并没有将timestamp作为key的一个关键位置,有成百上千的metric_type就足够将压力分散到各个region了。

6.3.2. 尽量最小化行和列的大小(为何我的存储文件指示很大?)

在HBase中,值是作为一个单元(Cell)保存在系统的中的,要定位一个单元,需要行,列名和时间戳。通常情况下,如果你的行和列的名字要是太大(甚至比value的大小还要大)的话,你可能会遇到一些有趣的情况。例如Marc Limotte 在 HBASE-3551(推荐!)尾部提到的现象。在HBase的存储文件Section 9.7.5.2, “StoreFile (HFile)”中,有一个索引用来方便值的随机访问,但是访问一个单元的坐标要是太大的话,会占用很大的内存,这个索引会被用尽。所以要想解决,可以设置一个更大的块大小,当然也可以使用更小的列名 。压缩也能得到更大指数。参考话题 a question storefileIndexSize 用户邮件列表.

大部分时候,小的低效不会影响很大。不幸的是,这里会是个问题。无论是列族,属性和行键都会在数据中重复上亿次。参考 Section 9.7.5.4, “KeyValue” 获取更多信息,关于HBase 内部保存数据,了解为什么这很重要。

6.3.2.1. 列族

尽量使列族名小,最好一个字符。(如 "d" 表示 data/default).

参考 Section 9.7.5.4, “KeyValue” 获取更多信息,关于HBase 内部保存数据,了解为什么这很重要。

6.3.2.2. 属性

详细属性名 (如, "myVeryImportantAttribute") 易读,最好还是用短属性名 (e.g., "via") 保存到HBase.

参考 Section 9.7.5.4, “KeyValue” 获取更多信息,关于HBase 内部保存数据,了解为什么这很重要。

6.3.2.3. 行键长度

让行键短到可读即可,这样对获取数据有用(e.g., Get vs. Scan)。 短键对访问数据无用,并不比长键对get/scan更好。设计行键需要权衡。

6.3.2.4. 字节模式

long 类型有 8 字节. 8字节内可以保存无符号数字到18,446,744,073,709,551,615. 如果用字符串保存--假设一个字节一个字符--,需要将近3倍的字节数。

不信? 下面是示例代码,可以自己运行一下。

// long
//
long l = 1234567890L;
byte[] lb = Bytes.toBytes(l);
System.out.println("long bytes length: " + lb.length);   // returns 8

String s = “” + l;
byte[] sb = Bytes.toBytes(s);
System.out.println("long as string length: " + sb.length); // returns 10

// hash
//
MessageDigest md = MessageDigest.getInstance(“MD5”);
byte[] digest = md.digest(Bytes.toBytes(s));
System.out.println("md5 digest bytes length: " + digest.length); // returns 16

String sDigest = new String(digest);
byte[] sbDigest = Bytes.toBytes(sDigest);
System.out.println("md5 digest as string length: " + sbDigest.length); // returns 26(译者注:实测值为22)

6.3.3. 倒序时间戳

一个数据库处理的通常问题是找到最近版本的值。采用倒序时间戳作为键的一部分可以对此特定情况有很大帮助。也在Tom White的Hadoop书籍的HBase 章节能找到: The Definitive Guide (O'Reilly), 该技术包含追加(Long.MAX_VALUE - timestamp) 到key的后面,如 [key][reverse_timestamp].

表内[key]的最近的值可以用[key]进行 Scan 找到并获取第一个记录。由于 HBase 行键是排序的,该键排在任何比它老的行键的前面,所以必然是第一个。

该技术可以用于代替Section 6.4, “ 版本的数量 ” ,其目的是保存所有版本到“永远”(或一段很长时间) 。同时,采用同样的Scan技术,可以很快获取其他版本。

6.3.4. 行键和列族

行键在列族范围内。所以同样的行键可以在同一个表的每个列族中存在而不会冲突。

6.3.5. 行键永远不变

行键不能改变。唯一可以“改变”的方式是删除然后再插入。这是一个网上常问问题,所以要注意开始就要让行键正确(且/或在插入很多数据之前)。

6.4.  版本数量

6.4.1. 最大版本数

行的版本的数量是HColumnDescriptor设置的,每个列族可以单独设置,默认是3。这个设置是很重要的,在Chapter 5, 数据模型有描述,因为HBase是不会去覆盖一个值的,他只会在后面在追加写,用时间戳来区分、过早的版本会在执行主紧缩的时候删除。这个版本的值可以根据具体的应用增加减少。

不推荐将版本最大值设到一个很高的水平 (如, 成百或更多),除非老数据对你很重要。因为这会导致存储文件变得极大。

6.4.2.  最小版本数

和行的最大版本数一样,最小版本数也是通过HColumnDescriptor 在每个列族中设置的。最小版本数缺省值是0,表示该特性禁用。 最小版本数参数和存活时间一起使用,允许配置如“保存最后T秒有价值数据,最多N个版本,但最少约M个版本”(M是最小版本数,M<N)。 该参数仅在存活时间对列族启用,且必须小于行版本数。

6.5.  支持数据类型

HBase 通过 Put 和 Result支持 "bytes-in/bytes-out" 接口,所以任何可被转为字节数组的东西可以作为值存入。输入可以是字符串,数字,复杂对象,甚至图像,只要他们能转为字节。

存在值的实际长度限制 (如 保存 10-50MB 对象到 HBase 可能对查询来说太长); 搜索邮件列表获取本话题的对话。 HBase的所有行都遵循 Chapter 5, 数据模型, 包括版本化。 设计时需考虑到这些,以及列族的块大小。

6.5.1. 计数器

一种支持的数据类型,值得一提的是“计数器”(如, 具有原子递增能力的数值)。参考 HTable的 Increment .

同步计数器在区域服务器中完成,不是客户端。

6.6. 联合

如果有多个表,不要在模式设计中忘了 Section 5.11, “Joins” 的潜在因素。

6.7. 存活时间 (TTL)

列族可以设置TTL秒数,HBase 在超时后将自动删除数据。影响 全部 行的全部版本 - 甚至当前版本。HBase里面TTL 时间时区是 UTC.

参考 HColumnDescriptor 获取更多信息。

6.8.  保留删除的单元

列族允许是否保留单元。这就是说  Get 或 Scan 操作仍可以获取删除的单元。由于这些操作指定时间范围,结束在删除单元发生效果之前。这甚至允许在删除进行时进行即时查询。

删除的单元仍然受TTL控制,并永远不会超过“最大版本数”被删除的单元。新 "raw" scan 选项返回所有已删除的行和删除标志。

参考 HColumnDescriptor 获取更多信息

6.9.  第二索引和改变路径查询

本节标题也可以为"如果表的行键像这样 ,但我又想像那样查询该表." A common example on the dist-list is where a row-key is of the format "user-timestamp" but there are are reporting requirements on activity across users for certain time ranges. Thus, selecting by user is easy because it is in the lead position of the key, but time is not.

There is no single answer on the best way to handle this because it depends on...

  • Number of users
  • Data size and data arrival rate
  • Flexibility of reporting requirements (e.g., completely ad-hoc date selection vs. pre-configured ranges)
  • Desired execution speed of query (e.g., 90 seconds may be reasonable to some for an ad-hoc report, whereas it may be too long for others)

... and solutions are also influenced by the size of the cluster and how much processing power you have to throw at the solution. Common techniques are in sub-sections below. This is a comprehensive, but not exhaustive, list of approaches.

It should not be a surprise that secondary indexes require additional cluster space and processing. This is precisely what happens in an RDBMS because the act of creating an alternate index requires both space and processing cycles to update. RBDMS products are more advanced in this regard to handle alternative index management out of the box. However, HBase scales better at larger data volumes, so this is a feature trade-off.

Pay attention to Chapter 11, Performance Tuning when implementing any of these approaches.

Additionally, see the David Butler response in this dist-list thread HBase, mail # user - Stargate+hbase

6.9.1.  过滤查询

根据具体应用,可能和 Section 9.4, “Client Request Filters” 用法相当。在这种情况下,没有第二索引被创建。然而,不要像这样从应用 (如单线程客户端)中对大表尝试全表扫描。

6.9.2.  定期更新第二索引

第二索引可以在另一个表中创建,并通过MapReduce任务定期更新。任务可以在当天执行,但依赖于加载策略,可能会同主表失去同步。

参考 Section 7.2.2, “HBase MapReduce Read/Write Example” 获取更多信息.

6.9.3.  双写第二索引

另一个策略是在将数据写到集群的同时创建第二索引(如:写到数据表,同时写到索引表)。如果该方法在数据表存在之后采用,则需要利用MapReduce任务来生成已有数据的第二索引。 (参考 Section 6.9.2, “ Periodic-Update Secondary Index ”).

6.9.4.  汇总表(Summary Tables)

对时间跨度长 (e.g., 年报) 和数据量巨大,汇总表是通用路径。可通过MapReduce任务生成到另一个表。

参考 Section 7.2.4, “HBase MapReduce Summary to HBase Example” 获取更多信息。

6.9.5.  协处理第二索引

协处理动作像 RDBMS 触发器。 这在 0.92中添加. 更多参考 Section 9.6.3, “Coprocessors”

6.10. 限制

HBase currently supports 'constraints' in traditional (SQL) database parlance. The advised usage for Constraints is in enforcing business rules for attributes in the table (eg. make sure values are in the range 1-10). Constraints could also be used to enforce referential integrity, but this is strongly discouraged as it will dramatically decrease the write throughput of the tables where integrity checking is enabled. Extensive documentation on using Constraints can be found at: Constraint since version 0.94.

 

6.11. 模式(schema)设计用例

This effectively is the OpenTSDB approach. What OpenTSDB does is re-write data and pack rows into columns for certain time-periods. For a detailed explanation, see:http://opentsdb.net/schema.html, and Lessons Learned from OpenTSDB from HBaseCon2012.

But this is how the general concept works: data is ingested, for example, in this manner…

[hostname][log-event][timestamp1]  [hostname][log-event][timestamp2]  [hostname][log-event][timestamp3]  

… with separate rowkeys for each detailed event, but is re-written like this…

[hostname][log-event][timerange]

… and each of the above events are converted into columns stored with a time-offset relative to the beginning timerange (e.g., every 5 minutes). This is obviously a very advanced processing technique, but HBase makes this possible.

6.11.3. Case Study - Customer/Order

Assume that HBase is used to store customer and order information. There are two core record-types being ingested: a Customer record type, and Order record type.

The Customer record type would include all the things that you’d typically expect:

  • Customer number
  • Customer name
  • Address (e.g., city, state, zip)
  • Phone numbers, etc.

The Order record type would include things like:

  • Customer number
  • Order number
  • Sales date
  • A series of nested objects for shipping locations and line-items (see Section 6.11.3.2, “Order Object Design” for details)

Assuming that the combination of customer number and sales order uniquely identify an order, these two attributes will compose the rowkey, and specifically a composite key such as:

[customer number][order number]

… for a ORDER table. However, there are more design decisions to make: are the raw values the best choices for rowkeys?

The same design questions in the Log Data use-case confront us here. What is the keyspace of the customer number, and what is the format (e.g., numeric? alphanumeric?) As it is advantageous to use fixed-length keys in HBase, as well as keys that can support a reasonable spread in the keyspace, similar options appear:

Composite Rowkey With Hashes:

  • [MD5 of customer number] = 16 bytes
  • [MD5 of order number] = 16 bytes

Composite Numeric/Hash Combo Rowkey:

  • [substituted long for customer number] = 8 bytes
  • [MD5 of order number] = 16 bytes

6.11.3.1. Single Table? Multiple Tables?

A traditional design approach would have separate tables for CUSTOMER and SALES. Another option is to pack multiple record types into a single table (e.g., CUSTOMER++).

Customer Record Type Rowkey:

  • [customer-id]
  • [type] = type indicating ‘1’ for customer record type

Order Record Type Rowkey:

  • [customer-id]
  • [type] = type indicating ‘2’ for order record type
  • [order]

The advantage of this particular CUSTOMER++ approach is that organizes many different record-types by customer-id (e.g., a single scan could get you everything about that customer). The disadvantage is that it’s not as easy to scan for a particular record-type.

6.11.3.2. Order Object Design

Now we need to address how to model the Order object. Assume that the class structure is as follows:

Order       ShippingLocation     (an Order can have multiple ShippingLocations)            LineItem               (a ShippingLocation can have multiple LineItems)  

... there are multiple options on storing this data.

6.11.3.2.1. Completely Normalized

With this approach, there would be separate tables for ORDER, SHIPPING_LOCATION, and LINE_ITEM.

The ORDER table's rowkey was described above: Section 6.11.3, “Case Study - Customer/Order”

The SHIPPING_LOCATION's composite rowkey would be something like this:

  • [order-rowkey]
  • [shipping location number] (e.g., 1st location, 2nd, etc.)

The LINE_ITEM table's composite rowkey would be something like this:

  • [order-rowkey]
  • [shipping location number] (e.g., 1st location, 2nd, etc.)
  • [line item number] (e.g., 1st lineitem, 2nd, etc.)

Such a normalized model is likely to be the approach with an RDBMS, but that's not your only option with HBase. The cons of such an approach is that to retrieve information about any Order, you will need:

  • Get on the ORDER table for the Order
  • Scan on the SHIPPING_LOCATION table for that order to get the ShippingLocation instances
  • Scan on the LINE_ITEM for each ShippingLocation

... granted, this is what an RDBMS would do under the covers anyway, but since there are no joins in HBase you're just more aware of this fact.

6.11.3.2.2. Single Table With Record Types

With this approach, there would exist a single table ORDER that would contain

The Order rowkey was described above: Section 6.11.3, “Case Study - Customer/Order”

  • [order-rowkey]
  • [ORDER record type]

The ShippingLocation composite rowkey would be something like this:

  • [order-rowkey]
  • [SHIPPING record type]
  • [shipping location number] (e.g., 1st location, 2nd, etc.)

The LineItem composite rowkey would be something like this:

  • [order-rowkey]
  • [LINE record type]
  • [shipping location number] (e.g., 1st location, 2nd, etc.)
  • [line item number] (e.g., 1st lineitem, 2nd, etc.)
6.11.3.2.3. Denormalized

A variant of the Single Table With Record Types approach is to denormalize and flatten some of the object hierarchy, such as collapsing the ShippingLocation attributes onto each LineItem instance.

The LineItem composite rowkey would be something like this:

  • [order-rowkey]
  • [LINE record type]
  • [line item number] (e.g., 1st lineitem, 2nd, etc. - care must be taken that there are unique across the entire order)

... and the LineItem columns would be something like this:

  • itemNumber
  • quantity
  • price
  • shipToLine1 (denormalized from ShippingLocation)
  • shipToLine2 (denormalized from ShippingLocation)
  • shipToCity (denormalized from ShippingLocation)
  • shipToState (denormalized from ShippingLocation)
  • shipToZip (denormalized from ShippingLocation)

The pros of this approach include a less complex object heirarchy, but one of the cons is that updating gets more complicated in case any of this information changes.

6.11.3.2.4. Object BLOB

With this approach, the entire Order object graph is treated, in one way or another, as a BLOB. For example, the ORDER table's rowkey was described above: Section 6.11.3, “Case Study - Customer/Order”, and a single column called "order" would contain an object that could be deserialized that contained a container Order, ShippingLocations, and LineItems.

There are many options here: JSON, XML, Java Serialization, Avro, Hadoop Writables, etc. All of them are variants of the same approach: encode the object graph to a byte-array. Care should be taken with this approach to ensure backward compatibilty in case the object model changes such that older persisted structures can still be read back out of HBase.

Pros are being able to manage complex object graphs with minimal I/O (e.g., a single HBase Get per Order in this example), but the cons include the aforementioned warning about backward compatiblity of serialization, language dependencies of serialization (e.g., Java Serialization only works with Java clients), the fact that you have to deserialize the entire object to get any piece of information inside the BLOB, and the difficulty in getting frameworks like Hive to work with custom objects like this.

6.11.4. Case Study - "Tall/Wide/Middle" Schema Design Smackdown

This section will describe additional schema design questions that appear on the dist-list, specifically about tall and wide tables. These are general guidelines and not laws - each application must consider its own needs.

6.11.4.1. Rows vs. Versions

A common question is whether one should prefer rows or HBase's built-in-versioning. The context is typically where there are "a lot" of versions of a row to be retained (e.g., where it is significantly above the HBase default of 3 max versions). The rows-approach would require storing a timstamp in some portion of the rowkey so that they would not overwite with each successive update.

Preference: Rows (generally speaking).

6.11.4.2. Rows vs. Columns

Another common question is whether one should prefer rows or columns. The context is typically in extreme cases of wide tables, such as having 1 row with 1 million attributes, or 1 million rows with 1 columns apiece.

Preference: Rows (generally speaking). To be clear, this guideline is in the context is in extremely wide cases, not in the standard use-case where one needs to store a few dozen or hundred columns. But there is also a middle path between these two options, and that is "Rows as Columns."

6.11.4.3. Rows as Columns

The middle path between Rows vs. Columns is packing data that would be a separate row into columns, for certain rows. OpenTSDB is the best example of this case where a single row represents a defined time-range, and then discrete events are treated as columns. This approach is often more complex, and may require the additional complexity of re-writing your data, but has the advantage of being I/O efficient. For an overview of this approach, see ???.

6.11.5. Case Study - List Data

The following is an exchange from the user dist-list regarding a fairly common question: how to handle per-user list data in Apache HBase.

*** QUESTION ***

We're looking at how to store a large amount of (per-user) list data in HBase, and we were trying to figure out what kind of access pattern made the most sense. One option is store the majority of the data in a key, so we could have something like:

<FixedWidthUserName><FixedWidthValueId1>:"" (no value)  <FixedWidthUserName><FixedWidthValueId2>:"" (no value)  <FixedWidthUserName><FixedWidthValueId3>:"" (no value)  			
The other option we had was to do this entirely using:
<FixedWidthUserName><FixedWidthPageNum0>:<FixedWidthLength><FixedIdNextPageNum><ValueId1><ValueId2><ValueId3>...  <FixedWidthUserName><FixedWidthPageNum1>:<FixedWidthLength><FixedIdNextPageNum><ValueId1><ValueId2><ValueId3>...      		

where each row would contain multiple values. So in one case reading the first thirty values would be:

scan { STARTROW => 'FixedWidthUsername' LIMIT => 30}      		
And in the second case it would be
get 'FixedWidthUserName\x00\x00\x00\x00'      		

The general usage pattern would be to read only the first 30 values of these lists, with infrequent access reading deeper into the lists. Some users would have <= 30 total values in these lists, and some users would have millions (i.e. power-law distribution)

The single-value format seems like it would take up more space on HBase, but would offer some improved retrieval / pagination flexibility. Would there be any significant performance advantages to be able to paginate via gets vs paginating with scans?

My initial understanding was that doing a scan should be faster if our paging size is unknown (and caching is set appropriately), but that gets should be faster if we'll always need the same page size. I've ended up hearing different people tell me opposite things about performance. I assume the page sizes would be relatively consistent, so for most use cases we could guarantee that we only wanted one page of data in the fixed-page-length case. I would also assume that we would have infrequent updates, but may have inserts into the middle of these lists (meaning we'd need to update all subsequent rows).

Thanks for help / suggestions / follow-up questions.

*** ANSWER ***

If I understand you correctly, you're ultimately trying to store triples in the form "user, valueid, value", right? E.g., something like:

"user123, firstname, Paul",  "user234, lastname, Smith"  			

(But the usernames are fixed width, and the valueids are fixed width).

And, your access pattern is along the lines of: "for user X, list the next 30 values, starting with valueid Y". Is that right? And these values should be returned sorted by valueid?

The tl;dr version is that you should probably go with one row per user+value, and not build a complicated intra-row pagination scheme on your own unless you're really sure it is needed.

Your two options mirror a common question people have when designing HBase schemas: should I go "tall" or "wide"? Your first schema is "tall": each row represents one value for one user, and so there are many rows in the table for each user; the row key is user + valueid, and there would be (presumably) a single column qualifier that means "the value". This is great if you want to scan over rows in sorted order by row key (thus my question above, about whether these ids are sorted correctly). You can start a scan at any user+valueid, read the next 30, and be done. What you're giving up is the ability to have transactional guarantees around all the rows for one user, but it doesn't sound like you need that. Doing it this way is generally recommended (see here #schema.smackdown).

Your second option is "wide": you store a bunch of values in one row, using different qualifiers (where the qualifier is the valueid). The simple way to do that would be to just store ALL values for one user in a single row. I'm guessing you jumped to the "paginated" version because you're assuming that storing millions of columns in a single row would be bad for performance, which may or may not be true; as long as you're not trying to do too much in a single request, or do things like scanning over and returning all of the cells in the row, it shouldn't be fundamentally worse. The client has methods that allow you to get specific slices of columns.

Note that neither case fundamentally uses more disk space than the other; you're just "shifting" part of the identifying information for a value either to the left (into the row key, in option one) or to the right (into the column qualifiers in option 2). Under the covers, every key/value still stores the whole row key, and column family name. (If this is a bit confusing, take an hour and watch Lars George's excellent video about understanding HBase schema design: http://www.youtube.com/watch?v=_HLoH_PgrLk).

A manually paginated version has lots more complexities, as you note, like having to keep track of how many things are in each page, re-shuffling if new values are inserted, etc. That seems significantly more complex. It might have some slight speed advantages (or disadvantages!) at extremely high throughput, and the only way to really know that would be to try it out. If you don't have time to build it both ways and compare, my advice would be to start with the simplest option (one row per user+value). Start simple and iterate! :)

 

6.12. 业务和性能配置选项

参考 the Performance section Section 11.6, “Schema Design” for more information operational and performance schema design options, such as Bloom Filters, Table-configured regionsizes, compression, and blocksizes.

 

 

Chapter 7. HBase 和 MapReduce

Table of Contents

7.1. 默认 HBase MapReduce 分割器(Splitter)
7.2. HBase Input MapReduce 例子
7.3. 在一个MapReduce Job中访问其他的HBase Tables
7.4. 预测执行

关于 HBase 和 MapReduce详见 javadocs. 下面是一些附加的帮助文档. MapReduce的更多信息 (如,通用框架), 参考 Hadoop MapReduce Tutorial.

7.1. Map-Task 分割

7.1.1 默认 HBase MapReduce 分割器(Splitter)

当 MapReduce 任务的HBase 表使用TableInputFormat为数据源格式的时候,他的splitter会给这个table的每个region一个map。因此,如果一个table有100个region,就有100个map-tasks,不论需需要scan多少个列族 。

7.1.2. 自定义分割器

iv>

For those interested in implementing custom splitters, see the method getSplits in TableInputFormatBase. That is where the logic for map-task assignment resides.

 

7.2. HBase MapReduce 例子

7.2.1 HBase MapReduce 读取例子

下面是使用HBase 作为源的MapReduce读取示例。特别是仅有Mapper实例,没有Reducer。Mapper什么也不产生。

如下所示...

Configuration config = HBaseConfiguration.create();
Job job = new Job(config, "ExampleRead");
job.setJarByClass(MyReadJob.class);     // class that contains mapper

Scan scan = new Scan();
scan.setCaching(500); // 1 is the default in Scan, which will be bad for MapReduce jobs
scan.setCacheBlocks(false); // don’t set to true for MR jobs
// set other scan attrs

TableMapReduceUtil.initTableMapperJob(
tableName, // input HBase table name
scan, // Scan instance to control CF and attribute selection
MyMapper.class, // mapper
null, // mapper output key
null, // mapper output value
job);
job.setOutputFormatClass(NullOutputFormat.class); // because we aren’t emitting anything from mapper

boolean b = job.waitForCompletion(true);
if (!b) {
throw new IOException(“error with job!”);
}

...mapper需要继承于TableMapper...

public class MyMapper extends TableMapper<Text, LongWritable> {
public void map(ImmutableBytesWritable row, Result value, Context context) 
throws InterruptedException, IOException {
// process data for the row from the Result instance.

7.2.2. HBase MapReduce 读/写 示例

下面是使用HBase 作为源和目标的MapReduce示例. 本示例简单从一个表复制到另一个表。

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleReadWrite");
job.setJarByClass(MyReadWriteJob.class);    // class that contains mapper

Scan scan = new Scan();
scan.setCaching(500); // 1 is the default in Scan, which will be bad for MapReduce jobs
scan.setCacheBlocks(false); // don’t set to true for MR jobs
// set other scan attrs

TableMapReduceUtil.initTableMapperJob(
sourceTable, // input table
scan, // Scan instance to control CF and attribute selection
MyMapper.class, // mapper class
null, // mapper output key
null, // mapper output value
job);
TableMapReduceUtil.initTableReducerJob(
targetTable, // output table
null, // reducer class
job);
job.setNumReduceTasks(0);

boolean b = job.waitForCompletion(true);
if (!b) {
throw new IOException(“error with job!”);
}

TableMapReduceUtil做了什么需要解释, 特别是对 reducer. TableOutputFormat 作为 outputFormat 类, 几个参数在config中设置(e.g., TableOutputFormat.OUTPUT_TABLE), 同时设置reducer output key 到 ImmutableBytesWritable 和 reducer value到 Writable.这可以编程时设置到job和conf,但TableMapReduceUtil 使其变简单.

下面是 mapper示例, 创建一个 Put,匹配输入的 Result并提交. Note: 这是 CopyTable 工具做的.

public static class MyMapper extends TableMapper<ImmutableBytesWritable, Put>  {
public void map(ImmutableBytesWritable row, Result value, Context context) throws IOException, InterruptedException {
	// this example is just copying the data from the source table...
	context.write(row, resultToPut(row,value));
}
    
private static Put resultToPut(ImmutableBytesWritable key, Result result) throws IOException {
	Put put = new Put(key.get());
	for (KeyValue kv : result.raw()) {
		put.add(kv);
	}
	return put;
}

}

这不是真正的 reducer 步骤, 所以 TableOutputFormat 处理发送 Put 到目标表.

这仅是示例, 开发者可以选择不使用TableOutputFormat并自己连接到目标表。

7.2.3. HBase MapReduce Read/Write 多表输出示例

TODO: MultiTableOutputFormat 示例.

7.2.4. HBase MapReduce 汇总到 HBase 示例

下面是使用HBase 作为源和目标的MapReduce示例,具有汇总步骤。本示例计算一个表中值的个数,并将汇总的计数输出到另一个表。

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleSummary");
job.setJarByClass(MySummaryJob.class);     // class that contains mapper and reducer

Scan scan = new Scan();
scan.setCaching(500); // 1 is the default in Scan, which will be bad for MapReduce jobs
scan.setCacheBlocks(false); // don’t set to true for MR jobs
// set other scan attrs

TableMapReduceUtil.initTableMapperJob(
sourceTable, // input table
scan, // Scan instance to control CF and attribute selection
MyMapper.class, // mapper class
Text.class, // mapper output key
IntWritable.class, // mapper output value
job);
TableMapReduceUtil.initTableReducerJob(
targetTable, // output table
MyTableReducer.class, // reducer class
job);
job.setNumReduceTasks(1); // at least one, adjust as required

boolean b = job.waitForCompletion(true);
if (!b) {
throw new IOException(“error with job!”);
}

本示例mapper,将一个列的一个字符串值作为汇总值。该值作为key在mapper中生成。 IntWritable 代表一个实例计数。

public static class MyMapper extends TableMapper<Text, IntWritable>  {
private final IntWritable ONE = new IntWritable(1);
private Text text = new Text();
	
public void map(ImmutableBytesWritable row, Result value, Context context) throws IOException, InterruptedException {
    	String val = new String(value.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr1")));
      	text.set(val);     // we can only emit Writables...

    	context.write(text, ONE);
}

}

在 reducer, "ones" 被统计 (和其他 MR 示例一样), 产生一个 Put.

public static class MyTableReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable>  {
public void reduce(Text key, Iterable&lt;IntWritable&gt; values, Context context) throws IOException, InterruptedException {
		int i = 0;
		for (IntWritable val : values) {
			i += val.get();
		}
		Put put = new Put(Bytes.toBytes(key.toString()));
		put.add(Bytes.toBytes("cf"), Bytes.toBytes("count"), Bytes.toBytes(i));

		context.write(null, put);
}

}

7.2.5. HBase MapReduce 汇总到文件示例

This very similar to the summary example above, with exception that this is using HBase as a MapReduce source but HDFS as the sink. The differences are in the job setup and in the reducer. The mapper remains the same.

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleSummaryToFile");
job.setJarByClass(MySummaryFileJob.class);     // class that contains mapper and reducer

Scan scan = new Scan();
scan.setCaching(500); // 1 is the default in Scan, which will be bad for MapReduce jobs
scan.setCacheBlocks(false); // don’t set to true for MR jobs
// set other scan attrs

TableMapReduceUtil.initTableMapperJob(
sourceTable, // input table
scan, // Scan instance to control CF and attribute selection
MyMapper.class, // mapper class
Text.class, // mapper output key
IntWritable.class, // mapper output value
job);
job.setReducerClass(MyReducer.class); // reducer class
job.setNumReduceTasks(1); // at least one, adjust as required
FileOutputFormat.setOutputPath(job, new Path("/tmp/mr/mySummaryFile")); // adjust directories as required

boolean b = job.waitForCompletion(true);
if (!b) {
throw new IOException(“error with job!”);
}

As stated above, the previous Mapper can run unchanged with this example. As for the Reducer, it is a “generic” Reducer instead of extending TableMapper and emitting Puts.

 public static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>  {
        
	public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
		int i = 0;
		for (IntWritable val : values) {
			i += val.get();
		}	
		context.write(key, new IntWritable(i));
	}
}
    

7.2.6. HBase MapReduce 没有Reducer时汇总到 HBase

It is also possible to perform summaries without a reducer - if you use HBase as the reducer.

An HBase target table would need to exist for the job summary. The HTable method incrementColumnValue would be used to atomically increment values. From a performance perspective, it might make sense to keep a Map of values with their values to be incremeneted for each map-task, and make one update per key at during the cleanup method of the mapper. However, your milage may vary depending on the number of rows to be processed and unique keys.

In the end, the summary results are in HBase.

7.2.7. HBase MapReduce 汇总到 RDBMS

有时更合适产生汇总到 RDBMS.这种情况下,可以将汇总直接通过一个自定义的reducer输出到 RDBMS 。 setup 方法可以连接到 RDBMS (连接信息可以通过context的自定义参数传递), cleanup 可以关闭连接.

关键需要理解job的多个reducer会影响汇总实现,必须在reducer中进行设计。无论是一个recucer还是多个reducer。不管对错, 依赖于你的用例。认识到多个reducer分配到job,需要创建多个并发的RDBMS连接-可以扩充,但仅在一个点。

 public static class MyRdbmsReducer extends Reducer<Text, IntWritable, Text, IntWritable>  {
private Connection c = null;

public void setup(Context context) {
	// create DB connection...
}
    
public void reduce(Text key, Iterable&lt;IntWritable&gt; values, Context context) throws IOException, InterruptedException {
	// do summarization
	// in this example the keys are Text, but this is just an example
}

public void cleanup(Context context) {
	// close db connection
}

}

最后,汇总的结果被写入到 RDBMS 表.

7.3. 在一个MapReduce Job中访问其他的HBase Tables

尽管现有的框架允许一个HBase table作为一个MapReduce job的输入,其他的HBase table可以同时作为普通的表被访问。例如在一个MapReduce的job中,可以在Mapper的setup方法中创建HTable实例。

public class MyMapper extends TableMapper<Text, LongWritable> {
  private HTable myOtherTable;

@Override
public void setup(Context context) {
myOtherTable = new HTable(“myOtherTable”);
}

7.4. 预测执行

通常建议关掉针对HBase的MapReduce job的预测执行(speculative execution)功能。这个功能也可以用每个Job的配置来完成。对于整个集群,使用预测执行意味着双倍的运算量。这可不是你所希望的。

参考 Section 2.8.2.9, “Speculative Execution” 获取更多信息。

Chapter 8.  HBase安全

Table of Contents

8.1. 安全客户端访问HBase
8.1.1. 先决条件
8.1.2. Server-side Configuration for Secure Operation
8.1.3. 客户端安全操作配置
8.1.4. 客户端安全操作配置 - Thrift Gateway
8.1.5. 客户端安全操作配置 - REST Gateway
8.2. 访问控制
8.2.1. 先决条件
8.2.2. 概述
8.2.3. Server-side Configuration for Access Control
8.2.4. Shell Enhancements for Access Control

8.1. 安全客户端访问HBase

新版 HBase (>= 0.92) 支持客户端可选 SASL 认证.

这里描述如何设置HBase 和 HBase 客户端,以安全连接到HBase 资源.

8.1.1. 先决条件

HBase 必须使用 安全Hadoop/HBase的新 maven 配置文件: -P security. Secure Hadoop dependent classes are separated under a pseudo-module in the security/ directory and are only included if built with the secure Hadoop profile.

You need to have a working Kerberos KDC.

A HBase configured for secure client access is expected to be running on top of a secured HDFS cluster. HBase must be able to authenticate to HDFS services. HBase needs Kerberos credentials to interact with the Kerberos-enabled HDFS daemons. Authenticating a service should be done using a keytab file. The procedure for creating keytabs for HBase service is the same as for creating keytabs for Hadoop. Those steps are omitted here. Copy the resulting keytab files to wherever HBase Master and RegionServer processes are deployed and make them readable only to the user account under which the HBase daemons will run.

A Kerberos principal has three parts, with the form username/fully.qualified.domain.name@YOUR-REALM.COM. We recommend using hbase as the username portion.

The following is an example of the configuration properties for Kerberos operation that must be added to the hbase-site.xml file on every server machine in the cluster. Required for even the most basic interactions with a secure Hadoop configuration, independent of HBase security.

      <property>
        <name>hbase.regionserver.kerberos.principal</name>
        <value>hbase/_HOST@YOUR-REALM.COM</value>
      </property>
      <property>
        <name>hbase.regionserver.keytab.file</name>
        <value>/etc/hbase/conf/keytab.krb5</value>
      </property>
      <property>
        <name>hbase.master.kerberos.principal</name>
        <value>hbase/_HOST@YOUR-REALM.COM</value>
      </property>
      <property>
        <name>hbase.master.keytab.file</name>
        <value>/etc/hbase/conf/keytab.krb5</value>
      </property>
    

Each HBase client user should also be given a Kerberos principal. This principal should have a password assigned to it (as opposed to a keytab file). The client principal's maxrenewlife should be set so that it can be renewed enough times for the HBase client process to complete. For example, if a user runs a long-running HBase client process that takes at most 3 days, we might create this user's principal within kadmin with: addprinc -maxrenewlife 3days

Long running daemons with indefinite lifetimes that require client access to HBase can instead be configured to log in from a keytab. For each host running such daemons, create a keytab with kadmin or kadmin.local. The procedure for creating keytabs for HBase service is the same as for creating keytabs for Hadoop. Those steps are omitted here. Copy the resulting keytab files to where the client daemon will execute and make them readable only to the user account under which the daemon will run.

8.1.2. 安全操作的服务器端配置

增加下列内容到 hbase-site.xml file on every server machine in the cluster:

      <property>
        <name>hbase.security.authentication</name>
        <value>kerberos</value> 
      </property> 
      <property>
        <name>hbase.security.authorization</name>
        <value>true</value>
      </property>
      <property>
        <name>hbase.rpc.engine</name>
        <value>org.apache.hadoop.hbase.ipc.SecureRpcEngine</value>
      </property>
      <property>
      <name>hbase.coprocessor.region.classes</name>
        <value>org.apache.hadoop.hbase.security.token.TokenProvider</value>
      </property>
    

A full shutdown and restart of HBase service is required when deploying these configuration changes.

8.1.3. 客户端安全操作配置

每个客户端增加下列内容到 hbase-site.xml:

      <property>
        <name>hbase.security.authentication</name>
        <value>kerberos</value>
      </property> 
      <property>
        <name>hbase.rpc.engine</name>
        <value>org.apache.hadoop.hbase.ipc.SecureRpcEngine</value>
      </property>
    

The client environment must be logged in to Kerberos from KDC or keytab via the kinit command before communication with the HBase cluster will be possible.

Be advised that if the hbase.security.authentication and hbase.rpc.engine properties in the client- and server-side site files do not match, the client will not be able to communicate with the cluster.

Once HBase is configured for secure RPC it is possible to optionally configure encrypted communication. To do so, 增加下列内容到 hbase-site.xml file on every client:

      <property>
        <name>hbase.rpc.protection</name>
        <value>privacy</value>
      </property>
    

This configuration property can also be set on a per connection basis. Set it in the Configuration supplied to HTable:

      Configuration conf = HBaseConfiguration.create();
      conf.set("hbase.rpc.protection", "privacy");
      HTable table = new HTable(conf, tablename);
    

Expect a ~10% performance penalty for encrypted communication.

8.1.4. 客户端安全操作配置 - Thrift 网关

每个Thrift网关增加下列内容到 hbase-site.xml:

    <property>
      <name>hbase.thrift.keytab.file</name>
      <value>/etc/hbase/conf/hbase.keytab</value>
    </property>
    <property>
      <name>hbase.thrift.kerberos.principal</name>
      <value>$USER/_HOST@HADOOP.LOCALDOMAIN</value>
    </property>
    

Substitute the appropriate credential and keytab for $USER and $KEYTAB respectively.

The Thrift gateway will authenticate with HBase using the supplied credential. No authentication will be performed by the Thrift gateway itself. All client access via the Thrift gateway will use the Thrift gateway's credential and have its privilege.

8.1.5. 客户端安全操作配置 - REST 网关

每个REST网关增加下列内容到 hbase-site.xml:

    <property>
      <name>hbase.rest.keytab.file</name>
      <value>$KEYTAB</value>
    </property>
    <property>
      <name>hbase.rest.kerberos.principal</name>
      <value>$USER/_HOST@HADOOP.LOCALDOMAIN</value>
    </property>
    

Substitute the appropriate credential and keytab for $USER and $KEYTAB respectively.

The REST gateway will authenticate with HBase using the supplied credential. No authentication will be performed by the REST gateway itself. All client access via the REST gateway will use the REST gateway's credential and have its privilege.

It should be possible for clients to authenticate with the HBase cluster through the REST gateway in a pass-through manner via SPEGNO HTTP authentication. This is future work.

8.2. 访问控制

Newer releases of HBase (>= 0.92) support optional access control list (ACL-) based protection of resources on a column family and/or table basis.

This describes how to set up Secure HBase for access control, with an example of granting and revoking user permission on table resources provided.

8.2.1. 先决条件

You must configure HBase for secure operation. Refer to the section "安全客户端访问HBase" and complete all of the steps described there.

You must also configure ZooKeeper for secure operation. Changes to ACLs are synchronized throughout the cluster using ZooKeeper. Secure authentication to ZooKeeper must be enabled or otherwise it will be possible to subvert HBase access control via direct client access to ZooKeeper. Refer to the section on secure ZooKeeper configuration and complete all of the steps described there.

8.2.2. 概述

With Secure RPC and Access Control enabled, client access to HBase is authenticated and user data is private unless access has been explicitly granted. Access to data can be granted at a table or per column family basis.

However, the following items have been left out of the initial implementation for simplicity:

  1. Row-level or per value (cell): This would require broader changes for storing the ACLs inline with rows. It is a future goal.

  2. Push down of file ownership to HDFS: HBase is not designed for the case where files may have different permissions than the HBase system principal. Pushing file ownership down into HDFS would necessitate changes to core code. Also, while HDFS file ownership would make applying quotas easy, and possibly make bulk imports more straightforward, it is not clear that it would offer a more secure setup.

  3. HBase managed "roles" as collections of permissions: We will not model "roles" internally in HBase to begin with. We instead allow group names to be granted permissions, which allows external modeling of roles via group membership. Groups are created and manipulated externally to HBase, via the Hadoop group mapping service.

Access control mechanisms are mature and fairly standardized in the relational database world. The HBase implementation approximates current convention, but HBase has a simpler feature set than relational databases, especially in terms of client operations. We don't distinguish between an insert (new record) and update (of existing record), for example, as both collapse down into a Put. Accordingly, the important operations condense to four permissions: READ, WRITE, CREATE, and ADMIN.

Operation To Permission MappingPermissionOperationReadGetExistsScanWritePutDeleteLock/UnlockRowIncrementColumnValueCheckAndDelete/PutFlushCompactCreateCreateAlterDropAdminEnable/DisableSplitMajor CompactGrantRevokeShutdown

Permissions can be granted in any of the following scopes, though CREATE and ADMIN permissions are effective only at table scope.

  • Table

    • Read: User can read from any column family in table

    • Write: User can write to any column family in table

    • Create: User can alter table attributes; add, alter, or drop column families; and drop the table.

    • Admin: User can alter table attributes; add, alter, or drop column families; and enable, disable, or drop the table. User can also trigger region (re)assignments or relocation.

  • Column Family

    • Read: User can read from the column family

    • Write: User can write to the column family

There is also an implicit global scope for the superuser.

The superuser is a principal, specified in the HBase site configuration file, that has equivalent access to HBase as the 'root' user would on a UNIX derived system. Normally this is the principal that the HBase processes themselves authenticate as. Although future versions of HBase Access Control may support multiple superusers, the superuser privilege will always include the principal used to run the HMaster process. Only the superuser is allowed to create tables, switch the balancer on or off, or take other actions with global consequence. Furthermore, the superuser has an implicit grant of all permissions to all resources.

Tables have a new metadata attribute: OWNER, the user principal who owns the table. By default this will be set to the user principal who creates the table, though it may be changed at table creation time or during an alter operation by setting or changing the OWNER table attribute. Only a single user principal can own a table at a given time. A table owner will have all permissions over a given table.

8.2.3. Server-side Configuration for Access Control

Enable the AccessController coprocessor in the cluster configuration and restart HBase. The restart can be a rolling one. Complete the restart of all Master and RegionServer processes before setting up ACLs.

To enable the AccessController, modify the hbase-site.xml file on every server machine in the cluster to look like:

      <property>
        <name>hbase.coprocessor.master.classes</name>
        <value>org.apache.hadoop.hbase.security.access.AccessController</value>
      </property>
      <property>
      <name>hbase.coprocessor.region.classes</name>
        <value>org.apache.hadoop.hbase.security.token.TokenProvider,
        org.apache.hadoop.hbase.security.access.AccessController</value>
      </property>
    

8.2.4. Shell Enhancements for Access Control

The HBase shell has been extended to provide simple commands for editing and updating user permissions. The following commands have been added for access control list management:

Grant

    grant <user> <permissions> <table> [ <column family> [ <column qualifier> ] ]
    

<permissions> is zero or more letters from the set "RWCA": READ('R'), WRITE('W'), CREATE('C'), ADMIN('A').

Note: Grants and revocations of individual permissions on a resource are both accomplished using the grant command. A separate revoke command is also provided by the shell, but this is for fast revocation of all of a user's access rights to a given resource only.

Revoke

    revoke <user> <table> [ <column family> [ <column qualifier> ] ]
    

Alter

The alter command has been extended to allow ownership assignment:

      alter 'tablename', {OWNER => 'username'}
    

User Permission

The user_permission command shows all access permissions for the current user for a given table:

      user_permission <table>
    

8.3. Secure Bulk Load

Bulk loading in secure mode is a bit more involved than normal setup, since the client has to transfer the ownership of the files generated from the mapreduce job to HBase. Secure bulk loading is implemented by a coprocessor, named SecureBulkLoadEndpoint. SecureBulkLoadEndpoint uses a staging directory "hbase.bulkload.staging.dir", which defaults to /tmp/hbase-staging/. The algorithm is as follows.

  • Create an hbase owned staging directory which is world traversable (-rwx--x--x, 711) /tmp/hbase-staging.
  • A user writes out data to his secure output directory: /user/foo/data
  • A call is made to hbase to create a secret staging directory which is globally readable/writable (-rwxrwxrwx, 777): /tmp/hbase-staging/averylongandrandomdirectoryname
  • The user makes the data world readable and writable, then moves it into the random staging directory, then calls bulkLoadHFiles()

Like delegation tokens the strength of the security lies in the length and randomness of the secret directory.

You have to enable the secure bulk load to work properly. You can modify the hbase-site.xml file on every server machine in the cluster and add the SecureBulkLoadEndpoint class to the list of regionserver coprocessors:

      <property>          <name>hbase.bulkload.staging.dir</name>          <value>/tmp/hbase-staging</value>        </property>        <property>          <name>hbase.coprocessor.region.classes</name>          <value>org.apache.hadoop.hbase.security.token.TokenProvider,          org.apache.hadoop.hbase.security.access.AccessController,org.apache.hadoop.hbase.security.access.SecureBulkLoadEndpoint</value>        </property>      


Chapter 9. 架构

Table of Contents

9.1. 概述
9.2. 目录表
9.3. Client
9.4. 客户端请求过滤器
9.5. Master
9.6. RegionServer
9.7. Regions
9.8. Bulk Loading
9.9. HDFS

 

9.1. 概述

9.1.1. NoSQL?

HBase是一种 "NoSQL" 数据库. "NoSQL"是一个通用词表示数据库不是RDBMS ,后者支持 SQL 作为主要访问手段。有许多种 NoSQL 数据库: BerkeleyDB 是本地 NoSQL 数据库例子, 而 HBase 是大型分布式数据库。 技术上来说, HBase 更像是"数据存储(Data Store)" 多于 "数据库(Data Base)"。因为缺少很多RDBMS特性, 如列类型,第二索引,触发器,高级查询语言等.

然而, HBase 有许多特征同时支持线性化和模块化扩充。 HBase 集群通过增加RegionServers进行扩充。 它可以放在普通的服务器中。例如,如果集群从10个扩充到20个RegionServer,存储空间和处理容量都同时翻倍。 RDBMS 也能很好扩充, 但仅对一个点 - 特别是对一个单独数据库服务器的大小 - 同时,为了更好的性能,需要特殊的硬件和存储设备。 HBase 特性:

  • 强一致性读写: HBase 不是 "最终一致性(eventually consistent)" 数据存储. 这让它很适合高速计数聚合类任务。
  • 自动分片(Automatic sharding): HBase 表通过region分布在集群中。数据增长时,region会自动分割并重新分布。
  • RegionServer 自动故障转移
  • Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。
  • MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.
  • Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.
  • Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.
  • Block Cache 和 Bloom Filters: 对于大容量查询优化, HBase支持 Block Cache 和 Bloom Filters。
  • 运维管理: HBase提供内置网页用于运维视角和JMX 度量.

9.1.2. 什么时候用 HBase?

HBase不适合所有问题.

首先,确信有足够多数据,如果有上亿或上千亿行数据,HBase是很好的备选。 如果只有上千或上百万行,则用传统的RDBMS可能是更好的选择。因为所有数据可以在一两个节点保存,集群其他节点可能闲置。

其次,确信可以不依赖所有RDBMS的额外特性 (e.g., 列数据类型, 第二索引, 事物,高级查询语言等.) 一个建立在RDBMS上应用,如不能仅通过改变一个JDBC驱动移植到HBase。相对于移植, 需考虑从RDBMS 到 HBase是一次完全的重新设计。

第三, 确信你有足够硬件。甚至 HDFS 在小于5个数据节点时,干不好什么事情 (根据如 HDFS 块复制具有缺省值 3), 还要加上一个 NameNode.

HBase 能在单独的笔记本上运行良好。但这应仅当成开发配置。

9.1.3.  HBase 和 Hadoop/HDFS 的区别?

HDFS 是分布式文件系统,适合保存大文件。官方宣称它并非普通用途文件系统,不提供文件的个别记录的快速查询。 另一方面,HBase基于HDFS且提供大表的记录快速查找(和更新)。这有时可能引起概念混乱。 HBase 内部将数据放到索引好的 "存储文件(StoreFiles)" ,以便高速查询。存储文件位于 HDFS中。参考Chapter 5, 数据模型 和该章其他内容获取更多HBase如何归档的信息。

9.2. 目录表(Catalog Tables)

目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了, 但他们和其他表一样存在。

9.2.1. ROOT

-ROOT- 保存 .META. 表存在哪里的踪迹. -ROOT- 表结构如下:

Key:

  • .META. region key (.META.,,1)

Values:

  • info:regioninfo (序列化.META.的 HRegionInfo 实例 )
  • info:server ( 保存 .META.的RegionServer的server:port)
  • info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)

9.2.2. META

.META. 保存系统中所有region列表。 .META.表结构如下:

Key:

  • Region key 格式 ([table],[region start key],[region id])

Values:

  • info:regioninfo (序列化.META.的 HRegionInfo 实例 )
  • info:server ( 保存 .META.的RegionServer的server:port)
  • info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)

当表在分割过程中,会创建额外的两列, info:splitA info:splitB 代表两个女儿 region. 这两列的值同样是序列化HRegionInfo 实例. region最终分割完毕后,这行会删除。

HRegionInfo的备注: 空 key 用于指示表的开始和结束。具有空开始键值的region是表内的首region。 如果 region 同时有空起始和结束key,说明它是表内的唯一region。

在需要编程访问(希望不要)目录元数据时,参考 Writables 工具.

9.2.3. 启动时序

META 地址首先在ROOT 中设置。META 会更新 server 和 startcode 的值.

需要 region-RegionServer 分配信息, 参考 Section 9.7.2, “Region-RegionServer 分配”.

9.3. 客户端

HBase客户端的 HTable类负责寻找相应的RegionServers来处理行。他是先查询 .META. -ROOT 目录表。然后再确定region的位置。定位到所需要的区域后,客户端会直接 去访问相应的region(不经过master),发起读写请求。这些信息会缓存在客户端,这样就不用每发起一个请求就去查一下。如果一个region已经废弃(原因可能是master load balance或者RegionServer死了),客户端就会重新进行这个步骤,决定要去访问的新的地址。

参考 Section 9.5.2, “Runtime Impact” for more information about the impact of the Master on HBase Client communication.

管理集群操作是经由HBaseAdmin发起的

9.3.1. 连接

关于连接的配置信息,参见Section 3.7, “连接HBase集群的客户端配置和依赖”.

HTable不是线程安全的。建议使用同一个HBaseConfiguration实例来创建HTable实例。这样可以共享ZooKeeper和socket实例。例如,最好这样做:

HBaseConfiguration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, "myTable");
HTable table2 = new HTable(conf, "myTable");

而不是这样:

HBaseConfiguration conf1 = HBaseConfiguration.create();
HTable table1 = new HTable(conf1, "myTable");
HBaseConfiguration conf2 = HBaseConfiguration.create();
HTable table2 = new HTable(conf2, "myTable");

如果你想知道的更多的关于HBase客户端connection的知识,可以参照: HConnectionManager.

9.3.1.1. 连接池

对需要高端多线程访问的应用 (如网页服务器或应用服务器需要在一个JVM服务很多应用线程),参考 HTablePool.

 

9.3.2. 写缓冲和批量操作

若关闭了HTable中的 Section 11.10.4, “AutoFlush”,Put操作会在写缓冲填满的时候向RegionServer发起请求。默认情况下,写缓冲是2MB.在Htable被废弃之前,要调用close(), flushCommits()操作,这样写缓冲就不会丢失。

要想更好的细粒度控制 PutDelete的批量操作,可以参考Htable中的batch 方法.

9.3.3. 外部客户端

关于非Java客户端和定制协议信息,在 Chapter 10, 外部 API

9.3.4. 行锁

行锁 在客户端 API中仍然存在, 但是 不鼓励使用,因为管理不好,会锁定整个RegionServer.

这里是优秀ticket HBASE-2332 从终端移除该特性。

 

9.4. 客户端请求过滤器

Get 和 Scan 实例可以用 filters 配置,以应用于 RegionServer.

过滤器可能会搞混,因为有很多类型的过滤器, 最好通过理解过滤器功能组来了解他们。

9.4.1. 结构(Structural)过滤器

结构过滤器包含其他过滤器

9.4.1.1. FilterList

FilterList 代表一个过滤器列表,过滤器间具有 FilterList.Operator.MUST_PASS_ALL FilterList.Operator.MUST_PASS_ONE 关系。下面示例展示两个过滤器的'或'关系(检查同一属性的'my value' 或'my other value' ).

FilterList list = new FilterList(FilterList.Operator.MUST_PASS_ONE);
SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
	cf,
	column,
	CompareOp.EQUAL,
	Bytes.toBytes("my value")
	);
list.add(filter1);
SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
	cf,
	column,
	CompareOp.EQUAL,
	Bytes.toBytes("my other value")
	);
list.add(filter2);
scan.setFilter(list);

9.4.2. 列值

9.4.2.1. SingleColumnValueFilter

SingleColumnValueFilter 用于测试列值相等 (CompareOp.EQUAL ), 不等 (CompareOp.NOT_EQUAL),或范围 (e.g., CompareOp.GREATER). 下面示例检查列值和字符串'my value' 相等...

SingleColumnValueFilter filter = new SingleColumnValueFilter(
	cf,
	column,
	CompareOp.EQUAL,
	Bytes.toBytes("my value")
	);
scan.setFilter(filter);

9.4.3. 列值比较器

过滤器包内有好几种比较器类需要特别提及。这些比较器和其他过滤器一起使用, 如 Section 9.4.2.1, “SingleColumnValueFilter”.

9.4.3.1. RegexStringComparator

RegexStringComparator 支持值比较的正则表达式 。

RegexStringComparator comp = new RegexStringComparator("my.");   // any value that starts with 'my'
SingleColumnValueFilter filter = new SingleColumnValueFilter(
	cf,
	column,
	CompareOp.EQUAL,
	comp
	);
scan.setFilter(filter);

参考 Oracle JavaDoc 了解 supported RegEx patterns in Java.

9.4.3.2. SubstringComparator

SubstringComparator 用于检测一个子串是否存在于值中。大小写不敏感。

SubstringComparator comp = new SubstringComparator("y val");   // looking for 'my value'
SingleColumnValueFilter filter = new SingleColumnValueFilter(
	cf,
	column,
	CompareOp.EQUAL,
	comp
	);
scan.setFilter(filter);

9.4.3.3. BinaryPrefixComparator

参考 BinaryPrefixComparator.

9.4.3.4. BinaryComparator

参考 BinaryComparator.

9.4.4. 键值元数据

由于HBase 采用键值对保存内部数据,键值元数据过滤器评估一行的键是否存在(如 ColumnFamily:Column qualifiers) , 对应前节所述值的情况。

9.4.4.1. FamilyFilter

FamilyFilter 用于过滤列族。 通常,在Scan中选择ColumnFamilie优于在过滤器中做。

9.4.4.2. QualifierFilter

QualifierFilter 用于基于列名(即 Qualifier)过滤.

9.4.4.3. ColumnPrefixFilter

ColumnPrefixFilter 可基于列名(即Qualifier)前缀过滤。

A ColumnPrefixFilter seeks ahead to the first column matching the prefix in each row and for each involved column family. It can be used to efficiently get a subset of the columns in very wide rows.

Note: The same column qualifier can be used in different column families. This filter returns all matching columns.

Example: Find all columns in a row and family that start with "abc"

HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] prefix = Bytes.toBytes("abc");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnPrefixFilter(prefix);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

9.4.4.4. MultipleColumnPrefixFilter

MultipleColumnPrefixFilter 和 ColumnPrefixFilter 行为差不多,但可以指定多个前缀。

Like ColumnPrefixFilter, MultipleColumnPrefixFilter efficiently seeks ahead to the first column matching the lowest prefix and also seeks past ranges of columns between prefixes. It can be used to efficiently get discontinuous sets of columns from very wide rows.

Example: Find all columns in a row and family that start with "abc" or "xyz"

HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[][] prefixes = new byte[][] {Bytes.toBytes("abc"), Bytes.toBytes("xyz")};
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new MultipleColumnPrefixFilter(prefixes);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

9.4.4.5. ColumnRangeFilter

ColumnRangeFilter 可以进行高效内部扫描。

A ColumnRangeFilter can seek ahead to the first matching column for each involved column family. It can be used to efficientlyget a 'slice' of the columns of a very wide row. i.e. you have a million columns in a row but you only want to look at columns bbbb-bbdd.

Note: The same column qualifier can be used in different column families. This filter returns all matching columns.

Example: Find all columns in a row and family between "bbbb" (inclusive) and "bbdd" (inclusive)

HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] startColumn = Bytes.toBytes("bbbb");
byte[] endColumn = Bytes.toBytes("bbdd");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnRangeFilter(startColumn, true, endColumn, true);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

Note: HBase 0.92 引入

9.4.5. RowKey

9.4.5.1. RowFilter

通常认为行选择时Scan采用 startRow/stopRow 方法比较好。然而 RowFilter 也可以用。

9.4.6. Utility

9.4.6.1. FirstKeyOnlyFilter

This is primarily used for rowcount jobs. 参考 FirstKeyOnlyFilter.

9.5. 主服务器

HMaster is the implementation of the Master Server. The Master server is responsible for monitoring all RegionServer instances in the cluster, and is the interface for all metadata changes. In a distributed cluster, the Master typically runs on the Section 9.9.1, “NameNode”.

9.5.1. Startup Behavior

If run in a multi-Master environment, all Masters compete to run the cluster. If the active Master loses it's lease in ZooKeeper (or the Master shuts down), then then the remaining Masters jostle to take over the Master role.

9.5.2. Runtime Impact

A common dist-list question is what happens to an HBase cluster when the Master goes down. Because the HBase client talks directly to the RegionServers, the cluster can still function in a "steady state." Additionally, per Section 9.2, “Catalog Tables” ROOT and META exist as HBase tables (i.e., are not resident in the Master). However, the Master controls critical functions such as RegionServer failover and completing region splits. So while the cluster can still run for a time without the Master, the Master should be restarted as soon as possible.

9.5.3. Interface

The methods exposed by HMasterInterface are primarily metadata-oriented methods:

  • Table (createTable, modifyTable, removeTable, enable, disable)
  • ColumnFamily (addColumn, modifyColumn, removeColumn)
  • Region (move, assign, unassign)

For example, when the HBaseAdmin method disableTable is invoked, it is serviced by the Master server.

9.5.4. 进程

Master 后台运行几种线程:

9.5.4.1. LoadBalancer

Periodically, and when there are not any regions in transition, a load balancer will run and move regions around to balance cluster load. 参考 Section 2.8.3.1, “Balancer” for configuring this property.

参考 Section 9.7.2, “Region-RegionServer Assignment” for more information on region assignment.

9.5.4.2. CatalogJanitor

Periodically checks and cleans up the .META. table. 参考 Section 9.2.2, “META” for more information on META.

9.6. RegionServer

HRegionServer is the RegionServer implementation. It is responsible for serving and managing regions. In a distributed cluster, a RegionServer runs on a Section 9.9.2, “DataNode”.

9.6.1. 接口

The methods exposed by HRegionRegionInterface contain both data-oriented and region-maintenance methods:

  • Data (get, put, delete, next, etc.)
  • Region (splitRegion, compactRegion, etc.)

For example, when the HBaseAdmin method majorCompact is invoked on a table, the client is actually iterating through all regions for the specified table and requesting a major compaction directly to each region.

9.6.2. 进程

RegionServer 后台运行几种线程:

9.6.2.1. CompactSplitThread

检查分割并处理最小紧缩。

9.6.2.2. MajorCompactionChecker

检查主紧缩。

9.6.2.3. MemStoreFlusher

周期将写到内存存储的内容刷到文件存储。

9.6.2.4. LogRoller

周期检查RegionServer 的 HLog.

9.6.3. 协处理器

协处理器在0.92版添加。 有一个详细帖子 Blog Overview of CoProcessors 供参考。文档最终会放到本参考手册,但该blog是当前能获取的大部分信息。

9.6.4. 块缓存

9.6.4.1. 设计

The Block Cache is an LRU cache that contains three levels of block priority to allow for scan-resistance and in-memory ColumnFamilies:

  • Single access priority: The first time a block is loaded from HDFS it normally has this priority and it will be part of the first group to be considered during evictions. The advantage is that scanned blocks are more likely to get evicted than blocks that are getting more usage.
  • Mutli access priority: If a block in the previous priority group is accessed again, it upgrades to this priority. It is thus part of the second group considered during evictions.
  • In-memory access priority: If the block's family was configured to be "in-memory", it will be part of this priority disregarding the number of times it was accessed. Catalog tables are configured like this. This group is the last one considered during evictions.

For more information, see the LruBlockCache source

9.6.4.2. 使用

Block caching is enabled by default for all the user tables which means that any read operation will load the LRU cache. This might be good for a large number of use cases, but further tunings are usually required in order to achieve better performance. An important concept is the working set size, or WSS, which is: "the amount of memory needed to compute the answer to a problem". For a website, this would be the data that's needed to answer the queries over a short amount of time.

The way to calculate how much memory is available in HBase for caching is:

            number of region servers * heap size * hfile.block.cache.size * 0.85
        

The default value for the block cache is 0.25 which represents 25% of the available heap. The last value (85%) is the default acceptable loading factor in the LRU cache after which eviction is started. The reason it is included in this equation is that it would be unrealistic to say that it is possible to use 100% of the available memory since this would make the process blocking from the point where it loads n

www.htsjk.Com true http://www.htsjk.com/hbase/34907.html NewsArticle HBase官方文档中文版,hbase官方文档 from: http://abloz.com/hbase/book.html Apache HBase™ 参考指南 HBase 官方文档中文版 Copyright © 2012 Apache Software Foundation。保留所有权利。 Apache Hadoop, Hadoop, MapRe...
相关文章
    暂无相关文章
评论暂时关闭