HBase-管理秘籍-全-
HBase 管理秘籍(全)
零、前言
作为一个开源、分布式的大数据存储,HBase 可扩展到数十亿行、数百万列,并位于商用机器集群的顶部。 如果您正在寻找一种实时存储和访问海量数据的方法,那么只需查看 HBase 即可。
HBase 管理手册为您轻松管理 HBase 提供了实用示例和简单的分步说明。 这些食谱涵盖了在云上管理完全分布式、高度可用的 HBase 集群的广泛流程。 处理如此庞大的数据意味着一个有组织和可管理的过程是关键,这本书将帮助你实现这一点。
这本实用食谱中的食谱从设置一个完全分布式的 HBase 集群并将数据移动到其中开始。 您将学习如何将所有工具用于日常管理任务,以及如何有效地管理和监控集群以实现最佳性能。 了解 Hadoop 和 HBase 之间的关系将使您能够充分利用 HBase;因此,本书将向您展示如何设置 Hadoop 集群、配置 Hadoop 以与 HBase 协作以及调优其性能。
这本书涵盖了哪些内容
第 1 章,设置 HBase 群集:本章介绍如何在 Amazon EC2 上设置 HBase 群集,从基本的独立 HBase 实例到完全分布式、高度可用的 HBase 群集。
第 2 章,数据迁移:在本章中,我们将从使用 PUT API 将数据从 MySQL 导入 HBase 这一简单任务开始。 然后,我们将介绍如何使用 Importtsv 和批量加载工具将 TSV 数据文件加载到 HBase 中。 我们还将使用 MapReduce 示例从其他文件格式导入数据。 这包括将数据直接放入 HBase 表和写入 Hadoop 分布式文件系统(HDFS)上的 HFile 格式文件。 本章的最后一个诀窍解释了如何在将数据加载到 HBase 之前预先创建区域。
本章附带了几个用 Java 编写的示例源代码。 本文假设您具有基本的 Java 知识,因此不会解释如何在食谱中编译和打包示例 Java 源代码。
第 3 章,使用管理工具:在本章中,我们将介绍各种管理工具的使用,如 HBase web UI、HBase Shell、HBase hbck 等。 我们将解释这些工具的用途,以及如何使用它们来解决特定任务。
第 4 章,备份和还原 HBase 数据:在本章中,我们将介绍如何使用各种方法备份 HBase 数据,它们的优缺点,以及根据数据集大小、资源和要求选择哪种方法。
第 5 章,监控和诊断:在本章中,我们将介绍如何使用 Ganglia、OpenTSDB、Nagios 和其他工具监控和诊断 HBase 集群。 我们将从一个简单的任务开始,显示 HBase 表的磁盘利用率。 我们将安装和配置 Ganglia 以监视 HBase 指标,并展示 Ganglia 图的用法示例。 我们还将设置 OpenTSDB,它类似于 Ganglia,但更具可伸缩性,因为它构建在 HBase 之上。
我们将设置 Nagios 来检查我们想要检查的所有内容,包括与 HBase 相关的守护进程运行状况、Hadoop/HBase 日志、HBase 不一致、HDFS 运行状况和空间利用率。
在最后一个菜谱中,我们将描述一种诊断和修复常见热点区域问题的方法。
第 6 章,维护和安全:在本章的前六个菜谱中,我们将了解各种 HBase 维护任务,例如查找和纠正故障、更改集群大小、更改配置等。
我们还将在本章中介绍安全性。 在最后三个配方中,我们将安装 Kerberos,然后使用 Kerberos 设置 HDFS 安全性,最后设置安全的 HBase 客户端访问。
第 7 章,故障排除:在本章中,我们将介绍几个最常见的问题。 我们将介绍这些问题的错误消息、发生原因以及如何使用故障排除工具修复这些问题。
第 8 章,基本性能调整:在本章中,我们将介绍如何调整 HBase 以获得更好的性能。 我们还将介绍调优其他调优点的方法,如 Hadoop 配置、JVM 垃圾收集设置和操作系统内核参数。
第 9 章,高级配置和调优:这是本书中关于性能调优的另一章。 上一章介绍了一些调整 Hadoop、操作系统设置、Java 和 HBase 本身的方法,以提高 HBase 集群的整体性能。 这些是对许多用例的一般性改进。 在本章中,我们将介绍更具体的配方,其中一些是针对写入繁重的集群,而另一些则旨在提高集群的读取性能。
这本书你需要什么
你需要的一切都列在每个食谱里了。
本书所需软件的基本列表如下:
- Debian 6.0.1(压缩)
- Oracle JDK(Java 开发工具包)SE 6
- HBase 0.92.1
- Hadoop 1.0.2
- 动物园管理员 3.4.3
这本书是给谁看的
本书面向 HBase 管理员、开发人员,甚至会对 Hadoop 管理员有所帮助。 您不需要有 HBase 经验,但需要对 Hadoop 和 MapReduce 有基本的了解。
公约
在这本书中,你会发现许多区分不同信息的文本样式。 以下是这些风格的一些示例,并解释了它们的含义。
文本中的代码字如下所示:“可以使用其 stop-hbase.sh
脚本停止 HBase。”
代码块设置如下:
nameserver 10.160.49.250 #private IP of ns
search hbase-admin-cookbook.com #domain name
当我们希望您注意代码块的特定部分时,相关行或项将以粗体显示:
MAJOR_COMPACTION_KEY = \x00
MAX_SEQ_ID_KEY = 96573
TIMERANGE = 1323026325955....1323026325955
hfile.AVG_KEY_LEN = 31
hfile.AVG_VALUE_LEN = 4
hfile.COMPARATOR = org.apache.hadoop.hbase.KeyValue$KeyComparator
任何命令行输入或输出都如下所示:
$ bin/ycsb load hbase -P workloads/workloada -p columnfamily=f1 -p recordcount=1000000 -p threadcount=4 -s | tee -a workloada.dat
YCSB Client 0.1
Command line: -db com.yahoo.ycsb.db.HBaseClient -P workloads/workloada -p columnfamily=f1 -p recordcount=1000000 -p threadcount=4 -s -load
Loading workload...
新术语和重要单词以粗体显示。 例如,您在屏幕、菜单或对话框中看到的文字会出现在文本中,如下所示:“从AWS 管理控制台验证启动”。
备注
警告或重要说明会出现在这样的框中。
提示
提示和技巧如下所示。
读者反馈
欢迎读者的反馈。 让我们知道你对这本书的看法-你喜欢什么或不喜欢什么。 读者反馈对于我们开发真正能让您获得最大收益的图书非常重要。
要向我们发送一般反馈,只需向<[feedback@packtpub.com](mailto:feedback@packtpub.com)>
发送一封电子邮件,并在消息主题中提及书名。
如果有一个您擅长的主题,并且您有兴趣撰写或投稿一本书,请参阅我们位于www.Packtpub.com/Authors上的作者指南。
客户支持
现在您已经成为 Packt 图书的拥有者,我们有很多东西可以帮助您从购买中获得最大价值。
下载示例代码
您可以从您的帐户http://www.packtpub.com下载购买的所有 Packt 图书的示例代码文件。 如果您在其他地方购买了本书,您可以访问http://www.packtpub.com/support并注册,以便将文件通过电子邮件直接发送给您。
勘误表
虽然我们已经竭尽全力确保内容的准确性,但错误还是会发生。 如果您在我们的一本书中发现错误--可能是文本或代码中的错误--如果您能向我们报告,我们将不胜感激。 通过这样做,您可以将其他读者从挫折中解救出来,并帮助我们改进本书的后续版本。 如果您发现任何勘误表,请访问http://www.packtpub.com/support,选择您的图书,单击勘误表提交表链接,然后输入勘误表的详细信息。 一旦您的勘误表被核实,您提交的勘误表将被接受,勘误表将被上传到我们的网站,或添加到该标题勘误表部分下的任何现有勘误表列表中。
盗版
在互联网上盗版版权材料是所有媒体持续存在的问题。 在 Packt,我们非常重视版权和许可证的保护。 如果您在互联网上发现任何形式的非法复制我们的作品,请立即提供我们的位置、地址或网站名称,以便我们采取补救措施。
请拨打<[copyright@packtpub.com](mailto:copyright@packtpub.com)>
与我们联系,并提供疑似盗版材料的链接。
我们感谢您在保护我们的作者方面的帮助,以及我们为您提供有价值内容的能力。
问题
如果您对本书的任何方面有任何问题,可以拨打<[questions@packtpub.com](mailto:questions@packtpub.com)>
与我们联系,我们将尽最大努力解决。
一、配置 HBase 集群
在本章中,我们将介绍:
- 快速启动
- 为 Amazon EC2 做好准备
- 设置 Hadoop
- 设置动物园管理员
- 更改内核设置
- 设置 HBase
- 基本 Hadoop/ZooKeeper/HBase 配置
- 设置多个高可用性(HA)主服务器
简介
本章介绍如何在 Amazon EC2 上设置 HBase 集群,从基本的独立 HBase 实例到完全分布式、高度可用的 HBase 集群。
根据 Apache HBase 的主页:
HBase 是 Hadoop 数据库。 当您需要对大数据进行随机、实时读/写访问时,请使用 HBase。 该项目的目标是在商用硬件集群上托管非常大的表-数十亿行 X 数百万列。
HBase 可以在任何文件系统上运行。 例如,您可以在 EXT4 本地文件系统、Amazon Simple Storage Service(Amazon S3)和Hadoop Distributed File System(HDFS)上运行 HBase,后者是 Hadoop 的主要分布式文件系统。 在大多数情况下,完全分布式的 HBase 集群在 HDFS 实例上运行,因此在继续之前,我们将解释如何设置 Hadoop。
Apache ZooKeeper 是一个开源软件,提供高度可靠的分布式协调服务。 分布式 HBase 依赖于正在运行的 ZooKeeper 集群。
HBase 是一个运行在 Hadoop 上的数据库,它同时保持大量文件处于打开状态。 我们需要更改一些 Linux 内核设置才能顺利运行 HBase。
一个完全分布式的 HBase 集群有一个或多个主节点(HMaster)和多个从节点(RegionServer),主节点(HMaster)负责协调整个集群,从节点(RegionServer)负责处理实际的数据存储和请求。 下图显示了典型的 HBase 集群结构:
HBase 可以同时运行多个主节点,并使用 ZooKeeper 对主节点进行监控和故障切换。 但是,由于 HBase 使用 HDFS 作为其低层文件系统,如果 HDFS 关闭,HBase 也会关闭。 HDFS 的主节点称为 NameNode,是 HDFS 的单点故障(Single Point of Failure,SPOF),因此它是 HBase 集群的单点故障(Single Point of Failure,SPOF)。 然而,NameNode 作为一个软件是非常健壮和稳定的。 此外,HDFS 团队正在努力开发一个真正的 HA NameNode,预计它将包含在 Hadoop 的下一个主要版本中。
本章的前七个菜谱解释了如何让 HBase 及其所有依赖项协同工作,作为一个完全分布式的 HBase 集群。 最后一个食谱解释了一个关于如何避免集群的 SPOF 问题的高级主题。
我们将从设置一个独立的 HBase 实例开始,然后演示如何在 Amazon EC2 上设置分布式 HBase 集群。
快速入门
HBase 有两种运行模式-独立模式和分布式模式。 单机模式是 HBase 的默认模式。 在独立模式下,HBase 使用本地文件系统而不是 HDFS,并在同一 JVM 中运行所有 HBase 守护进程和由 HBase 管理的 ZooKeeper 实例。
本食谱描述了独立 HBase 的设置。 它将引导您完成安装 HBase、以独立模式启动它、通过 HBase Shell 创建表、插入行,然后清理和关闭独立 HBase 实例。
做好准备
您将需要一台 Linux 机器来运行堆栈。 不建议在 Windows 上运行 HBase。 在本书中,我们将使用 Debian 6.0.1(Debian Squze),因为在我的乐天公司(Rakuten Inc.)的生产中,我们有几个运行在 Debian 之上的 Hadoop/HBase 集群,而 6.0.1 是我们在http://wiki.debian.org/Cloud/AmazonEC2Image上拥有的最新的Amazon Machine Image。
由于 HBase 是用 Java 编写的,您需要先安装 Java。 HBase 只能在 Oracle 的 JDK 上运行,因此不要使用 OpenJDK 进行设置。 虽然可以使用 Java7,但我们不建议您现在使用 Java7,因为它需要更多时间进行测试。 您可以从以下链接下载最新的 JavaSE6:http://www.oracle.com/technetwork/java/javase/downloads/index.html。
执行下载的 bin
文件安装 Java SE 6。我们将在本书中使用 /usr/local/jdk1.6
作为 JAVA_HOME
:
root# ln -s /your/java/install/directory /usr/local/jdk1.6
我们将添加一个名为 hadoop
的用户,作为所有 HBase/Hadoop 守护进程和文件的所有者。 我们将所有 HBase 文件和数据存储在 /usr/local/hbase:
下
root# useradd hadoop
root# mkdir /usr/local/hbase
root# chown hadoop:hadoop /usr/local/hbase
怎么做……
从 HBase 官方网站http://www.apache.org/dyn/closer.cgi/hbase/获取最新稳定的 HBase 版本。 在撰写本书时,当前的稳定版本是 0.92.1。
您可以按照以下说明设置独立的 HBase 实例:
-
下载 tarball 并解压缩到我们的 HBase 根目录。 我们将使用以下命令设置
HBASE_HOME
环境变量以简化设置:root# su - hadoop hadoop$ cd /usr/local/hbase hadoop$ tar xfvz hbase-0.92.1.tar.gz hadoop$ ln -s hbase-0.92.1 current hadoop$ export HBASE_HOME=/usr/local/hbase/current
-
使用以下命令在 HBase 的环境设置文件中设置
JAVA_HOME
:hadoop$ vi $HBASE_HOME/conf/hbase-env.sh # The java implementation to use. Java 1.6 required. export JAVA_HOME=/usr/local/jdk1.6
-
使用以下命令为 HBase 创建存储其数据的目录,并在 HBase 配置文件(
hbase-site.xml
)中设置<configuration>
标记之间的路径:hadoop$ mkdir -p /usr/local/hbase/var/hbase hadoop$ vi /usr/local/hbase/current/conf/hbase-site.xml <property> <name>hbase.rootdir</name> <value>file:///usr/local/hbase/var/hbase</value> </property>
-
使用以下命令在独立模式下启动 HBase:
hadoop$ $HBASE_HOME/bin/start-hbase.sh starting master, logging to /usr/local/hbase/current/logs/hbase-hadoop-master-master1.out
-
使用以下命令通过 HBase Shell 连接到正在运行的 HBase:
hadoop$ $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.1, r1298924, Fri Mar 9 16:58:34 UTC 2012
-
Verify HBase's installation by creating a table and then inserting some values. Create a table named
test
, with a single column family namedcf1
, as shown here:hbase(main):001:0> create 'test', 'cf1' 0 row(s) in 0.7600 seconds
I.要列出新创建的表,请使用以下命令:
hbase(main):002:0> list TABLE test 1 row(s) in 0.0440 seconds
二、。 要将一些值插入到新创建的表中,请使用以下命令:
hbase(main):003:0> put 'test', 'row1', 'cf1:a', 'value1' 0 row(s) in 0.0840 seconds hbase(main):004:0> put 'test', 'row1', 'cf1:b', 'value2' 0 row(s) in 0.0320 seconds
-
使用
scan
命令验证我们插入到 HBase 中的数据:hbase(main):003:0> scan 'test' ROW COLUMN+CELL row1 column=cf1:a, timestamp=1320947312117, value=value1 row1 column=cf1:b, timestamp=1320947363375, value=value2 1 row(s) in 0.2530 seconds
-
Now clean up all that was done, by using the
disable
anddrop
commands:I.要禁用表测试,请使用以下命令:
hbase(main):006:0> disable 'test' 0 row(s) in 7.0770 seconds
二、。 要删除该表测试,请使用以下命令:
hbase(main):007:0> drop 'test' 0 row(s) in 11.1290 seconds
-
使用以下命令退出 HBase Shell:
hbase(main):010:0> exit
-
通过执行
stop
脚本停止 HBase 实例:
hadoop$ /usr/local/hbase/current/bin/stop-hbase.sh
stopping hbase.......
它是如何工作的.
我们在一台服务器上安装了 HBase 0.92.1。 我们为它使用了一个名为 current
的符号链接,这样将来的版本升级就很容易了。
为了通知 HBase 安装 Java 的位置,我们将在 hbase-env.sh
中设置 JAVA_HOME
,这是 HBase 的环境设置文件。 您还将在其中看到一些 Java 堆和 HBase 守护进程设置。 我们将在本书的最后两章讨论这些设置。
在步骤 1 中,我们在本地文件系统上创建了一个目录,供 HBase 存储其数据。 对于完全分布式安装,需要将 HBase 配置为使用 HDFS,而不是本地文件系统。 HBase 主守护进程(HMaster)在执行 start-hbase.sh
的服务器上启动。 因为我们在这里没有配置区域服务器,所以 HBase 也会在同一个 JVM 上启动一个从守护进程(HRegionServer)。
正如我们在简介部分中提到的,HBase 依赖于 ZooKeeper 作为其协调服务。 您可能已经注意到,我们在前面的步骤中没有启动 ZooKeeper。 这是因为默认情况下,HBase 将启动并管理自己的动物园管理员组合。
然后我们通过 HBase Shell 连接到 HBase。 使用 HBase Shell,您可以管理集群、访问 HBase 中的数据以及执行许多其他工作。 在这里,我们刚刚创建了一个名为 test
的表,我们将数据插入到 HBase 中,扫描 test
表,然后禁用并删除它,然后退出 shell。
可以使用其 stop-hbase.sh
脚本停止 HBase。 此脚本停止 HMaster 和 HRegionServer 守护进程。
为 Amazon EC2 做好准备
Amazon Elastic Compute Cloud(EC2)是一项 Web 服务,可在云中提供可调整大小的计算机容量。 通过使用 Amazon EC2,我们可以轻松、低成本地在完全分布式模式下实践 HBase。 我们将在本书中用来演示 HBase 的所有服务器都运行在 Amazon EC2 上。
本食谱描述了 Amazon EC2 环境的设置,为在其上安装 HBase 做准备。 我们将在 Amazon EC2 上设置名称服务器和客户端。 您还可以使用其他托管服务,如 Rackspace 或真实服务器来设置您的 HBase 集群。
做好准备
您需要在http://aws.amazon.com/注册或创建亚马逊网络服务(AWS)帐户。
我们将使用 EC2 命令行工具来管理我们的实例。 您可以按照以下页面提供的说明下载和设置工具:
http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?SettingUp_CommandLine.html。
您需要公钥/私钥才能登录到 EC2 实例。 您可以使用以下说明生成密钥对并将公钥上载到 EC2:
http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/generating-a-keypair.html。
您必须先授权访问,然后才能登录实例。 以下链接包含向默认安全组添加规则的说明:
http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/adding-security-group-rules.html。
完成所有这些步骤后,请查看以下核对表,以确保一切准备就绪:
- X.509 证书:检查是否上传了 X.509 证书。 您可以在帐户的安全凭证页面进行检查。
- EC2 密钥对:检查是否上传了 EC2 密钥对。 您可以在AWS 管理控制台|Amazon EC2|网络&安全|密钥对查看此信息。
- 访问:检查访问是否已授权。 可通过AWS 管理控制台|Amazon EC2|网络&安全|安全组|入站进行检查。
- 环境变量设置:检查环境变量设置是否已完成。 例如,以下代码片段显示了我的设置;请确保您使用的是您所在地区的正确
EC2_URL
:
$ cat ~/.bashrc
export EC2_HOME=~/opt/ec2-api-tools-1.4.4.2
export PATH=$PATH:$EC2_HOME/bin
export EC2_PRIVATE_KEY=~/.ec2/pk-OWRHNWUG7UXIOPJXLOBC5UZTQBOBCVQY.pem
export EC2_CERT=~/.ec2/cert-OWRHNWUG7UXIOPJXLOBC5UZTQBOBCVQY.pem
export JAVA_HOME=/Library/Java/Home
export EC2_URL=https://ec2.us-west-1.amazonaws.com
我们需要导入 EC2 密钥对以通过 EC2 命令行工具管理 EC2 实例:
$ ec2-import-keypair your-key-pair-name --public-key-file ~/.ssh/id_rsa.pub
通过键入以下命令验证设置:
$ ec2-describe-instances
如果一切都已正确设置,该命令将以类似于您在前一个命令中配置它们的方式显示您的实例。
提示
下载示例代码
您可以从您的帐户http://www.packtpub.com下载购买的所有 Packt 图书的示例代码文件。 如果您在其他地方购买了本书,您可以访问http://www.packtpub.com/support并注册,以便将文件通过电子邮件直接发送给您。
最后的准备是找到合适的 AMI。 AMI 是预配置的操作系统和软件,用于在 EC2 中创建虚拟机。 我们可以在http://wiki.debian.org/Cloud/AmazonEC2Image找到注册的 Debian AMI。
就实践 HBase 而言,使用 32 位、EBS 支持的 AMI 是最具成本效益的 AMI。 确保您选择的是您所在地区的 AMI。 由于本书使用 US-WEST(US-WEST-1),因此我们的 AMI ID 为 ami-77287b32
。 这是一个 32 位的 EC2 小实例。 一个小实例很适合在 EC2 上练习 HBase,因为它很便宜。 对于生产,我们建议您至少将高内存超大型实例与 EBS 配合使用,或者使用真正的服务器。
怎么做……
按照以下说明为您的 EC2 实例做好 HBase 的准备。 我们将启动两个 EC2 实例;一个是 DNS/NTP 服务器,另一个是客户端:
-
启动上述服务器的微型实例。 在本书后面的部分中,我们将使用
ns1.hbase-admin-cookbook.com (ns1)
作为其完全限定域名(FQDN):$ ec2-run-instances ami-77287b32 -t t1.micro -k your-key-pair
-
为客户端启动一个小实例。 我们将在本书后面使用
client1.hbase-admin-cookbook.com (client1)
作为其 FQDN:$ ec2-run-instances ami-77287b32 -t m1.small -k your-key-pair
-
Verify the startup from AWS Management Console, or by typing the following command:
$ ec2-describe-instances
- 您应该从该命令的输出中看到两个实例。 从
ec2-describe-instances
命令或AWS 管理控制台的输出中,您可以找到已经启动的实例的公共 DNS。 DNS 显示类似ec2-xx-xx-xxx-xx.us-west-1
.computer te.amazonaws.com 的值:
- 您应该从该命令的输出中看到两个实例。 从
-
使用以下命令通过
SSH
登录实例:$ ssh root@ec2-xx-xx-xxx-xx.us-west-1.compute.amazonaws.com
-
在服务器上安装软件包之前,请使用以下命令更新软件包索引文件:
root# apt-get update
-
使用以下命令将实例的时区更改为您的本地时区:
root# dpkg-reconfigure tzdata
-
使用以下命令在 DNS 服务器上安装 NTP 服务器守护程序:
root@ns# apt-get install ntp ntp-server ntpdate
-
使用以下命令在客户端/服务器上安装 NTP 客户端:
root@client1# apt-get install ntp ntpdate
-
Configure
/etc/ntp.conf
onns1
to run as an NTP server, andclient1
to run as an NTP client, usingns1
as its server.由于 NTP 设置没有特定于 HBase 的配置,因此我们将跳过详细信息。 您可以从本书的示例源代码中找到服务器和客户端的示例
ntp.conf
文件。 -
使用以下命令在
ns1
上安装 BIND9 以作为 DNS 服务器运行:
```scala
root@ns# apt-get install bind9
```
* You will need to configure BIND9 to run as a primary master server for internal lookup, and run as a caching server for external lookup. You also need to configure the DNS server, to allow other EC2 instances to update their record on the DNS server.
我们将跳过细节,因为这超出了本书的范围。 有关示例 BIND9 配置,请参阅本书附带的源代码。
- 对于
client1
,只需使用ns1
作为其 DNS 服务器进行设置:
```scala
root@client1# vi /etc/resolv.conf
nameserver 10.160.49.250 #private IP of ns
search hbase-admin-cookbook.com #domain name
```
- Update the DNS hostname automatically. Set up hostname to the EC2 instance's user data of the client. From the My Instances page of AWS Management Console, select
client1
from the instances list, stop it, and then click Instance Actions | View | Change User Data; enter thehostname
of the instance you want to use (hereclient1)
in the pop-up page:

- 使用用户数据创建脚本以更新 DNS 服务器上的客户端记录:
```scala
root@client1# vi ec2-hostname.sh
#!/bin/bash
#you will need to set up your DNS server to allow update from this key
DNS_KEY=/root/etc/Kuser.hbase-admin-cookbook.com.+157+44141.private
DOMAIN=hbase-admin-cookbook.com
USER_DATA=`/usr/bin/curl -s http://169.254.169.254/latest/user-data`
HOSTNAME=`echo $USER_DATA`
#set also the hostname to the running instance
hostname $HOSTNAME
#we only need to update for local IP
LOCIP=`/usr/bin/curl -s http://169.254.169.254/latest/meta-data/local-ipv4`
cat<<EOF | /usr/bin/nsupdate -k $DNS_KEY -v
server ns.$DOMAIN
zone $DOMAIN
update delete $HOSTNAME.$DOMAIN A
update add $HOSTNAME.$DOMAIN 60 A $LOCIP
send
EOF
```
- 最后,要在引导时从
rc.local
运行此脚本,请将以下脚本添加到rc.local
文件:
```scala
root@client1# vi /etc/rc.local
sh /root/bin/ec2-hostname.sh
```
它是如何工作的.
首先,我们启动了两个实例,一个是 DNS/NTP 服务器的微型实例,另一个是客户端的小实例。 要向其他实例提供名称服务,DNS 名称服务器必须保持运行。 使用微实例可以降低您的 EC2 成本。
在步骤 3 中,我们设置了 NTP 服务器和客户端。 我们将在同一 DNS 服务器上运行我们自己的 NTP 服务器,并在所有其他服务器上运行 NTP 客户端。
备注
注意:请确保 HBase 集群成员上的时钟基本对齐。
EC2 实例可以按需启动和停止;我们不需要为停止的实例付费。 但是,重启 EC2 实例会更改实例的 IP 地址,这将使运行 HBase 变得困难。 我们可以通过运行 DNS 服务器为 HBase 集群中的所有 EC2 实例提供名称服务来解决此问题。 我们可以在每次重新启动其他 EC2 实例时更新 DNS 服务器上的 name
记录。
这正是我们在步骤 4 和 5 中所做的。步骤 4 是正常的 DNS 设置。 在第 5 步中,我们首先将实例名称存储在它的 User Data 属性中,这样当实例重新启动时,我们可以使用 EC2API 将其取回。 此外,我们还将通过 EC2API 获取实例的内网 IP 地址。 有了这些数据,我们就可以在每次实例重新启动时向 DNS 服务器发送 DNS update
命令。 因此,我们可以始终使用其固定主机名来访问实例。
我们将只保持 DNS 实例持续运行。 您可以在不需要运行 HBase 集群时停止所有其他实例。
设置 Hadoop
完全分布式的 HBase 运行在 HDFS 之上。 作为完全分布式的 HBase 集群安装,其主守护进程(HMaster)通常与 HDFS(NameNode)的主节点运行在同一服务器上,而其从守护进程(HRegionServer)与 HDFS 的从节点(称为 DataNode)运行在同一服务器上。
HBase 不需要 Hadoop MapReduce。 不需要启动 MapReduce 后台进程。 如果您喜欢在 HBase 上运行 MapReduce,我们还将在本食谱中介绍 MapReduce 的设置。 对于小型 Hadoop 集群,我们通常在 NameNode 服务器上运行 MapReduce(JobTracker)的主守护进程,在 DataNode 服务器上运行 MapReduce(TaskTracker)的从守护进程。
本食谱描述了 Hadoop 的设置。 我们将有一个主节点(master1
)在其上运行 NameNode 和 JobTracker。 我们将设置三个从节点(slave1
到 slave3)
,它们将分别在它们上运行 DataNode 和 TaskTracker。
做好准备
您需要四个小型 EC2 实例,可以使用以下命令获得它们:
$ec2-run-instances ami-77287b32 -t m1.small -n 4 -k your-key-pair
所有这些实例都必须正确设置,如前面的食谱在 Amazon EC2上做好准备中所述。 除了 NTP 和 DNS 设置外,所有服务器都需要安装 Java。
我们将使用 hadoop
用户作为所有 Hadoop 守护进程和文件的所有者。 所有 Hadoop 文件和数据都将存储在 /usr/local/hadoop
下。 提前添加 hadoop
用户并在所有服务器上创建一个 /usr/local/hadoop
目录。
我们还将设置一个 Hadoop 客户端节点。 我们将使用在上一个食谱中设置的 client1
。 因此,Java 安装、 hadoop
用户和目录也应该在 client1
上准备好。
怎么做……
以下是设置完全分布式 Hadoop 集群的步骤:
-
要通过 SSH 登录到集群的所有节点,需要在主节点上生成
hadoop
用户的公钥:hadoop@master1$ ssh-keygen -t rsa -N ""
- 此命令将在
~/.ssh/id_rsa.pub.
为主节点上的hadoop
用户创建公钥
- 此命令将在
-
在所有从节点和客户端节点上,添加
hadoop
用户的公钥以允许从主节点 SSH 登录:hadoop@slave1$ mkdir ~/.ssh hadoop@slave1$ chmod 700 ~/.ssh hadoop@slave1$ cat >> ~/.ssh/authorized_keys
-
复制您在上一步中生成的
hadoop
用户的公钥,并粘贴到~/.ssh/authorized_keys
。 然后,将其权限更改如下:hadoop@slave1$ chmod 600 ~/.ssh/authorized_keys
-
从 Hadoop 的官方网站http://www.apache.org/dyn/closer.cgi/hadoop/common/获取最新的、稳定的、受 HBase 支持的 Hadoop 版本。 在撰写本章时,HBase 支持的最新稳定 Hadoop 版本是 1.0.2。 下载 tarball 并解压缩到 Hadoop 的
root
目录,然后添加一个符号链接和一个环境变量:hadoop@master1$ ln -s hadoop-1.0.2 current hadoop@master1$ export HADOOP_HOME=/usr/local/hadoop/current
-
在主节点上创建以下目录:
hadoop@master1$ mkdir -p /usr/local/hadoop/var/dfs/name hadoop@master1$ mkdir -p /usr/local/hadoop/var/dfs/data hadoop@master1$ mkdir -p /usr/local/hadoop/var/dfs/namesecondary
-
如果不使用 MapReduce,可以跳过以下步骤:
hadoop@master1$ mkdir -p /usr/local/hadoop/var/mapred
-
在 Hadoop 的环境设置文件(hadoop-env.sh)中设置
JAVA_HOME
:hadoop@master1$ vi $HADOOP_HOME/conf/hadoop-env.sh export JAVA_HOME=/usr/local/jdk1.6
-
将
hadoop.tmp.dir
属性添加到core-site.xml:
hadoop@master1$ vi $HADOOP_HOME/conf/core-site.xml <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop/var</value> </property>
-
将
fs.default.name
属性添加到core-site.xml:
hadoop@master1$ vi $HADOOP_HOME/conf/core-site.xml <property> <name>fs.default.name</name> <value>hdfs://master1:8020</value> </property>
-
如果需要 MapReduce,请将
mapred.job.tracker
属性添加到mapred-site.xml:
```scala
hadoop@master1$ vi $HADOOP_HOME/conf/mapred-site.xml
<property>
<name>mapred.job.tracker</name>
<value>master1:8021</value>
</property>
```
- 将从服务器列表添加到
slaves
文件:
```scala
hadoop@master1$ vi $HADOOP_HOME/conf/slaves
slave1
slave2
slave3
```
- 将所有 Hadoop 文件从主节点同步到客户端和从节点。 初始安装后不同步
${hadoop.tmp.dir}
:
```scala
hadoop@master1$ rsync -avz /usr/local/hadoop/ client1:/usr/local/hadoop/
hadoop@master1$ for i in 1 2 3
do rsync -avz /usr/local/hadoop/ slave$i:/usr/local/hadoop/
sleep 1
done
```
- 您需要在启动 Hadoop 之前格式化 NameNode。 仅对初始安装执行此操作:
```scala
hadoop@master1$ $HADOOP_HOME/bin/hadoop namenode -format
```
- 从
master
节点启动 HDFS:
```scala
hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh
```
- You can access your HDFS by typing the following command:
```scala
hadoop@master1$ $HADOOP_HOME/bin/hadoop fs -ls /
```
* 您还可以从浏览器查看 HDFS 管理页面。 确保`50070`端口已打开。 可以在`http://master1:50070/dfshealth.jsp:` 查看 HDFS 管理页面

- Start MapReduce from the master node, if needed:
```scala
hadoop@master1$ $HADOOP_HOME/bin/start-mapred.sh
```
* 现在,您可以从浏览器访问 MapReduce 管理页面。 确保`50030`端口已打开。 可在`http://master1:50030/jobtracker.jsp:` 查看 MapReduce 管理页面

- 要停止 HDFS,请从主节点执行以下命令:
```scala
hadoop@master1$ $HADOOP_HOME/bin/stop-dfs.sh
```
- 要停止 MapReduce,请从主节点执行以下命令:
```scala
hadoop@master1$ $HADOOP_HOME/bin/stop-mapred.sh
```
它是如何工作的.
要从主节点启动/停止远程从服务器上的守护程序,需要 hadoop
用户的无密码 SSH 登录。 我们在步骤 1 中做到了这一点。
HBase 必须在支持持久 sync
实现的特殊 HDFS 上运行。 如果 HBase 运行在没有持久的 sync
实现的 HDFS 上,那么如果它的从服务器宕机,它可能会丢失数据。 高于 0.20.205 的 Hadoop 版本,包括我们选择的 Hadoop 1.0.2,都支持此功能。
HDFS 和 MapReduce 使用本地文件系统存储数据。 我们在步骤 3 中创建了 Hadoop 所需的目录,并在步骤 5 中设置了 Hadoop 配置文件的路径。
在步骤 9 到 11 中,我们设置了 Hadoop,以便它可以找到 HDFS、JobTracker 和从服务器。 在启动 Hadoop 之前,所有 Hadoop 目录和设置都需要与从服务器同步。 第一次启动 Hadoop(HDFS)时,需要格式化 NameNode。 请注意,您应该仅在初始安装时执行此操作。
此时,您可以使用 Hadoop 的启动/停止脚本启动/停止 Hadoop。 这里我们分别启动/停止了 HDFS 和 MapReduce,以防您不需要 MapReduce。 您还可以使用 $HADOOP_HOME/bin/start-all.sh
和 stop-all.sh
通过一个命令启动/停止 HDFS 和 MapReduce。
设置动物园管理员
分布式 HBase 依赖于正在运行的 ZooKeeper 集群。 所有 HBase 集群节点和客户端都需要能够访问 ZooKeeper 集合。
本食谱介绍了如何设置动物园管理员集群。 我们将只为我们的 HBase 集群设置一个独立的 ZooKeeper 节点,但在生产中,建议您运行至少包含三个节点的 ZooKeeper 集成。 此外,请确保运行奇数个节点。
我们将在中介绍如何设置集群式动物园管理员。 。 这份食谱的一节。
做好准备
首先,确保您的 ZooKeeper 服务器中安装了 Java。
我们将使用 hadoop
用户作为所有 ZooKeeper 守护进程和文件的所有者。 所有 ZooKeeper 文件和数据都将存储在 /usr/local/ZooKeeper
下;您需要提前创建此目录。 我们的动物园管理员也会设在 master1
。
我们将在 client1
上设置一个动物园管理员客户端。 因此,Java 安装、 hadoop
用户和目录也应该在 client1
上准备好。
怎么做……
要设置独立的 ZooKeeper 安装,请按照以下说明操作:
-
从 ZooKeeper 的官方网站http://ZooKeeper.apache.org/releases.html#download获取最新稳定的 ZooKeeper 版本。
-
下载 tarball 并解压缩到 ZooKeeper 的根目录。 我们将设置
ZK_HOME
环境变量以简化设置。 在撰写本文时,ZooKeeper 3.4.3 是最新的稳定版本:hadoop@master1$ ln -s ZooKeeper-3.4.3 current hadoop@master1$ export ZK_HOME=/usr/local/ZooKeeper/current
-
为 ZooKeeper 创建目录以存储其快照和事务日志:
hadoop@master1$ mkdir -p /usr/local/ZooKeeper/data hadoop@master1$ mkdir -p /usr/local/ZooKeeper/datalog
-
创建
$ZK_HOME/conf/java.env
文件并将 Java 设置放在那里:hadoop@master1$ vi $ZK_HOME/conf/java.env JAVA_HOME=/usr/local/jdk1.6 export PATH=$JAVA_HOME/bin:$PATH
-
复制示例 ZooKeeper 设置文件,并进行以下更改以设置 ZooKeeper 应存储其数据的位置:
hadoop@master1$ cp $ZK_HOME/conf/zoo_sample.cfg $ZK_HOME/conf/zoo.cfg hadoop@master1$ vi $ZK_HOME/conf/zoo.cfg dataDir=/usr/local/ZooKeeper/var/data dataLogDir=/usr/local/ZooKeeper/var/datalog
-
将
/usr/local/ZooKeeper
下的所有文件从主节点同步到客户端。 在此初始安装之后,不要同步${dataDir}
和${dataLogDir}
。 -
通过执行以下命令从主节点启动 ZooKeeper:
hadoop@master1$ $ZK_HOME/bin/zkServer.sh start
-
连接到正在运行的 ZooKeeper,并执行一些命令来验证安装:
hadoop@client1$ $ZK_HOME/bin/zkCli.sh -server master1:2181 [zk: master1:2181(CONNECTED) 0] ls / [ZooKeeper] [zk: master1:2181(CONNECTED) 1] quit
-
通过执行以下命令从主节点停止 ZooKeeper:
hadoop@master1$ $ZK_HOME/bin/zkServer.sh stop
它是如何工作的.
在本食谱中,我们设置了一个基本的独立 ZooKeeper 实例。 如您所见,设置非常简单;您所需要做的就是告诉 ZooKeeper 哪里可以找到 Java,哪里可以保存它的数据。
在步骤 4 中,我们创建了一个名为 java.env
的文件,并将 Java 设置放在该文件中。 您必须使用此文件名作为 ZooKeeper,默认情况下,ZooKeeper 从此文件获取其 Java 设置。
ZooKeeper 的设置文件称为 zoo.cfg
。 您可以从 ZooKeeper 附带的示例文件中复制设置。 默认设置适用于基本安装。 由于 ZooKeeper 总是在集群系统中扮演核心角色,因此应该对其进行适当的设置,以获得最佳性能。
要连接到正在运行的 ZooKeeper 集合,请使用其命令行工具,并指定要连接的 ZooKeeper 服务器和端口。 默认客户端端口为 2181
。 如果您使用的是默认端口设置,则不需要指定它。
所有 ZooKeeper 数据都称为Znode。 Znode 的结构类似于文件系统层次结构。 ZooKeeper 提供了从其命令行工具访问或更新 Znode 的命令;键入 help
可获得更多信息。
还有更多...
由于 HBase 依赖 ZooKeeper 作为其协调服务,因此 ZooKeeper 服务必须非常可靠。 在生产中,您必须运行至少包含三个节点的 ZooKeeper 群集。 此外,请确保运行奇数个节点。
设置集群式动物园管理员的步骤与本食谱中所示的基本相同。 您可以按照前面的步骤首先设置每个集群节点。 将以下设置添加到每个节点的 zoo.cfg
,以便每个节点都知道系综中的所有其他节点:
hadoop@node{1,2,3}$ vi $ZK_HOME/conf/zoo.cfg
server.1=node1:2888:3888
server.2=node2:2888:3888
server.3=node3:2888:3888
此外,您还需要在 ${dataDir}
下放置一个 myid
文件。 myid
文件由一行组成,仅包含节点 ID。因此, node1
的 myid
将不包含文本 1
。
备注
请注意,所有 ZooKeeper 节点上的时钟必须同步。 您可以使用网络时间协议(NTP)同步时钟。
分别从集群的每个节点启动 ZooKeeper。 然后,您可以使用以下命令从客户端连接到群集:
$ zkCli.sh -server node1,node2,node3
只要 ZooKeeper 群集中超过一半的节点处于活动状态,ZooKeeper 就会正常工作。 这意味着,在一个三节点集群中,只有一台服务器可以失效。
更改内核设置
HBase 是一个运行在 Hadoop 上的数据库,就像其他数据库一样,它同时保持许多文件处于打开状态。 Linux 限制任何一个进程可以打开的文件描述符的数量;默认限制是每个进程 1024 个。 要平稳运行 HBase,您需要为启动 HBase 的用户增加打开文件描述符的最大数量。 在我们的示例中,用户名为 hadoop
。
您还应该增加 Hadoop 的 nproc
设置。 nproc
设置指定用户可以同时存在的最大进程数。 如果 nproc
太低,可能会发生 OutOfMemoryError
错误。
在本指南中,我们将介绍如何显示和更改内核设置。
做好准备
确保您在所有服务器上都拥有 root
权限。
怎么做……
您需要对群集的所有服务器进行以下内核设置更改:
-
要确认当前打开的文件限制,请以
hadoop
用户身份登录并执行以下命令:hadoop$ ulimit -n 1024
-
要显示最大进程数的设置,请使用
ulimit
命令的-u
选项:hadoop$ ulimit -u unlimited
-
以
root
用户身份登录以增加打开文件和nproc
限制。 将以下设置添加到limits.conf
文件:root# vi /etc/security/limits.conf hadoop soft nofile 65535 hadoop hard nofile 65535 hadoop soft nproc 32000 hadoop hard nproc 32000
-
要应用更改,请将以下行添加到
/etc/pam.d/common-session
文件中:root# echo "session required pam_limits.so" >> /etc/pam.d/common-session
-
以
hadoop
用户身份注销并重新登录,并再次确认设置值;您应该看到已应用上述更改:hadoop$ ulimit -n 65535 hadoop$ ulimit -u 32000
它是如何工作的.
前面的设置将 hadoop
用户的打开文件限制更改为 65535
。 它还将 hadoop
用户的最大进程数更改为 32000
。 更改内核设置后,HBase 可以同时打开足够多的文件,并且运行平稳。
另请参阅
- 第 8 章,基本性能调整
设置 HBase
全分布式 HBase 实例有一个或多个主节点(HMaster),以及多个在 HDFS 上运行的从节点(RegionServer)。 它使用可靠的 ZooKeeper 集合来协调集群的所有组件,包括主组件、从组件和客户端组件。
没有必要在 HDFS NameNode 的同一台服务器上运行 HMaster,但对于小型集群来说,为了便于管理,通常会将它们运行在同一台服务器上。 RegionServers 通常配置为在 HDFS DataNode 的服务器上运行。 在 DataNode 服务器上运行 RegionServer 还具有数据局部性的优势。 最终,在同一服务器上运行的 DataNode 将拥有 RegionServer 所需的所有数据的副本。
本食谱描述了完全分布式 HBase 的设置。 我们将在 master1
上设置一个 HMaster,并在 (slave1
到 slave3)
上设置三个区域服务器。 我们还将在 client1
上设置一个 HBase 客户端。
做好准备
首先,确保集群的所有服务器上都安装了 Java。
我们还将使用 hadoop
用户作为所有 HBase 守护进程和文件的所有者。 所有 HBase 文件和数据都将存储在 /usr/local/hbase
下。 提前在您的 HBase 集群的所有服务器上创建此目录。
我们将在 client1
上设置一个 HBase 客户端。 因此,Java 安装、 hadoop
用户和目录也应该在 client1
上准备好。
确保 HDFS 正在运行。 您可以使用以下命令访问 HDFS,以确保其正常启动:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -ls /
MapReduce 不需要启动,因为 HBase 通常不使用它。
我们假设您正在管理您自己的动物园管理员,在这种情况下,您可以启动它并确认它是否正常运行。 您可以通过将 ruok
命令发送到其客户端端口来确保其正常运行
hadoop@client1$ echo ruok | nc master1 2181
怎么做……
要设置完全分布式的 HBase 集群,我们将首先在主节点上下载并配置 HBase,然后同步到所有从节点和客户端。
从 HBase 官方网站http://www.apache.org/dyn/closer.cgi/hbase/获取最新稳定的 HBase 版本。
在撰写本书时,当前的稳定版本是 0.92.1。
-
下载 tarball 并解压缩到 HBase 的
root
目录。 另外,设置HBASE_HOME
环境变量以简化设置:hadoop@master1$ ln -s hbase-0.92.1 current hadoop@master1$ export HBASE_HOME=/usr/local/hbase/current
-
我们将使用
/usr/local/hbase/var
作为本地文件系统上 HBase 的临时目录。 如果您已为独立的 HBase 安装创建了它,请先将其删除:hadoop@master1$ mkdir -p /usr/local/hbase/var
-
要告诉 HBase Java 安装在哪里,请在 HBase 环境设置文件(hbase-env.sh)中设置
JAVA_HOME
:hadoop@master1$ vi $HBASE_HOME/conf/hbase-env.sh # The java implementation to use. Java 1.6 required. export JAVA_HOME=/usr/local/jdk1.6
-
设置 HBase 以使用独立的 ZooKeeper 合奏:
hadoop@master1$ vi $HBASE_HOME/conf/hbase-env.sh # Tell HBase whether it should manage it's own instance of ZooKeeper or not. export HBASE_MANAGES_ZK=false
-
将这些设置添加到 HBase 的配置文件(
hbase-site.xml
):hadoop@master1$ vi $HBASE_HOME/conf/hbase-site.xml <configuration> <property> <name>hbase.rootdir</name> <value>hdfs://master1:8020/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.tmp.dir</name> <value>/usr/local/hbase/var</value> </property> <property> <name>hbase.ZooKeeper.quorum</name> <value>master1</value> </property> </configuration>
-
配置集群的从节点:
hadoop@master1$ vi $HBASE_HOME/conf/regionservers slave1 slave2 slave3
-
将 HDFS 配置文件(hdfs-site.xml)链接到 HBase 的 Configuration 文件夹(Conf),以便 HBase 可以看到 Hadoop 群集上 HDFS 的客户端配置:
hadoop@master1$ ln -s $HADOOP_HOME/conf/hdfs-site.xml $HBASE_HOME/conf/hdfs-site.xml
-
从 Hadoop 和 ZooKeeper 安装复制
hadoop-core
和Zookeeper
JAR 文件及其依赖项:hadoop@master1$ rm -i $HBASE_HOME/lib/hadoop-core-*.jar hadoop@master1$ rm -i $HBASE_HOME/lib/ZooKeeper-*.jar hadoop@master1$ cp -i $HADOOP_HOME/hadoop-core-*.jar $HBASE_HOME/lib/ hadoop@master1$ cp -i $HADOOP_HOME/lib/commons-configuration-1.6.jar $HBASE_HOME/lib/ hadoop@master1$ cp -i $ZK_HOME/ZooKeeper-*.jar $HBASE_HOME/lib/
-
将
/usr/local/hbase
下的所有 HBase 文件从master
同步到与客户端和从节点相同的目录。 -
从主节点启动 HBase 集群:
```scala
hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
```
- Connect to your HBase cluster from the client node:
```scala
hadoop@client1$ $HBASE_HOME/bin/hbase shell
```
* 您也可以从浏览器访问 HBase Web 用户界面。 确保主服务器的`60010`端口已打开。 URL 为`http://master1:60010/master.jsp:`

- 从主节点停止 HBase 群集:
```scala
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh
```
它是如何工作的.
通过指定 hbase.rootdir
属性,我们的 HBase 集群被配置为使用 /hbase
作为其在 HDFS 上的根目录。 由于这是 HBase 第一次启动,它将自动创建目录。 您可以从以下客户端查看 HBase 在 HDFS 上创建的文件:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -ls /hbase
我们希望我们的 HBase 在分布式模式下运行,因此我们在 hbase-site.xml
中将 hbase.cluster.distributed
设置为 true
。
我们还通过在 hbase-env.sh
中指定 HBASE_MANAGES_ZK=false
将集群设置为使用独立的 ZooKeeper 集合。 ZooKeeper 集合由 hbase.ZooKeeper.quorum
属性指定。 您可以通过列出集群的所有服务器来使用集群 ZooKeeper,比如 zoo1,zoo2,zoo3
。
所有区域服务器都在 $HBASE_HOME/conf/regionservers
文件中配置。 您应该在每个区域服务器上使用一行。 在启动集群时,HBase 将通过 SSH 连接到这里配置的每个区域服务器,并在该服务器上启动 HRegionServer 守护进程。
通过链接 $HBASE_HOME/conf
目录下的 hdfs-site.xml
,HBase 将使用您在 hdfs-site.xml
中为 HDFS 所做的所有客户端配置,例如 dfs.replication
设置。
HBase 附带预置的 hadoop-core
和 ZooKeeper JAR 文件。 与您在 Hadoop 和 ZooKeeper 安装中使用的相比,它们可能已经过时了。 确保 HBase 与 Hadoop 和 ZooKeeper 使用相同版本的 .jar
文件,以避免出现任何意外问题。
基本 Hadoop/ZooKeeper/HBase 配置
在前进之前,我们应该调整一些基本设置。 这些是非常基本和重要的 Hadoop(HDFS)、ZooKeeper 和 HBase 设置,您应该在设置集群后立即考虑更改这些设置。
其中一些设置会因为数据持久性或集群可用性(必须配置)而生效,而另一些则是为了平稳运行 HBase 而推荐的配置。
配置设置取决于您的硬件、数据和群集大小。 我们将在这份食谱中描述一条指南。 您可能需要更改设置以适合您的环境。
每次进行更改时,都需要同步到所有客户端和从节点,然后重新启动相应的守护进程以应用更改。
怎么做……
应考虑更改的配置如下:
-
为 HDFS 打开
dfs.support.append
。dfs.support.append
属性确定 HDFS 是否应该支持追加(sync
)功能。 默认值为false
。 它必须设置为true
,否则如果区域服务器崩溃,您可能会丢失数据:hadoop$ vi $HADOOP_HOME/conf/hdfs-site.xml <property> <name>dfs.support.append</name> <value>true</value> </property>
-
增加
dfs.datanode.max.xcievers
值可使 DataNode 保持更多线程打开,以处理更多并发请求:hadoop$ vi $HADOOP_HOME/conf/hdfs-site.xml <property> <name>dfs.datanode.max.xcievers</name> <value>4096</value> </property>
-
增加 ZooKeeper 的堆内存大小,使其不会交换:
hadoop$ vi $ZK_HOME/conf/java.env export JAVA_OPTS="-Xms1000m -Xmx1000m"
-
增加 ZooKeeper 的最大客户端连接数以处理更多并发请求:
hadoop$ echo "maxClientCnxns=60" >> $ZK_HOME/conf/zoo.cfg
-
增加 HBase 的堆内存大小以平稳运行 HBase:
hadoop$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_HEAPSIZE=8000
-
减小
zookeeper.session.timeout
值,以便 HBase 可以快速找到崩溃的地域服务器,并在短时间内恢复:hadoop$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>zookeeper.session.timeout</name> <value>60000</value> </property>
-
要更改 Hadoop/zooKeeper/HBase 日志设置,请编辑 Hadoop/zooKeeper/HBase 安装目录
conf
下的log4j.properties
文件和hadoop-env.sh/hbase-env.sh
文件。 最好将日志目录从安装文件夹中更改出来。 例如,下面指定 HBase 在/usr/local/hbase/logs
目录下生成其日志:hadoop$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_LOG_DIR=/usr/local/hbase/logs
它是如何工作的.
在步骤 1 中,通过打开 dfs.support.append
,启用 HDFS 刷新。 启用此功能后,HDFS 的编写器可以通过调用 flush
调用来保证数据将被持久化。 因此,HBase 可以保证当某个区域服务器死机时,可以使用其预写日志(WAL)在其他区域服务器上恢复和重放数据。
要验证是否支持 HDFS 附加,请查看 HBase 启动的 HMaster 日志。 如果追加未转到 on
,您将发现如下所示的日志:
$ grep -i "HDFS-200" hbase-hadoop-master-master1.log
...syncFs -- HDFS-200 -- not available, dfs.support.append=false
对于步骤 2,我们配置了 dfs.datanode.max.xcievers
设置,该设置指定 HDFS DataNode 一次服务的文件数的上限。
备注
请注意,该名称是 xcievers—it's
一个拼写错误的名称。 它的默认值是 256
,这对于在 HDFS 上运行 HBase 来说太低了。
步骤 3 和 4 是关于动物园管理员设置的。 动物园饲养员对交换非常敏感,这将严重降低其性能。 ZooKeeper 的堆大小在 java.env
文件中设置。 动物园饲养员在任何时候服务的连接数量都有上限。 它的默认值是 10
,这对于 HBase 来说太低了,特别是在它上运行 MapReduce 时。 我们建议将其设置为 60
。
在第 5 步中,我们配置了 HBase 的堆内存大小。 HBase 附带的堆大小为 1 GB,对于现代机器来说太低了。 大型计算机的合理值为 8 GB 或更大,但小于 16 GB。
在步骤 6 中,我们将动物园管理员的会话超时更改为一个较低的值。 更低的超时时间意味着 HBase 可以更快地找到崩溃的地域服务器,从而在短时间内恢复其他服务器上的崩溃地域。 另一方面,在会话超时非常短的情况下,HRegionServer 守护进程可能会在群集负载过重时自杀,因为它可能无法在超时之前向 ZooKeeper 发送心跳。
另请参阅
设置多个高可用性(HA)主机
Hadoop 和 HBase 旨在自动处理其从节点的故障转移。 由于大型集群中可能有多个节点,因此服务器硬件故障或从节点关机在集群中被视为正常。
对于主节点,HBase 本身没有 SPOF。 HBase 使用动物园管理员作为其中央协调服务。 一个 ZooKeeper 集合通常由三个或更多服务器组成集群;只要集群中超过一半的服务器在线,ZooKeeper 就可以正常提供服务。
HBase 将其活动主节点、根区域服务器位置和其他重要运行数据保存在 ZooKeeper 中。 因此,我们只能在单独的服务器上启动两个或更多 HMaster
守护进程,最先启动的守护进程将是 HBase 集群的活动主服务器。
但是,HDFS 的 NameNode 是集群的 SPOF。 NameNode 将整个 HDFS 的文件系统映像保存在其本地内存中。 如果 NameNode 关闭,HDFS 将无法再工作,因为 HBase 也关闭了。 正如您可能注意到的,有一个 HDFS 的辅助 NameNode。 请注意,辅助 NameNode 不是 NameNode 的备用名称节点,它只是向 NameNode 提供检查点功能。 因此,高可用集群的挑战是使 NameNode 高度可用。
在本食谱中,我们将描述两个高度可用的主节点的设置,这两个节点将使用心跳相互监视。 心跳是一种广泛使用的 HA 解决方案,用于为 Linux 集群提供通信和成员资格。 检测信号需要与群集资源管理器(CRM)结合才能启动/停止该群集的服务。 起搏器是心跳信号的首选群集资源管理器。 我们将使用心跳和 Pacemaker 设置虚拟 IP(VIP)地址,然后将其与活动主节点相关联。 因为 EC2 不支持静态 IP 地址,所以我们无法在 EC2 上演示它,但我们将讨论使用弹性 IP(EIP)来实现我们的目的的替代方法。
我们将重点介绍如何设置 NameNode 和 HBase;您也可以简单地使用类似的方法来设置两个 JobTracker 节点。
做好准备
您应该已经安装了 HDFS 和 HBase。 我们将设置一个备用主节点(master2
),因为您需要另一台可供使用的服务器。 确保所有依赖项都已正确配置。 将您的 Hadoop 和 HBase 根目录从活动主机(master1
)同步到备用主机。
在这个食谱中我们也需要 NFS。 设置您的 NFS 服务器,并从 master1
和 master2
挂载相同的 NFS 目录。 确保 hadoop
用户拥有对 NFS 目录的写入权限。 在 NFS 上创建一个目录来存储 Hadoop 的元数据。 我们假设目录为 /mnt/nfs/hadoop/dfs/name
。
我们将为两个主机设置 VIP,并假设您有以下 IP 地址和 DNS 映射:
master1:
它的 IP 地址为 10.174.14.11。master2:
它的 IP 地址为 10.174.14.12。master:
它的 IP 地址为 10.174.14.10。 稍后将设置的是 VIP。
怎么做……
以下说明介绍了如何设置两个高度可用的主节点。
安装和配置心跳和起搏器
首先,我们将安装心跳和起搏器,并进行一些基本配置:
-
在
master1
和master2:
root# apt-get install heartbeat cluster-glue cluster-agents pacemaker
上安装心跳和起搏器
-
要配置心跳,请对
master1
和master2:
root# vi /etc/ha.d/ha.cf # enable pacemaker, without stonith crm yes # log where ? logfacility local0 # warning of soon be dead warntime 10 # declare a host (the other node) dead after: deadtime 20 # dead time on boot (could take some time until net is up) initdead 120 # time between heartbeats keepalive 2 # the nodes node master1 node master2 # heartbeats, over dedicated replication interface! ucast eth0 master1 # ignored by master1 (owner of ip) ucast eth0 master2 # ignored by master2 (owner of ip) # ping the name server to assure we are online ping ns
进行以下更改
-
创建一个
authkeys
文件。 以root
用户身份在master1
和master2:
root# ( echo -ne "auth 1\n1 sha1 "; \ dd if=/dev/urandom bs=512 count=1 | openssl md5 ) \ > /etc/ha.d/authkeys root# chmod 0600 /etc/ha.d/authkeys
上执行以下脚本
创建并安装 NameNode 资源代理
起搏器依赖资源代理来管理群集。 资源代理是管理群集资源的可执行文件。 在我们的示例中,VIP 地址和 HDFS NameNode 服务是我们希望使用 Pacemaker 管理的群集资源。 Pacemaker 附带 IPaddr
资源代理来管理 VIP,因此我们只需要创建自己的 namenode
资源代理:
-
Add environment variables to the
.bashrc
file of theroot
user onmaster1
andmaster2
. Don't forget to apply the changes:root# vi /root/.bashrc export JAVA_HOME=/usr/local/jdk1.6 export HADOOP_HOME=/usr/local/hadoop/current export OCF_ROOT=/usr/lib/ocf
调用以下命令以应用之前的更改:
root# source /root/.bashrc
-
Create a standard Open Clustering Framework (OCF) resource agent file called
namenode
, with the following content.namenode
资源代理首先包含如下标准 OCF 功能:root# vi namenode #!/bin/sh : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat} . ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs usage() { echo "Usage: $0 {start|stop|status|monitor|meta-data|validate-all}" }
-
添加一个
meta_data()
函数,如以下代码所示。 函数的作用是:将资源代理元数据转储到标准输出。 每个资源代理必须具有一组 XML 元数据,描述其自身用途和支持的参数:root# vi namenode meta_data() {cat <<END <?xml version="1.0"?> <!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> <resource-agent name="namenode"> <version>0.1</version> <longdesc lang="en"> This is a resource agent for NameNode. It manages HDFS namenode daemon. </longdesc> <shortdesc lang="en">Manage namenode daemon.</shortdesc> <parameters></parameters> <actions> <action name="start" timeout="120" /> <action name="stop" timeout="120" /> <action name="status" depth="0" timeout="120" interval="120" /> <action name="monitor" depth="0" timeout="120" interval="120" /> <action name="meta-data" timeout="10" /> <action name="validate-all" timeout="5" /> </actions> </resource-agent> END }
-
添加
namenode_start()
函数。 Pacemaker 使用此函数实际启动服务器上的 NameNode 守护进程。 在namenode_start()
函数中,我们首先检查 NameNode 是否已经在服务器上启动;如果没有启动,我们从hadoop
用户调用hadoop-daemon.sh
来启动它:root# vi namenode namenode_start() { # if namenode is already started on this server, bail out early namenode_status if [ $? -eq 0 ]; then ocf_log info "namenode is already running on this server, skip" return $OCF_SUCCESS fi # start namenode on this server ocf_log info "Starting namenode daemon..." su - hadoop -c "${HADOOP_HOME}/bin/hadoop-daemon.sh start name node" if [ $? -ne 0 ]; then ocf_log err "Can not start namenode daemon." return $OCF_ERR_GENERIC; fi sleep 1 return $OCF_SUCCESS }
-
添加
namenode_stop()
函数。 Pacemaker 使用此函数实际停止服务器上的 NameNode 守护进程。 在namenode_stop()
函数中,我们首先检查 NameNode 是否已经在服务器上停止;如果它正在运行,我们从hadoop
用户调用hadoop-daemon.sh
来停止它:root# vi namenode namenode_stop () { # if namenode is not started on this server, bail out early namenode_status if [ $? -ne 0 ]; then ocf_log info "namenode is not running on this server, skip" return $OCF_SUCCESS fi # stop namenode on this server ocf_log info "Stopping namenode daemon..." su - hadoop -c "${HADOOP_HOME}/bin/hadoop-daemon.sh stop name node" if [ $? -ne 0 ]; then ocf_log err "Can not stop namenode daemon." return $OCF_ERR_GENERIC; fi sleep 1 return $OCF_SUCCESS }
-
添加
namenode_status()
函数。 Pacemaker 使用此函数监视服务器上 NameNode 守护程序的状态。 在namenode_status()
函数中,我们使用jps
命令显示hadoop
用户拥有的所有正在运行的 Java 进程,以及 NameNode 守护进程的grep
名称以查看它是否已启动:root# vi namenode namenode_status () { ocf_log info "monitor namenode" su - hadoop -c "${JAVA_HOME}/bin/jps" | egrep -q "NameNode" rc=$? # grep will return true if namenode is running on this machine if [ $rc -eq 0 ]; then ocf_log info "Namenode is running" return $OCF_SUCCESS else ocf_log info "Namenode is not running" return $OCF_NOT_RUNNING fi }
-
添加
namenode_validateAll()
函数以确保在运行其他函数之前正确设置环境变量:root# vi namenode namenode_validateAll () { if [ -z "$JAVA_HOME" ]; then ocf_log err "JAVA_HOME not set." exit $OCF_ERR_INSTALLED fi if [ -z "$HADOOP_HOME" ]; then ocf_log err "HADOOP_HOME not set." exit $OCF_ERR_INSTALLED fi # Any subject is OK return $OCF_SUCCESS }
-
添加以下 Main 例程。 在这里,我们只需调用前面的函数来实现所需的标准 OCF 资源代理操作:
root# vi namenode # See how we were called. if [ $# -ne 1 ]; then usage exit $OCF_ERR_GENERIC fi namenode_validateAll case $1 in meta-data) meta_data exit $OCF_SUCCESS;; usage) usage exit $OCF_SUCCESS;; *);; esac case $1 in status|monitor) namenode_status;; start) namenode_start;; stop) namenode_stop;; validate-all);; *)usage exit $OCF_ERR_UNIMPLEMENTED;; esac exit $?
-
更改
namenode
文件权限,并在master1
和master2:
root# chmod 0755 namenode root# ocf-tester -v -n namenode-test /full/path/of/namenode
上进行测试
-
在继续下一步之前,请确保通过所有测试,否则 HA 群集将出现意外行为。
-
在
master1
和master2:
```scala
root# mkdir ${OCF_ROOT}/resource.d/hac
root# cp namenode ${OCF_ROOT}/resource.d/hac
root# chmod 0755 ${OCF_ROOT}/resource.d/hac/namenode
```
上的 `hac`提供程序下安装 `namenode`资源代理
配置高可用性 NameNode
我们已准备好使用心跳和起搏器配置高可用性 NameNode。 我们将设置一个 VIP 地址,并将 Hadoop 和 HBase 配置为使用该 VIP 地址作为它们的主节点。 NameNode 将在分配 VIP 的活动主机上启动。 如果活动主节点崩溃,心跳和 Pacemaker 将检测到它,并将 VIP 地址分配给备用主节点,然后在那里启动 NameNode。
-
在
master1
和master2:
root# /etc/init.d/heartbeat start
上启动心跳
-
更改默认
crm
配置。 所有与资源相关的命令只执行一次,从master1
或master2:
root# crm configure property stonith-enabled=false root# crm configure property default-resource-stickiness=1
开始
-
使用我们的 VIP 地址添加 VIP 资源:
root# crm configure primitive VIP ocf:heartbeat:IPaddr params ip="10.174.14.10" op monitor interval="10s"
-
进行以下更改以将 Hadoop 配置为使用我们的 VIP 地址。 进行更改后,同步到所有主机、客户端和从机:
hadoop$ vi $HADOOP_HOME/conf/core-site.xml <property> <name>fs.default.name</name> <value>hdfs://master:8020</value> </property>
-
进行以下更改以将 HBase 配置为使用我们的 VIP 地址。 进行更改后,同步到所有主服务器、客户端和从属服务器:
hadoop$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.rootdir</name> <value>hdfs://master:8020/hbase</value> </property>
-
要将 Hadoop 配置为将其元数据写入本地磁盘和 NFS,请进行以下更改并同步到所有主机、客户端和从机:
hadoop$ vi $HADOOP_HOME/conf/hdfs-site.xml <property> <name>dfs.name.dir</name> <value>/usr/local/hadoop/var/dfs/name,/mnt/nfs/hadoop /dfs/name</value> </property>
-
将我们在步骤 5 中创建的
namenode
资源代理添加到 Pacemaker。 我们将使用NAMENODE
作为其资源名称:root# crm configure primitive NAMENODE ocf:hac:namenode op monitor interval="120s" timeout="120s" op start timeout="120s" op stop timeout="120s" meta resource-stickiness="1"
-
将
VIP
资源和NAMENODE
资源配置为资源组:root# crm configure group VIP-AND-NAMENODE VIP NAMENODE
-
配置 VIP 资源的
colocation
和NAMENODE
资源:root# crm configure colocation VIP-WITH-NAMENODE inf: VIP NAMENODE
-
配置 VIP 资源和
NAMENODE
资源的资源顺序:
```scala
root# crm configure order IP-BEFORE-NAMENODE inf: VIP NAMENODE
```
- 使用
crm_mon
命令验证以前的心跳和资源配置。 如果一切配置正确,您应该会看到如下所示的输出:
```scala
root@master1 hac$ crm_mon -1r
============
Last updated: Tue Nov 22 22:39:11 2011
Stack: Heartbeat
Current DC: master2 (7fd92a93-e071-4fcb-993f-9a84e6c7846f) - partition with quorum
Version: 1.0.9-74392a28b7f31d7ddc86689598bd23114f58978b
2 Nodes configured, 1 expected votes
1 Resources configured.
============
Online: [ master1 master2 ]
Full list of resources:
Resource Group: VIP-AND-NAMENODE
VIP (ocf::heartbeat:IPaddr): Started master1
NAMENODE (ocf::hac:namenode): Started master1
```
- 确保在同一台服务器上启动
VIP
和NAMENODE
资源。 - 现在,从
master1; VIP-AND-NAMENODE
停止心跳应该在几秒钟后在master2
开始。 - 从
master1; VIP-AND-NAMENODE
重新启动心跳应保持在master2
启动。 资源不应回切到master1
。
启动 DataNode、HBase 集群和备份 HBase 主机
我们已经确认我们的 HA 配置工作正常,现在可以启动 HDFS 和 HBase 了。 请注意,Pacemaker 已经启动了 NameNode,因此我们只需在此处启动 DataNode:
-
如果一切正常,我们现在可以启动 DataNode:
hadoop@master$ for i in 1 2 3 do ssh slave$i "$HADOOP_HOME/bin/hadoop-daemon.sh start datanode" sleep 1 done
-
从
master
启动 HBase 集群,它是与 VIP 地址关联的活动主服务器:hadoop@master$ $HBASE_HOME/bin/start-hbase.sh
-
从备用主服务器启动备用 HMaster,在本例中为
master2
:hadoop@master2$ $HBASE_HOME/bin/hbase-daemon.sh start master
它是如何工作的.
前面的步骤最终给我们留下了一个如下图所示的集群结构:
首先,我们在两台主机上安装了心跳和起搏器,然后将心跳配置为启用起搏器。
在创建并安装 NameNode 资源代理部分的第 2 步中,我们创建了 namenode
脚本,该脚本作为标准 OCF 资源代理实现。 namenode
脚本最重要的功能是 namenode_status
,它监视 NameNode 守护进程的状态。 在这里,我们使用 jps
命令显示 hadoop
用户拥有的所有正在运行的 Java 进程,并显示 NameNode 守护进程的 grep
名称以查看它是否已启动。 Pacemaker 使用 namenode
资源代理来启动/停止/监视 NameNode 守护进程。 在 namenode
脚本中,正如您在 namenode_start
和 namenode_stop
方法中看到的那样,我们实际上使用 hadoop-daemon.sh
启动/停止 NameNode,它用于启动/停止单个服务器上的 Hadoop
守护进程。 您可以在本书附带的源代码中找到完整的代码列表。
我们在测试并安装了 namenode
资源代理后启动了心跳。 然后,我们对默认的 crm
配置进行了一些更改。 default-resource-stickiness=1
设置非常重要,因为它会将资源的 off
自动回切。
我们向 Pacemaker 添加了一个 VIP
资源,并在配置高可用性 NameNode部分的步骤 3 到 5 中配置了 Hadoop 和 HBase 以使用它。 通过在其配置中使用 VIP,Hadoop 和 HBase 可以在活动主机关闭时切换到与备用主机通信。
在同一节的第 6 步中,我们将 Hadoop(HDFS NameNode)配置为将其元数据写入本地磁盘和 NFS。 如果活动主机关闭,NameNode 将从备用主机启动。 因为它们挂载在相同的 NFS 目录上,所以从备用主机启动的 NameNode 可以应用来自 NFS 的最新元数据,并将 HDFS 恢复到原始活动主机关闭之前的状态。
在步骤 7 到 10 中,我们使用在创建并安装 NameNode 资源代理部分的步骤 2 中创建的 namenode
资源代理添加了 NAMENODE
资源,然后将 VIP
和 NAMENODE
资源设置为一个组(步骤 8),并确保它们始终运行在同一台服务器上(步骤 9),并且具有正确的启动顺序(步骤 10)。 我们这样做是因为我们不希望 VIP 在 master1
上运行,而 NameNode 在 master2
上运行。
因为 Pacemaker 将通过 namenode
资源代理为我们启动 NameNode,所以我们需要单独启动 DataNode,这是我们在启动 DataNode、HBase 集群和备份 HBase MASTER部分的步骤 1 中所做的。
在正常启动 HBase 之后,我们在备用主服务器上启动了备用 HBase 主服务器(HMaster)。 如果您检查您的 HBase 主日志,您将发现如下所示的输出,它本身显示为备用 HMaster:
2011-11-21 23:38:55,168 INFO org.apache.hadoop.hbase.master.ActiveMasterManager: Another master is the active master, ip-10-174-14-15.us-west-1.compute.internal:60000; waiting to become the next active master
最后,我们让 NameNode 和 HMaster 在具有活动-备用配置的两台服务器上运行。 避免了群集的单点故障。
然而,这给我们在生产上留下了很多工作要做。 您需要在所有罕见情况下测试 HA 集群,例如服务器断电、拔下网线、关闭网络交换机或您能想到的任何其他情况。
另一方面,集群的 SPOF 可能并不像您想象的那么关键。 根据我们的经验,几乎所有群集停机都是由于操作失误或软件升级造成的。 最好让集群变得简单。
还有更多...
在 Amazon EC2 上设置高可用性 HBase 集群更为复杂,因为 EC2 不支持静态 IP 地址,因此我们无法在 EC2 上使用 VIP。 另一种方法是使用弹性 IP 地址。 弹性 IP 地址是 EC2 上与您的帐户(而不是特定实例)关联的静态 IP 地址的角色。 如果活动主机关闭,我们可以使用心跳自动将弹性公网 IP 关联到备用主机。 然后,我们将 Hadoop 和 HBase 配置为使用与弹性公网 IP 关联的实例公有 DNS 来查找活动主机。 此外,在 namenode
资源代理中,我们不仅需要启动/停止 NameNode,还需要启动/停止所有 DataNode。 这是因为活动主机的 IP 地址已更改,但除非重新启动,否则 DataNode 无法找到新的活动主机。
我们将跳过细节,因为它超出了本书的范围。 我们创建了一个 elastic-ip
资源代理来实现此目的。 您可以在这本书附带的源代码中找到它。
二、数据迁移
在本章中,我们将介绍:
- 使用单个客户端从 MySQL 导入数据
- 使用批量加载工具从 TSV 文件导入数据
- 编写您自己的 MapReduce 作业以导入数据
- 在将数据移动到 HBase 之前预先创建区域
简介
有几种方法可以将数据移动到 HBase 中:
- 使用 HBase PUT API
- 使用 HBase 批量加载工具
- 使用自定义的 MapReduce 作业
HBasePutAPI 是最直接的方法。 它的用法不难学。 然而,在大多数情况下,这并不总是最有效的方法。 当需要在有限的时间内将大量数据传输到 HBase 中时尤其如此。 要处理的数据量通常很大,这可能就是您将使用 HBase 而不是另一个数据库的原因。 在开始 HBase 项目时,您必须仔细考虑如何将所有数据移到 HBase 中;否则,您可能会遇到严重的性能问题。
HBase 具有批量加载功能,支持高效地将海量数据加载到 HBase 中。 大容量加载功能使用 MapReduce 作业将数据加载到特定的 HBase 表中,方法是生成 HBase 的内部 HFile 数据格式文件,然后将数据文件直接加载到正在运行的集群中。 使用此批量加载功能的最简单方法是使用 importtsv
工具。 importtsv
是一个内置工具,用于将数据从 TSV 文件加载到 HBase 中。 它将运行 MapReduce 作业从 TSV 文件读取数据,然后将输出直接写入 HBase 表或 HBase 内部数据格式文件。
虽然如果您要将文本数据加载到 HBase 中, importtsv
工具可能非常有用,但在某些情况下,例如从其他格式导入数据,您可能希望以编程方式生成数据。 MapReduce 是处理海量数据的最有效方式。 这可能是将大量数据加载到 HBase 中的唯一实用方法。 当然,我们可以使用 MapReduce 将数据导入 HBase。 但是,MapReduce 的工作可能非常繁重,因为数据集很大。 如果处理不当,繁重的 MapReduce 作业可能会以较低的吞吐量运行。
通过 HBase 进行数据迁移是一项写入繁重的任务,除非我们生成内部数据文件并将其直接加载到 HBase 中。 尽管 HBase 写入总是很快,但如果没有正确配置,在迁移过程中写入经常会被阻止。 写入繁重任务的另一个问题是,所有写入可能都会进入同一区域服务器,在将大量数据加载到新的 HBase 安装中时尤其如此。 由于所有负载都转到同一台服务器,因此群集不平衡,写入速度将显著减慢。
我们将在本章中讨论这些问题。 我们将从使用其 PUT API 将数据从 MySQL 导入到 HBase 这一简单任务开始。 然后,我们将介绍如何使用 importtsv
和批量加载工具将 TSV 数据文件加载到 HBase 中。 我们还将提供一个 MapReduce 示例,用于从其他文件格式导入数据。 这包括将数据直接放入 HBase 表,并写入Hadoop 分布式文件系统(HDFS)上的 HFile 格式文件。 本章的最后一个诀窍解释了如何在将数据加载到 HBase 之前预先创建区域。
本章附带了几个用 Java 编写的示例源代码。 我们假设您具有基本的 Java 知识,因此我们将跳过在食谱中说明如何编译和打包示例 Java 源代码,但我们将在示例源代码中添加一个介绍。
通过单客户端从 MySQL 导入数据
最常见的数据迁移情况可能是将数据从现有的 RDBMS 导入 HBase。 对于这类任务,最简单、最直接的方法可能是从单个客户端获取数据,然后使用 HBase put API 将其放入 HBase。 如果要传输的数据不太多,它工作得很好。
本食谱介绍如何使用其 PUT API 将数据从 MySQL 导入 HBase。 所有操作都将在单个客户端上执行。 本食谱中不包括 MapReduce。 本食谱将引导您通过 HBase Shell 创建一个 HBase 表,从 Java 连接到集群,然后将数据放入 HBase。
做好准备
公共数据集是实践 HBase 数据迁移的理想数据源。 互联网上有很多公开的数据集。 本书中我们将使用美国国家海洋和大气局 1981-2010 年的气候常态公开数据。 您可以通过http://www1.ncdc.noaa.gov/pub/data/normals/1981-2010/访问它。
这是美国国家海洋和大气管理局(NOAA)生成的气候统计数据。
备注
在本食谱中,我们将在Products|Hourly下使用其每小时温度数据,您可以在上面的链接中找到该数据。 从目录下载 hly-temp-normal.txt
。
本食谱需要运行 MySQL 安装。 使用以下 SQL 语句在 MySQL 数据库中创建 hly_temp_normal
表:
create table hly_temp_normal (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
stnid CHAR(11),
month TINYINT,
day TINYINT,
value1 VARCHAR(5),
value2 VARCHAR(5),
value3 VARCHAR(5),
value4 VARCHAR(5),
value5 VARCHAR(5),
value6 VARCHAR(5),
value7 VARCHAR(5),
value8 VARCHAR(5),
value9 VARCHAR(5),
value10 VARCHAR(5),
value11 VARCHAR(5),
value12 VARCHAR(5),
value13 VARCHAR(5),
value14 VARCHAR(5),
value15 VARCHAR(5),
value16 VARCHAR(5),
value17 VARCHAR(5),
value18 VARCHAR(5),
value19 VARCHAR(5),
value20 VARCHAR(5),
value21 VARCHAR(5),
value22 VARCHAR(5),
value23 VARCHAR(5),
value24 VARCHAR(5)
);
这本书附带了一些脚本,可以帮助您将数据加载到 MySQL 表中。 您可以使用 insert_hly.py
加载每小时 NOAA 数据。 您需要在脚本中更改主机、用户、密码和数据库名称。 更改这些设置后,使用:将下载的 hly-temp-normal.txt
文件中的数据插入到 hly_temp_normal
表中
$ python insert_hly.py -f hly-temp-normal.txt -t hly_temp_normal
要编译下一节中将提到的 Java 源代码,您将需要以下库:
hadoop-core-1.0.2.jar
hbase-0.92.1.jar
mysql-connector-java-5.1.18.jar
您可以手动将它们添加到类路径中,也可以使用本书提供的示例源代码。
在开始导入数据之前,请确保您的 HDFS、ZooKeeper 和 HBase 集群运行正常。 登录到您的 HBase 客户端节点。
怎么做……
要将数据从 MySQL 导入 HBase 到单个客户端,请执行以下操作:
-
从您的 HBase 客户端服务器通过 HBase Shell 连接到您的 HBase 群集:
hadoop$ $HBASE_HOME/bin/hbase shell
-
在 HBase:
hbase> create 'hly_temp', {NAME => 'n', VERSIONS => 1}
中创建
hly_temp
表 -
Write a Java source to import data from MySQL into HBase. Package it as a JAR file. The following steps are to import data using Java source:
I.创建一个
connectHBase()
方法,从 Java:连接到特定的 HBase 表$ vi Recipe1.java private static HTable connectHBase(String tablename) \ throws IOException { HTable table = null; Configuration conf = HBaseConfiguration.create(); table = new HTable(conf, tablename); return table; }
二、。 创建一个
connectDB()
方法以从 Java 连接到 MySQL:$ vi Recipe1.java private static Connection connectDB() \ throws Exception { String userName = "db_user"; String password = "db_password"; String url = "jdbc:mysql://db_host/database"; Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection conn = DriverManager.getConnection(url, userName, password); return conn; }
下面是 Java 类的 main()方法。 在此方法中,我们从 MySQL 获取数据并将该数据放入 HBase:
$ vi Recipe1.java public class Recipe1 { public static void main(String[] args) { Connection dbConn = null; HTable htable = null; Statement stmt = null; String query = "select * from hly_temp_normal"; try { dbConn = connectDB(); htable = connectHBase("hly_temp"); byte[] family = Bytes.toBytes("n"); stmt = dbConn.createStatement(); ResultSet rs = stmt.executeQuery(query); // time stamp for all inserted rows long ts = System.currentTimeMillis(); while (rs.next()) { String stationid = rs.getString("stnid"); int month = rs.getInt("month"); int day = rs.getInt("day"); String rowkey = stationid + Common.lpad(String. valueOf(month), 2, '0') + Common.lpad(String.valueOf(day), 2, '0'); Put p = new Put(Bytes.toBytes(rowkey)); // get hourly data from MySQL and put into hbase for (int i = 5; i < 29; i++) { String columnI = "v" + Common.lpad (String.valueOf(i - 4), 2, '0'); String valueI = rs.getString(i); p.add(family, Bytes.toBytes(columnI), ts, Bytes.toBytes(valueI)); } htable.put(p); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (stmt != null) { stmt.close(); } if (dbConn != null) { dbConn.close(); } if (htable != null) { htable.close(); } } catch (Exception e) { // ignore } } } }
-
运行导入作业。 运行 JAR 文件的脚本如下所示:
#/bin/bash bin=`dirname $0` bin=`cd $bin;pwd` cp=$HBASE_HOME/conf:$HBASE_HOME/hbase-0.92.1.jar:$bin/build/hac-chapter2.jar for jar in $bin/lib/*.jar do cp=$cp:$jar done for jar in $HBASE_HOME/lib/*.jar do cp=$cp:$jar done $JAVA_HOME/bin/java -classpath $cp "hac.chapter2.Recipe1"
-
要验证导入到 HBase 中的数据,请通过 HBase Shell 连接到正在运行的 HBase 实例:
hadoop$ $HBASE_HOME/bin/hbase shell
-
验证数据是否已导入 HBase 中的目标表:
hbase> count 'hly_temp' 95630 row(s) in 8.9850 seconds hbase> scan 'hly_temp', {LIMIT => 10} ... AQW000617050110 column=n:v23, timestamp=1322958813521, value=814S AQW000617050110 column=n:v24, timestamp=1322958813521, value=811C 10 row(s) in 0.6730 seconds
它是如何工作的.
在步骤 1 和 2 中,我们在 HBase 中创建了要向其中插入数据的目标表。 表名为 hly_temp
,它只有一个列族 n
。 我们将其命名为一个字符长度的原因是列族名称将存储在 HBase 中的每个键/值中。 使用非常短的名称可以高效地存储和缓存数据。 我们只需要数据的一个版本,它由列族的 VERSION
属性指定。
在 Java 源代码中,要连接到 HBase,我们首先创建一个 Configuration
对象,然后将其与表名一起使用来创建一个 HTable
实例。 HTable
对象将处理所有客户端 API 调用。 如您所见,我们没有在源代码中设置任何 ZooKeeper 或 HBase 连接配置。 那么,它如何找到正确运行的 HBase 集群呢? 这是可能的,因为我们在步骤 4 中将 $HBase/conf
目录添加到类路径中。通过执行此操作,HBase 客户端 API 将从类路径中的文件 hbase-site.xml
加载配置。 连接设置在 hbase-site.xml
文件中指定。
在使用 JDBC 从 MySQL 获取数据之后,我们循环遍历结果集,并将 MySQL 中的一行映射到 HBase 表中的一行。 这里,我们使用 stationid, month
和 day
组成 HBase 数据的行键。 我们还向月和日数据添加了左填充 0
。 这一点很重要,因为 HBase 行键按字典序排序,这意味着 12 排在 2 之前;这当然不是我们预期的结果。
我们使用行键为一行创建了一个 Put
对象。 每小时数据是通过调用 Put.add()
来设置的,它以列族、限定符、时间戳和值作为参数。 同样,我们在这里采用了非常短的列名,以提高存储数据的效率。 设置完所有数据后,调用 HTable.put()
会将数据放入表中。
最后,需要手动关闭所有打开的资源。 我们关闭了源代码最后一块中的 MySQL 和 HBase 连接,以确保即使在导入过程中发生异常也会调用它。
您可以通过对比 MySQL 和 HBase 表的行数来验证导入。 正如您在扫描结果中看到的那样,数据被准确地导入到 HBase 中。
使用批量加载工具从 TSV 文件导入数据
HBase 有一个 importtsv
工具,支持将数据从 TSV 文件导入 HBase。 使用该工具将文本数据加载到 HBase 非常高效,因为它运行 MapReduce 作业来执行导入。 即使您要从现有的 RDBMS 加载数据,也可以以某种方式将数据转储到文本文件,然后使用 importtsv
将转储的数据导入 HBase。 这种方法在导入大量数据时非常有效,因为转储数据比在 RDBMS 上执行 SQL 快得多。
importtsv
工具不仅将数据直接加载到 HBase 表中,还支持生成 HBase 内部格式(HFile)文件,因此您可以使用 HBase 批量加载工具将生成的文件直接加载到正在运行的 HBase 集群中。 这样,您就可以在迁移过程中减少数据传输产生的网络流量和 HBase 负载。
本食谱介绍了 importtsv
和批量加载工具的用法。 我们首先演示如何使用 importtsv
工具将数据从 TSV 文件加载到 HBase 表中。 我们还将介绍如何生成 HBase 内部格式文件,以及如何将生成的文件直接加载到 HBase 中。
做好准备
我们将在本食谱中使用 NOAA 气候正常数据。 数据可从以下链接获得:
http://www1.ncdc.noaa.gov/pub/data/normals/1981-2010/
备注
下载 hly-temp-10pctl.txt
文件,该文件位于Products|每小时目录下。
下载的数据不能直接从 importtsv
工具加载,因为其格式不受支持。 我们提供脚本来帮助您将数据转换为 TSV 文件。 除了实际数据之外,要加载的 TSV 文件必须包含一个表示 HBase 表行的行键的字段。 本书附带的 to_tsv_hly.py
脚本从每小时 NOAA 数据文件中读取数据,并生成行键并将数据输出到本地文件系统上的 TSV 文件:
$ python to_tsv_hly.py -f hly-temp-10pctl.txt -t hly-temp-10pctl.tsv
当 importtsv
工具运行 MapReduce 作业来执行导入时,我们需要在集群上运行 MapReduce。 通过从主节点执行以下命令启动 MapReduce 守护进程:
hadoop$ $HADOOP_HOME/bin/start-mapred.sh
我们将在客户端服务器上添加一个 hac
用户来运行该作业;这对于生产来说是可取的。 要从客户端运行 MapReduce 作业,您需要向客户端上的 hac
用户授予对 ${hadoop.tmp.dir}
目录的写入权限。 我们假设将 ${hadoop.tmp.dir}
目录设置为 /usr/local/hadoop/var:
root@client1# usermod -a -G hadoop hac
root@client1# chmod -R 775 /usr/local/hadoop/var
在 HDFS 上,为 hac
用户创建主目录:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hac
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -chown hac /user/hac
还要确保 hac
用户对 HDFS 上的 MapReduce 临时目录具有写入权限:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -chmod -R 775 /usr/local/hadoop/var/mapred
怎么做……
要使用 MapReduce 将数据从 TSV 文件加载到 HBase 表中,请执行以下步骤:
-
在 HDFS 上创建一个目录,并将 TSV 文件从本地文件系统复制到 HDFS:
hac@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hac/input/2-1 hac@client1$ $HADOOP_HOME/bin/hadoop fs -copyFromLocal hly-temp-10pctl.tsv /user/hac/input/2-1
-
在 HBase 中添加目标表。 连接到 HBase 并将
hly_temp
表添加到其中:hac@client1$ $HBASE_HOME/bin/hbase shell hbase> create 'hly_temp', {NAME => 't', VERSIONS => 1}
-
如果该表存在(我们在本章的第一个配方中创建了它),请向其中添加一个新的列族:
hbase> disable 'hly_temp' hbase> alter 'hly_temp', {NAME => 't', VERSIONS => 1} hbase> enable 'hly_temp'
-
将
hbase-site.xml
文件添加到 Hadoop 类路径,方法是将其链接到 Hadoop 配置目录下:hac@client1$ ln -s $HBASE_HOME/conf/hbase-site.xml $HADOOP_HOME/conf/hbase-site.xml
-
通过编辑客户端服务器
$HADOOP_HOME/conf
目录下的hadoop-env.sh
文件将 HBase 依赖 JAR 添加到 Hadoop 类路径:hadoop@client1$ vi $HADOOP_HOME/conf/hadoop-env.sh export HADOOP_CLASSPATH=/usr/local/zookeeper/current/zookeeper-3.4.3.jar:/usr/local/hbase/current/lib/guava-r09.jar
-
通过由
hac
用户运行以下脚本来运行importtsv
工具:hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar importtsv \ -Dimporttsv.columns=HBASE_ROW_KEY,t:v01,t:v02,t:v03,t:v04,t:v05,t:v06,t:v07,t:v08,t:v09,t:v10,t:v11,t:v12,t:v13,t:v14,t:v15,t:v16,t:v17,t:v18,t:v19,t:v20,t:v21,t:v22,t:v23,t:v24 \ hly_temp \ /user/hac/input/2-1
-
Check the job status via the MapReduce admin page—
http://master1:50030/jobtracker.jsp:
-
验证在 HBase 的目标表中导入的数据。 在这里,我们获得
hly_temp
表中的行数,还扫描表中的一些示例数据。 行数应等于 TSV 文件中的行数。 表中的行键应等于文件中的第一个字段。 每行将有t:v01, t:v02
、...、t:v24
个单元格,每个单元格的值应等于 TSV 文件中的相对字段:hbase> count 'hly_temp' 95630 row(s) in 12.2020 seconds hbase> scan 'hly_temp', {COLUMNS => 't:', LIMIT => 10} AQW000617050110 column=t:v23, timestamp=1322959962261, value=781S AQW000617050110 column=t:v24, timestamp=1322959962261, value=774C 10 row(s) in 0.1850 seconds
它是如何工作的.
importtsv
工具将只从 HDFS 读取数据,因此我们首先使用 hadoop fs -copyFromLocal
命令将 TSV 文件从本地文件系统复制到 HDFS。
在步骤 2 中,我们在 HBase 中创建目标表(hly_temp
)和列族(t
)。 如果该表已经存在,我们可以将其更改为仅向其中添加我们的列族。 所有数据都将载入到新添加的柱族中;现有柱族中的数据不会受到影响。
为了运行 MapReduce 作业,需要通过 hadoop jar
命令执行包含类文件的 JAR 文件。 为了将 HBase 配置传递给该命令,我们将 hbase-site.xml
链接到 $HADOOP_HOME/conf
目录下;该目录下的所有文件都将被添加到由 hadoop
命令启动的 Java 进程的类路径中。
在步骤 5 中,通过在 hadoop-env.sh
中设置 HADOOP_CLASSPATH
来添加运行时依赖项。 除了 ZooKeeper 库之外, importtsv
工具还需要 guava-r09.jar
文件。 它是一个解析 TSV 文件的库。
importtsv
工具本身是一个包含在 HBase JAR 文件中的 Java 类。 在步骤 6 中,我们通过执行 hadoop jar
命令来运行该工具。 此命令将为我们启动 Java 进程,并向其添加所有依赖项。 要运行的 JAR 由 hadoop jar
命令的第一个参数指定;这里是 hbase-0.92.1.jar
。
以下参数传递给 hbase-0.92.1.jar:
的主类
- TSV 文件的字段索引到 HBase 表列的映射信息由
-Dimporttsv.columns
参数设置。 在我们的示例中,TSV 文件格式为(rowkey, value1, value2, ..., value24)
。 我们将数据放入 HBaset
列族中,使用v01
作为value1, v02
作为value2
,依此类推。HBASE_ROW_KEY
是指定行键字段的常量字。 - 在
-Dimporttsv.columns
参数之后,我们指定了表名(hly_temp
),并输入了该命令的 TSV 文件路径(/user/hac/input/2-1
)。
还可以指定其他几个选项。 不带参数运行 importtsv
会打印简短的用法信息:
hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar importtsv
Usage: importtsv -Dimporttsv.columns=a,b,c <tablename> <inputdir>
Imports the given input directory of TSV data into the specified table.
...
可以使用 -D
指定的其他选项包括:
-Dimporttsv.skip.bad.lines=false - fail if encountering an invalid line
'-Dimporttsv.separator=|' - eg separate on pipes instead of tabs
-Dimporttsv.timestamp=currentTimeAsLong - use the specified timestamp for the import
-Dimporttsv.mapper.class=my.Mapper - A user-defined Mapper to use instead of org.apache.hadoop.hbase.mapreduce.TsvImporterMapper
该工具为我们启动 MapReduce 作业。 在作业的映射阶段,它读取并解析指定输入目录下的 TSV 文件中的行,并使用列映射信息将行放入 HBase 表中。 Read
和 Put
操作在多个服务器上并行执行,因此比从单个客户端加载数据要快得多。 默认情况下,作业中没有 Reduce 阶段。 我们可以在 MapReduce 的 Admin 页面上查看作业进度、计数器和其他 MapReduce 信息。
要查看插入到表中的数据,我们可以使用 HBase Shell 的 scan
命令。 我们指定 COLUMNS => 't:'
只扫描表中的 t
列族。
还有更多...
默认情况下, importtsv
工具使用 HBase put API 在其映射阶段使用 TableOutputFormat
将数据插入到 HBase 表中。 但是当指定 -Dimporttsv.bulk.output
选项时,它会使用 HFileOutputFormat
在 HDFS 上生成 HBase 内部格式(HFile)文件。 因此,我们可以使用 completebulkload
工具将生成的文件加载到正在运行的集群中。 以下步骤用于使用批量输出和加载工具:
-
在 HDFS 上创建一个目录以将生成的文件放入:
hac@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hac/output
-
使用批量输出选项运行
importtsv
:hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar importtsv \ -Dimporttsv.bulk.output=/user/hac/output/2-1 \ -Dimporttsv.columns=HBASE_ROW_KEY,t:v01,t:v02,t:v03,t:v04, t:v05,t:v06,t:v07,t:v08,t:v09,t:v10,t:v11,t:v12,t:v13,t:v14, t:v15,t:v16,t:v17,t:v18,t:v19,t:v20,t:v21,t:v22,t:v23,t:v24 \ hly_temp \ /user/hac/input/2-1
-
完成批量加载:
hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar completebulkload \ /user/hac/output/2-1 \ hly_temp
completebulkload
工具查看生成的文件,确定它们所属的区域,然后联系相应的区域服务器。 地域服务器将采用的 HFile 移动到其存储目录中,并为客户端在线创建数据。
编写您自己的 MapReduce 作业以导入数据
虽然 importtsv
工具对于将文本文件加载到 HBase 非常有用,但在许多情况下,为了完全控制加载过程,您可能需要编写自己的 MapReduce 作业来将数据导入 HBase。 例如,如果要加载其他格式的文件,则 importtsv
工具不起作用。
HBase 提供 TableOutputFormat
用于将数据从 MapReduce 作业写入 HBase 表。 您还可以通过使用 HFileOutputFormat
类在 MapReduce 作业中生成其内部 HFile 格式的文件,然后使用我们在前面的菜谱中描述的 completebulkload
工具将生成的文件加载到正在运行的 HBase 集群中。
在本食谱中,我们将解释使用您自己的 MapReduce 作业加载数据的步骤。 我们将首先描述如何使用 TableOutputFormat
。 在中还有更多..。 。 一节中,我们将解释如何在 MapReduce 作业中生成 HFile 格式文件。
做好准备
在本食谱中,我们将使用原始的 NOAA hly-temp-normal.txt
文件。 您不需要对下载的数据文件进行任何格式化。 我们将直接从 MapReduce 加载原始数据。
我们假设您的环境已经为在 HBase 上运行 MapReduce 做好了准备。 如果仍然没有,您可以参考使用批量加载工具从 TSV 文件导入的食谱以了解详细信息。
怎么做……
按照以下说明使用您自己的 MapReduce 作业将数据加载到 HBase 中:
-
将原始数据文件从本地文件系统复制到 HDFS:
hac@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hac/input/2-3 hac@client1$ $HADOOP_HOME/bin/hadoop fs -copyFromLocal hly-temp-normal.tsv /user/hac/input/2-3
-
在客户端服务器上编辑
hadoop-env.sh
,并将 HBase JAR 文件添加到 Hadoop 类路径:hadoop@client1$ vi $HADOOP_HOME/conf/hadoop-env.sh export HADOOP_CLASSPATH=/usr/local/hbase/current/hbase-0.92.1.jar
-
编写 MapReduce Java 源代码,然后将其打包为 JAR 文件。 Java 源代码应该如下所示:
$ vi Recipe3.java public class Recipe3 { public static Job createSubmittableJob (Configuration conf, String[] args) throws IOException { String tableName = args[0]; Path inputDir = new Path(args[1]); Job job = new Job (conf, "hac_chapter2_recipe3"); job.setJarByClass(HourlyImporter.class); FileInputFormat.setInputPaths(job, inputDir); job.setInputFormatClass(TextInputFormat.class); job.setMapperClass(HourlyImporter.class); // ++++ insert into table directly using TableOutputFormat ++++ TableMapReduceUtil.initTableReducerJob(tableName, null, job); job.setNumReduceTasks(0); TableMapReduceUtil.addDependencyJars(job); return job; } public static void main(String[] args) throws Exception { Configuration conf = HBaseConfiguration.create(); Job job = createSubmittableJob(conf, args); System.exit (job.waitForCompletion(true) ? 0 : 1); } }
-
在
Recipe3.java
中添加一个内部类(HourlyImporter
)。 此类是 MapReduce 作业的映射器类:$ vi Recipe3.java static class HourlyImporter extends Mapper<LongWritable, Text, ImmutableBytesWritable, Put> { private long ts; static byte[] family = Bytes.toBytes("n"); @Override protected void setup(Context context) { ts = System.currentTimeMillis(); } @Override public void map(LongWritable offset, Text value, Context context)throws IOException { try { String line = value.toString(); String stationID = line.substring(0, 11); String month = line.substring(12, 14); String day = line.substring(15, 17); String rowkey = stationID + month + day; byte[] bRowKey = Bytes.toBytes(rowkey); ImmutableBytesWritable rowKey = new ImmutableBytesWritable(bRowKey); ImmutableBytesWritable rowKey = new ImmutableBytesWritable(bRowKey); Put p = new Put(bRowKey); for (int i = 1; i < 25 ; i++) { String columnI = "v" + Common.lpad(String.valueOf(i), 2, '0'); int beginIndex = i * 7 + 11; String valueI = line.substring(beginIndex, beginIndex + 6).trim(); p.add(family, Bytes.toBytes(columnI), ts, Bytes.toBytes(valueI)); } context.write(rowKey, p); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
In order to run the MapReduce job, package the Java source into a JAR file, and run it from the client by using the
hadoop jar
command:hac@client1$ $HADOOP_HOME/bin/hadoop jar hac-chapter2.jar hac.chapter2.Recipe3 \ hly_temp \ /user/hac/input/2-3
- 检查一下结果。 MapReduce 作业的输出应如以下屏幕截图所示:
提示
映射输入记录应等于输入路径下的文件总行;映射输出记录值应等于输入记录计数。 您还可以使用 HBase
count/scan
命令检查结果。
它是如何工作的.
要运行 MapReduce 作业,我们首先在 createSubmittableJob()
中创建一个 Job
实例。 创建实例后,我们为作业设置输入路径、输入格式和映射器类。 之后,我们调用 TableMapReduceUtil.initTableReducerJob()
为我们适当地设置作业。 设置包括添加 HBase 配置、设置 TableOutputFormat
以及向作业添加依赖项。 TableMapReduceUtil
是在 HBase 上编写 MapReduce 程序的有用实用程序类。
main
方法中的 job.waitForCompletion()
调用将作业提交给 MapReduce 框架,并等待作业完成后再退出。 正在运行的作业将读取输入路径下的所有文件,并逐行将数据传递给指定的映射器类(HourlyImporter
)。
在类的 map
方法中,我们解析该行,组成行键,创建 Put
对象,然后通过调用 Put.add()
将解析的数据添加到相应的列。 最后,通过调用 context.write()
将数据写入 HBase 表。 在这种情况下不需要还原阶段。
如您所见,编写定制的 MapReduce 作业将数据插入 HBase 非常简单。 该程序类似于从单个客户端使用 HBase API 的程序,我们在使用单个客户端从 MySQL 导入数据食谱中描述了这一点。 对于数据量较大的情况,我们建议您使用 MapReduce 将数据加载到 HBase 中。
还有更多...
在许多情况下,使用定制的 MapReduce 作业将数据加载到 HBase 中是有意义的。 但是,如果您的数据量很大,则此方法可能效率不够高。 有一些方法可以提高数据迁移的效率。
在 MapReduce 中生成 HFile 文件
我们也可以在 MapReduce 作业中生成内部 HFile 格式文件,然后使用我们在第二个菜谱中描述的 completebulkload
工具将它们加载到集群中,而不是直接将数据写入 HBase 表。 与简单使用 TableOutputFormat
API:相比,此方法使用的 CPU 和网络资源更少
-
更改作业配置。 要生成 HFile 文件,请找到
createSubmittableJob()
方法的以下两行:TableMapReduceUtil.initTableReducerJob (tableName, null, job); job.setNumReduceTasks(0);
-
用以下代码替换它们:
HTable table = new HTable(conf, tableName); job.setReducerClass(PutSortReducer.class); Path outputDir = new Path(args[2]); FileOutputFormat.setOutputPath (job, outputDir); job.setMapOutputKeyClass (ImmutableBytesWritable.class); job.setMapOutputValueClass(Put.class); HFileOutputFormat.configureIncrementalLoad (job, table);
-
将输出路径添加到命令行参数。 编译并打包源代码,然后将输出路径添加到运行作业的命令中:
hac@client1$ $HADOOP_HOME/bin/hadoop jar hac-chapter2.jar hac.chapter2.Recipe3 \ hly_temp \ /user/hac/input/2-3 \ /user/hac/output/2-3
-
完成批量加载:
hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar completebulkload \ /user/hac/output/2-3 \ hly_temp
在步骤 1 中,我们更改了作业配置的源。 我们将作业设置为使用 HBase 提供的 PutSortReducer
Reducer 类。 该类将对一行中的列进行排序,然后再将其写出。 HFileOutputFormat.configureIncrementalLoad()
方法将负责为作业设置适当的配置以生成 HFile 文件。
在步骤 2 中作业运行完成后,将在我们指定的输出路径下生成内部 HFile 格式文件。 2-3/n
(列族目录)下的文件将由 completebulkload
工具加载到我们的 HBase 集群中。
在 MapReduce 作业执行期间,如果您从浏览器打开 HBase 管理页面,您会发现没有请求到达 HBase。 这表明数据没有直接写入 HBase 表。
影响数据迁移的重要配置
如果您使用 MapReduce 作业中的 TableOutputFormat
将数据直接写入 HBase 表,那么在 HBase 上这可能是非常繁重的写操作。 尽管 HBase 设计为能够快速处理写入,但您可能需要调整一些重要的配置,例如:
- JVM 堆和 GC 设置
- 区域服务器处理程序计数
- 最大区域文件大小
- 给你的 MemStore
- 更新块设置
您需要具备 HBase 体系结构的基本知识,才能了解这些配置如何影响 HBase 的写入性能。 我们将在第 8 章、基本性能调整和第 9 章、高级配置和调整中详细介绍这些内容。
Hadoop 和 HBase 集群生成了几种类型的日志。 检查日志可以提示您在 MapReduce 数据加载作业期间找出集群的瓶颈。 重要日志包括:
- Hadoop/HBase/zooKeeper 守护进程的 GC 日志
- HMaster 后台进程日志
我们将在章,基本性能调整中描述详细信息。
另请参阅
在将数据移入 HBase 之前预先创建区域
每个 HBase 行都属于一个特定的区域。 一个区域保存一系列已排序的 HBase 行。 区域由区域服务器部署和管理。
当我们在 HBase 中创建表时,该表从单个区域开始。 插入到表中的所有数据首先都会进入单个区域。 数据不断被插入,当达到阈值时,该区域将被一分为二。 这称为区域分割。 拆分后的地域将分布到其他地域服务器上,实现集群之间的负载均衡。
您可以想象,如果我们可以使用预先创建的区域初始化表,使用适当的算法,数据迁移的负载将在整个集群上实现均衡,这将显著提高数据加载速度。
在本食谱中,我们将介绍如何创建包含预先创建的区域的表。
做好准备
登录到您的 HBase 客户端节点。
怎么做……
在客户端节点上执行以下命令:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.RegionSplitter -c 10 -f n hly_temp2
12/04/06 23:16:32 DEBUG util.RegionSplitter: Creating table hly_temp2 with 1 column families. Presplitting to 10 regions
...
12/04/06 23:16:44 DEBUG util.RegionSplitter: Table created! Waiting for regions to show online in META...
12/04/06 23:16:44 DEBUG util.RegionSplitter: Finished creating table with 10 regions
它是如何工作的.
此命令使用以下参数调用 RegionSplitter
类:
-c 10—creates
有 10 个预裂区域的桌子-f n—creates
名为n
的单柱族hly_temp2—
;表的名称
从浏览器打开 HBase 管理页面,然后单击用户表部分中的HLY_TEMP2。 您会发现已预先创建了 10 个区域:
RegionSplitter
是 HBase 提供的实用程序类。 通过使用 RegionSplitter
,您可以:
- 创建具有指定数量的预创建区域的表
- 对现有表上的所有区域执行滚动拆分
- 使用自定义算法分割区域
如果您运行编写自己的 MapReduce 作业中讨论的数据加载作业以导入数据配方,您可能希望将数据写入分发到集群中的所有区域服务器,但结果不同。 在管理页面中,您会发现在 MapReduce 作业执行期间,所有请求都转到同一服务器。
这是因为默认拆分算法(MD5StringSplit
)不适合我们的情况。 我们的所有行都放入同一区域,因此所有 API 请求都转到保存该区域的区域服务器。 我们需要提供我们的自定义算法来正确分割区域。
预分割区域还会更改生成内部 HFile 格式文件的 MapReduce 作业的行为。 运行Writing Your Your MapReduce 作业中的作业以导入数据配方,并在 hly_temp2
表中打开用于生成 HFile 文件的选项。 如下图所示,您会发现 MapReduce 作业的Reduce计数从原来的 1 跳到了 10,这是预先创建的区域数:
这是因为作业的减少器计数基于目标表的区域计数。 如果减少器计数增加,通常意味着负载被分配到多个服务器,因此作业将会快得多。
另请参阅
- 在本第 9 章中,使用您自己的算法配方预先创建区域
三、使用管理工具
在本章中,我们将重点介绍:
- HBase Master Web UI
- 使用 HBase Shell 管理表格
- 使用 HBase Shell 访问 HBase 中的数据
- 使用 HBase Shell 管理群集
- 从 HBase Shell 执行 Java 方法
- 行计数器
- WAL 工具-手动拆分和转储 WAL
- HFile 工具-查看带纹理的 HFile 内容
- HBase
hbck—checking
HBase 群集的运行状况 - HBase 上的配置单元-使用类似 SQL 的语言查询 HBase
简介
每个人都希望他们的 HBase 管理员保持集群平稳运行,在其中存储大量数据,同时、快速、可靠地处理数百万个请求。 保持 HBase 中的大量数据是可访问、可管理和易于查询的,这是管理员的一项重要任务。
除了扎实地了解您正在操作的集群之外,您使用的工具也同样重要。 HBase 附带了几个管理工具,可以让生活变得更轻松。 有一个基于 Web 的管理页面;在该页面上,您可以查看群集的状态并执行简单的管理任务,如区域拆分。 然而,比 HBase web UI 更强大的是 HBase Shell 工具。 该命令行工具具有创建和管理 HBase 表、在表中插入和查看数据的功能,还具有管理集群本身的方法。
HBase 还在其安装过程中提供了大量 Java 实用程序。 您可以直接从 HBase Shell 提示符导入和使用这些实用程序,这将使管理任务更加高效。
我们也有 HBase 工具来处理其内部的预写日志(WAL)和 HFile 文件。 这些工具对高级用户非常有用,他们需要更深入地研究数据是如何存储在 HBase 中的。
HBase hbck
是可用于检查集群运行状况的工具。 在生产中,您可能希望频繁运行 hbck
,以便快速发现问题。
HIVE 是为 Hadoop 构建的数据仓库软件。 配置单元有一个配置单元查询语言(HQL)来管理 Hadoop 中的数据。 HQL 是一种功能强大、易于使用、类似 SQL 的查询语言。 尽管 HBase 有自己的命令来访问其数据,但其功能非常有限。 我们可以将 HBase 表和列映射到配置单元表,这样就可以使用 HQL 对 HBase 数据执行复杂的查询。
在本章中,我们将介绍这些工具的用法。 我们将解释这些工具的用途,以及如何使用它们来解决特定任务。
HBase Master Web UI
HBase Master web UI 是一个简单但有用的工具,可用于了解集群的当前状态。 从其页面中,您可以获得正在运行的 HBase 的版本、基本配置(包括根 HDFS 路径和 ZooKeeper 仲裁)、集群的平均负载以及表、区域和区域服务器列表。
此外,还可以使用特定边界行键手动拆分区域。 这在禁用簇的自动区域分割时非常有用。
做好准备
确保从网络防火墙向客户端计算机开放母版页的端口(默认值为 60010)。 如果您在 Amazon EC2 上运行群集,则可以从AWS 管理控制台|Amazon EC2|网络&安全|安全组|入站打开端口。
怎么做……
从 Web 浏览器访问以下 URL:
http://hbase_master_server:60010/master.jsp
提示
您需要将 hbase_master_server
更改为 HBase 主服务器的主机名。
它是如何工作的.
HBase Master Web 用户界面如下所示:
如您所见,Attributes部分显示了有关 HBase 和 Hadoop 版本、HDFS 上的 HBase 根目录、集群的平均负载和 ZooKeeper 仲裁的信息。 HBase 根目录和 ZooKeeper 仲裁数据是在您的 HBase 配置文件 hbase-site.xml
中设置的值。 平均负载是每个区域服务器的平均区域数。 单个区域服务器的负载显示在区域服务器部分中。
在目录表部分中显示了两个表格:-ROOT-和.META。 这些是 HBase 的系统表。 -root-表保存区域服务器的引用,其中所有的.META。 表被部署,而.META。 表包含所有用户表区域的引用。
用户表部分显示所有用户表及其列族属性的列表。 单击列表中表格名称的链接将带您进入表格的详细信息页面。 您将看到该表的区域列表,显示在此页面上。 您还可以在表的详细信息页面上手动压缩或拆分区域。 区域键对于区域的压缩和拆分是可选的。 如果指定,HBase 将只对密钥插入的区域执行操作。 如果未指定,则此操作将以所有区域为目标。
区域服务器部分显示所有在线区域服务器及其负载。 如果您正在向集群发送大量请求,您可以从这一部分看到请求发送到了哪台服务器。 单击区域服务器地址的链接将显示所单击服务器的详细信息页面。 在详情页,您可以看到该服务器的指标数据以及该服务器上部署的所有地域的详细信息。
区域服务器的详细信息页面是一个简单但非常重要的界面,用于了解区域服务器的洞察信息。
如上图所示,它显示了地域服务器非常重要的指标。 例如,压缩队列大小(compactionQueueSize)、数据块缓存命中率(block CacheHitRatio)等。
使用 HBase Shell 管理表
HBase Shell 是 HBase 附带的命令行工具。 它提供了管理表、访问 HBase 中的数据和管理集群的基本功能。 HBase Shell 有几组命令。 用于管理表的组称为数据定义语言(DDL)。 使用 DDL 组命令,您可以创建、删除和更改 HBase 表。 您还可以从 HBase Shell 禁用/启用表。
做好准备
启动您的 HBase 集群。
怎么做……
以下步骤将向您展示如何使用 DDL 命令管理 HBase 表:
-
从客户端节点执行以下命令,以启动 HBase Shell 提示符:
hac@client1$ $HBASE_HOME/bin/hbase shell
-
使用
create
命令从 HBase Shell 创建具有单个列族(f1
)的表(t1
):hbase> create 't1', 'f1'
-
使用
list
命令显示表列表:hbase> list TABLE hly_temp t1
-
使用
describe
命令显示表属性:hbase> describe 't1' DESCRIPTION ENABLED {NAME => 't1', FAMILIES => [{NAME => 'f1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE' true, VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}]}
-
使用
disable
命令禁用该表:hbase> disable 't1' hbase> is_enabled 't1' false
-
使用
alter
命令更改表的属性。 在下面的代码片段中,我们将f1
更改为只有一个版本,还添加了一个新的列族f2:
hbase> alter 't1', {NAME => 'f1', VERSIONS => '1'}, {NAME => 'f2'}
-
使用
enable
命令启用该表:hbase> enable 't1' hbase> is_enabled 't1' true
-
输入以下命令再次禁用该表并将其删除:
hbase> disable 't1' hbase> drop 't1'
它是如何工作的.
使用 hbase shell
命令启动 HBase Shell。 此命令使用 HBase 配置文件(hbase-site.xml
)供客户端查找要连接的群集。 连接到群集后,它会启动提示符,等待命令。 如以下代码所示,您还可以使用 --config
选项,该选项允许您传递不同的配置以供 HBase Shell 使用:
$ hbase --config <configuration_directory> shell
从步骤 2 到步骤 8,我们创建了一个表,显示了它的属性,禁用了它,更改了表的一些属性,再次启用了它,最后删除了它。
通过使用 create
命令,我们创建了一个具有单个列族 f1
和所有默认表属性的表 t1
。 list
命令用于显示集群中的所有表。 表创建后,我们可以使用 describe
命令显示其属性。 describe
命令还显示该表是否启用。
要禁用表格,请使用 disable
命令。 我们只能在表被禁用后才能更改表的属性。 在步骤 5 中,我们更改了 f1
列族以仅保存其数据的一个版本。 我们还添加了新的柱族 f2
。 可以使用 alter
命令更改 describe
命令显示的所有属性。 我们可以使用 enable
命令在 HBase 中启用一个表。 要删除 HBase 中的表,请先禁用它,然后使用 drop
命令删除已禁用的表。
还有更多...
最有用的命令可能是 help
命令:
hbase> help
HBase Shell, version 0.92.1, r1298924, Fri Mar 9 16:58:34 UTC 2012
Type 'help "COMMAND"', (e.g. 'help "get"' -- the quotes are necessary) for help on a specific command.
Commands are grouped. Type 'help "COMMAND_GROUP"', (e.g. 'help "general"') for help on a command group.
COMMAND GROUPS:
Group name: general
Commands: status, version
一个 help
命令可以打印 HBase Shell 的所有可用命令和一般用法。 要显示命令的详细说明,请将其名称传递给 help
命令。 例如, help 'create'
显示 create
命令的详细用法:
hbase> help 'create'
Create table; pass table name, a dictionary of specifications per
column family, and optionally a dictionary of table configuration.
Dictionaries are described below in the GENERAL NOTES section.
Examples:
hbase> create 't1', {NAME => 'f1', VERSIONS => 5}
hbase> create 't1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}
hbase> # The above in shorthand would be the following:
hbase> create 't1', 'f1', 'f2', 'f3'
hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}
hbase> create 't1', 'f1', {SPLITS => ['10', '20', '30', '40']}
hbase> create 't1', 'f1', {SPLITS_FILE => 'splits.txt'}
使用 HBase Shell 访问 HBase 中的数据
HBase Shell 提供数据操作语言(DML)组命令来操作 HBase 中的数据。 DML 组包括命令 count, delete, deleteall, get, get_counter, incr, put, scan
和 truncate
。 顾名思义,这些命令提供了对 HBase 中数据的基本访问和更新操作。
备注
HBase 有一个名为 Counter 的功能,这对于在 HBase 上构建指标收集系统非常有用。 Get_counter
和 incr
是用于计数器操作的命令。
当在 HBase 中的大量数据上运行 count, scan
和 truncate
命令时,它们可能需要一些时间才能完成。
要计算大表的数量,应该使用 HBase 附带的 rowcounter
MapReduce 作业。 我们将在本章后面的行计数器配方中对其进行描述。
做好准备
启动 HBase 集群,从客户端连接到集群,并创建名为 t1
的表(如果该表不存在)。
怎么做……
以下步骤演示了如何使用 DML 命令访问 HBase 中的数据:
-
使用
put
命令将以下数据插入到表中:hbase> put 't1', 'row1', 'f1:c1', 'value1' hbase> put 't1', 'row1', 'f1:c2', 'value2' hbase> put 't1', 'row2', 'f1:c1', 'value3'
-
通过运行
count
命令获取表的行数:hbase> count 't1' 2 row(s) in 0.0160 seconds
-
使用
scan
命令扫描数据。 在包含大量行的表上调用scan
时,不要忘记指定LIMIT
属性:hbase> scan 't1', {LIMIT => 10} ROW COLUMN+CELL row1 column=f1:c1, timestamp=1324100932695, value=value1 row1 column=f1:c2, timestamp=1324101005928, value=value2 row2 column=f1:c1, timestamp=1324101012054, value=value3 2 row(s) in 0.0800 seconds
-
使用
get
命令获取指定行:hbase> get 't1', 'row1' COLUMN CELL f1:c1 timestamp=1324100932695, value=value1 f1:c2 timestamp=1324101005928, value=value2 2 row(s) in 0.0100 seconds
-
使用
delete
命令删除指定的单元格:hbase> delete 't1', 'row1', 'f1:c1'
-
再次执行
get
命令;您将看到f1:c1
单元格已从行hbase> get 't1', 'row1' COLUMN CELL f1:c2 timestamp=1324101005928, value=value2 1 row(s) in 0.0150 seconds
中删除
-
使用
deleteall
命令删除给定行中的所有单元格:hbase> deleteall 't1', 'row1'
-
再次执行
get
命令;您将看到整个row1
行已从表中删除:hbase> get 't1', 'row1' COLUMN CELL 0 row(s) in 0.0090 seconds
-
使用
incr
命令将计数器的(row1:f1:c1
)值加 1:hbase> incr 't1', 'row1', 'f1:c1', 1 COUNTER VALUE = 1
-
再次增加计数器 10:
```scala
hbase> incr 't1', 'row1', 'f1:c1', 10
COUNTER VALUE = 11
```
- 使用
get_counter
命令获取新的计数器值:
```scala
hbase> get_counter 't1', 'row1', 'f1:c1'
COUNTER VALUE = 11
```
- 通过运行
truncate
命令截断表:
```scala
hbase> truncate 't1'
Truncating 't1' table (it may take a while):
- Disabling table...
- Dropping table...
- Creating table...
0 row(s) in 4.5700 seconds
```
它是如何工作的.
put
命令将表名(t1
)、行键(row1
)、列族和限定符(F1:C1)、要放置的值(value1
)以及时间戳(可选)作为其参数。 f1:c1
中的 :
用作列族(f1
)和限定符(c1
)之间的分隔符。 如果希望 HBase Shell 将其视为字符串值,请使用单引号将其引起来;否则,HBase Shell 将尝试猜测您输入的值的数据类型。 例如,如果您键入以下命令:
hbase> put 't1', 'r1', 'f1:c1', 0000
您将在表格中输入数值 0
:
hbase> get 't1', 'r1'
COLUMN CELL
f1:c1 timestamp=1324103322181, value=0
但是当您输入以下命令时:
hbase> put 't1', 'r1', 'f1:c1', '0000'
您将在表中获得字符串 0000
:
hbase> get 't1', 'r1'
COLUMN CELL
f1:c1 timestamp=1324103492444, value=0000
count
命令计算表中的行数。 它以行为单位对 HBase 表进行计数,并在其输出上显示行号。 这就是为什么它在步骤 2 中返回两行,即使我们在步骤 1 中将三个单元格放入表中。当计算 HBase 中的大量数据时,此命令可能需要一段时间才能完成。 在这种情况下,更好的选择是使用 HBase 提供的行计数器实用程序,它将在 HBase 上运行 MapReduce 作业进行计数。
scan
命令用于扫描表。 您可以指定扫描的行范围、要包括的列、时间范围和筛选器。 对于高级用户,还有一个额外的选项CACHE_BLOCKS
,它指定扫描仪的块缓存是打开还是关闭。 help
命令对此命令的用法有非常详细的解释;键入 help 'scan'
可获得更多信息。 扫描 HBase 表的另一种方法是使用 HBase 上的配置单元;对于扫描具有复杂条件的表,这是一个更简单的选项。
get
命令非常简单;您必须给出所需的行键才能访问该命令。 虽然您可以选择指定要包含在结果中的列、时间戳或版本的范围,但也可以通过指定 MAXLENGTH
参数来限制输出长度。
delete
命令将一个单元格标记为已删除,而 deleteall
命令删除给定行中的所有单元格。
HBase 计数器是分布式原子计数器,存储为指定单元的值。 在指定表/行/列坐标下,使用 incr
命令增加计数器,使用 get_counter
命令获取计数器值。
truncate
命令充当禁用、删除和重新创建表的序列。
另请参阅
在本章中:
- 行计数器
- HBase 上的配置单元-使用类似 SQL 的语言查询 HBase
使用 HBase Shell 管理集群
有许多用于管理集群的 HBase Shell 命令。 这些命令属于工具组。
提示
警告
其中许多命令仅供高级用户使用,因为误用它们可能会对 HBase 安装造成意想不到的损害。
该工具的 GROUP 命令提供了手动管理 HBase 区域的界面。 它们的功能包括:
- 区域部署
- 区域分割
- 集群均衡
- 区域冲刷和压实
尽管 HBase 在默认情况下会自动执行所有这些操作,但在某些情况下,您可能需要手动平衡区域服务器的负载。 当默认的平衡算法不能很好地适用于您的数据访问模式时,情况尤其如此。
在本食谱中,我们将介绍如何手动刷新、压缩、拆分、平衡和移动 HBase 区域。
做好准备
启动您的 HBase 集群,创建一个表,并在其中放入一些数据。 出于演示目的,我们将使用在第 2 章、数据迁移中创建的 hly_temp
表。 我们假设您正在使用的表中已经有几个区域。 如下面的屏幕截图所示,您可以通过单击表的链接来查看表的区域列表,该表的链接显示在您的 HBase web UI 上:
怎么做……
我们将从手动刷新和压缩区域开始,然后手动拆分区域。 最后,我们将重新平衡这些区域,使它们在集群内保持良好平衡。
-
使用
flush
命令刷新表中的所有区域:hbase> flush 'hly_temp'
-
You can also flush an individual region by passing the region name to the
flush
command:hbase> flush 'hly_temp,,1324174482248.e3d9b9952973964f3d8e61e191924698.'
- 您可以在表的管理页面的表区域部分下找到该区域的名称:
-
通过运行
compact
命令压缩表中的所有区域:hbase> compact 'hly_temp'
-
通过运行
major_compact
命令对表运行主要压缩:hbase> major_compact 'hly_temp'
-
Split a region in a table by running the
split
command:hbase> split 'hly_temp,,1324174482248.e3d9b9952973964f3d8e61e191924698.'
- 在表的管理页面中,您会发现该区域已被分成两个区域,因此总区域计数变为 4:
-
使用
balance_switch
命令启用平衡器:hbase> balance_switch true false
- 输出(
false
)是以前的平衡器状态。
- 输出(
-
使用
balancer
命令平衡群集负载:hbase> balancer true
- 输出
true
表示平衡调用已成功触发。 它将在主服务器的后台运行。
- 输出
-
使用
move
命令将区域移动到特定区域服务器:hbase> move 'e3d9b9952973964f3d8e61e191924698', 'ip-10-168-75-28.us-west-1.compute.internal:60030:1324190304563'
它是如何工作的.
HBase 数据编辑将首先写入预写日志(WAL),然后写入区域服务器的 MemStore
。 在 MemStore
的大小达到阈值之前,编辑内容不会刷新到磁盘。 默认情况下,阈值为 128MB,这是由 hbase-site.xml
中的 hbase.hregion.memstore.flush.size
属性配置的。 flush
命令将这些挂起的记录同步刷新到磁盘。 它接受表名或区域名作为其参数。 区域名称由其表名、区域起始行键、区域服务器起始代码和自身的散列编码值组成。 您可以在表的管理页面的表区域部分下找到区域名称。
就像 flush
命令一样,如果将区域名称而不是表名传递给 compact
和 major_compact
命令,则仅在指定区域上执行压缩。 compact
和 major_compact
命令是异步的。 执行这些命令将表或区域排队以进行压缩,压缩将由托管指定区域或表的所有区域的服务器在后台执行。 您可能希望仅在加载时间较低时手动执行主要压缩。 这是因为压缩过程中可能会发生大量磁盘 IO 和网络流量,可能需要很长时间才能完成。
split
命令允许您拆分表中的特定区域或所有区域(如果为该命令指定了表名)。 当以非常高的写入负载运行 HBase 时,我们建议您关闭 HBase 的自动区域拆分功能,使用您自己的算法预先创建区域,并在特定区域变得太大时使用 split
命令拆分该区域。 我们将在第 9 章、高级配置和调整中介绍如何预分割表。
如果数据持续增长,我们的群集可能会变得不平衡。 为了平衡集群,HBase 会定期在后台运行其平衡功能。 另一方面,您也可以通过运行 balance_switch
命令显式启用平衡器,然后使用 balancer
命令触发平衡操作。 当您需要停止区域服务器进行维护时,停止平衡器也很有用。
下面的屏幕截图显示了我们的集群在步骤 7 中运行平衡之前的管理页。如您所见,第二个区域服务器保存了集群中几乎所有的区域(全部六个区域中的五个),这意味着大部分集群负载将流向该服务器:
在执行平衡操作之后,我们的集群变得更加均衡,因为区域被分配到集群中的所有区域服务器:
默认的负载平衡算法只会导致每个区域服务器占用部署在其上的大量区域。 如果此算法不适合您的数据访问模式,您可以使用 move
命令手动将区域移动到特定服务器。 move
命令将编码的区域名称和目标服务器名称作为其参数。 编码的区域名称是区域名称的散列代码后缀。 目标服务器名称的格式为 hostname:port:start-code
。 move
调用将从其最初部署的服务器关闭区域,在目标服务器上打开它,最后更新 .META
中的区域记录。 系统表。
另请参阅
- 在第 9 章,高级配置和调整中,使用您自己的算法配方预先创建区域
从 HBase Shell 执行 Java 方法
HBase Shell 是用 JRuby 编写的。 由于 JRuby 在Java 虚拟机(JVM)内运行,因此从 HBase Shell 执行 Java 方法非常容易。 HBase 附带了许多 Java 实用程序类;从 HBase Shell 执行 Java 方法的能力使直接从 HBase Shell 导入和使用这些实用程序成为可能。
在本食谱中,我们将演示两个如何从 HBase Shell 调用 Java 方法的示例。 第一个将 HBase Shell 输出的时间戳转换为可读的日期格式。 第二个导入 HBase 过滤器类,并在 scan
命令的扫描仪上执行过滤。
做好准备
启动您的 HBase 集群,创建一个表,并在其中放入一些数据。 出于演示目的,我们将使用在第 2 章中创建的 hly_temp
表。
在开始之前,请通过 HBase Shell 连接到您的群集。
怎么做……
要将 HBase Shell 输出的时间戳转换为可读的日期格式,请执行以下操作:
-
输入以下命令以从 HBase 获取行:
hbase> get 'hly_temp', 'AQW000617050101', 'n:' COLUMN CELL n:v01 timestamp=1323026325955, value=808C n:v02 timestamp=1323026325955, value=806C n:v03 timestamp=1323026325955, value=803C ...
-
导入 Java Date 类以转换 TIMESTAMP 的输出,然后使用
get
命令的 TIMESTAMP 创建Date
类的实例:hbase> import java.util.Date => Java::JavaUtil::Date hbase> Date.new(1323026325955).toString() => "Mon Dec 05 04:18:45 JST 2011"
-
要在扫描中使用 HBase 筛选器,请将以下类导入到 HBase Shell 中:
hbase> import org.apache.hadoop.hbase.util.Bytes => Java::OrgApacheHadoopHbaseUtil::Bytes hbase> import org.apache.hadoop.hbase.filter.PrefixFilter => Java::OrgApacheHadoopHbaseFilter::PrefixFilter
-
使用导入的
PrefixFilter
类执行scan
命令:hbase> scan 'hly_temp', {FILTER => PrefixFilter.new (Bytes.toBytes('AQW00061705010')), COLUMN => 'n:'} ROW COLUMN+CELLAQW000617050101 column=n:v01, timestamp=1323026325955, value=808CAQW000617050101 column=n:v02, timestamp=1323026325955, value=806CAQW000617050101 column=n:v03, timestamp=1323026325955, value=803C ... AQW000617050109 column=n:v24, timestamp=1323026325955, value=811C 9 row(s) in 0.1570 seconds
它是如何工作的.
正如您从步骤 1 的输出中看到的那样,显示的时间戳不便于阅读。 我们希望将其转换为正常的日期格式。
使用 java.util.Date
类可以轻松完成转换。 在步骤 2 中,我们使用 import
命令将其导入到 shell 会话中。 import
命令是一个基本的 JRuby 特性,它不是特定于 HBase 的。 导入 Date
类后,我们使用要转换的时间戳创建该类的实例。 最后,我们调用它的 toString()
方法将时间戳转换并打印为正常的日期格式。
步骤 3 和 4 向我们展示了如何在 HBase Shell 中导入和使用 HBase 实用程序。 首先,我们导入 org.apache.hadoop.hbase.util.Bytes
类,它是一个实用程序类,用于将其他数据类型从字节数组转换为字节数组。 这个类非常有用,因为 HBase 中的所有数据都存储为字节数组。 我们还在步骤 3 中导入了 org.apache.hadoop.hbase.filter.PrefixFilter
类。这个类是一个筛选类。 将此类与 scan
命令配合使用,使其仅将具有特定行键前缀的数据传递给客户端。 前缀是通过将其字节数组表示形式传递给 PrefixFilter
类的构造函数来指定的。 这正是我们在步骤 4 中所做的。我们指定了前缀 AQW00061705010
,使用 Bytes
类将其转换为字节数组,使用转换后的字节数组创建了 PrefixFilter
类的一个实例,然后将其设置为 scan
命令的过滤器。 正如您从输出中看到的,过滤器对 scan
命令很有效,因为只向客户端返回了 9 行(AQW000617050101
到 AQW000617050109)
)。
还有更多...
HBase 附带了许多其他有用的 Java 类。 以下链接列出了 HBase 的 Java API:
http://hbase.apache.org/apidocs/index.html
浏览一下以下 HBase API 包:
org.apache.hadoop.hbase.util
org.apache.hadoop.hbase.client
org.apache.hadoop.hbase.filter
org.apache.hadoop.hbase.mapreduce
行计数器
HBase Shell 中的 count
命令是计算 HBase 表中行数的直接方法。 但是,在包含大量数据的表上运行 count
命令可能需要很长时间才能完成。 对于这种情况,更好的方法是使用 RowCounter
类。 这个类将启动一个 MapReduce 作业来计算表上的行数,这比 count
命令效率高得多。
我们将在本食谱中描述 RowCounter
的用法。
做好准备
确保您的 Hadoop 和 HBase 集群正在运行。 MapReduce 也是必需的;如果它没有运行,请在 JobTracker 服务器上使用以下命令启动它:
hadoop@master1$ $HADOOP_HOME/bin/start-mapred.sh
登录到您的 HBase 客户端节点。
怎么做……
要在 hly_temp
表上运行行计数器 MapReduce 作业,请执行以下步骤:
-
将 ZooKeeper JAR 文件添加到客户端节点上的 Hadoop 类路径:
hadoop@client1$ vi $HADOOP_HOME/conf/hadoop-env.sh HBASE_HOME=/usr/local/hbase/current export HADOOP_CLASSPATH=$HADOOP_CLASSPATH: $HBASE_HOME/lib/zookeeper-3.4.3.jar: $HBASE_HOME/lib/guava-r09.jar
-
Execute the
rowcounter
command from your client:hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar rowcounter hly_temp
- 您将获得以下输出:
-
正如您可以从输出的一个计数器中看到的那样,我们在
hly_temp
表中有 95,630 行:11/12/22 07:14:30 INFO mapred.JobClient: org.apache.hadoop.hbase.mapreduce.RowCounter $RowCounterMapper$Counters 11/12/22 07:14:30 INFO mapred.JobClient: ROWS=95630
它是如何工作的.
当我们使用 hadoop jar
命令运行 JAR 文件时,该 JAR 文件的依赖项必须包含在 Hadoop 的类路径中。 这正是我们在步骤 1 中所做的。
在步骤 2 中,我们使用 hadoop jar
命令运行 hbase-0.92.1.jar
。 JAR 文件名之后的参数被传递给 JAR 文件的主类。 为了在 hly_temp
表上运行行计数器,我们将修复字符串 rowcounter
和表名传递给命令。 指定 rowcounter
将使 Hadoop 从 hbase-0.92.1.jar
执行 RowCounter
类。 RowCounter
类将接受 hly_temp
作为其参数,并最终为我们启动 MapReduce 作业。 还可以通过在表名后传递空格分隔的列(族)名称来指定要计数的列或列族。
除了行计数器,您还可以从命令行执行其他 MapReduce 作业。 有关详细信息,请键入以下内容:
$HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar
An example program must be given as the first argument.
Valid program names are:
CellCounter: Count cells in HBase table
completebulkload: Complete a bulk data load.
copytable: Export a table from local cluster to peer cluster
export: Write table data to HDFS.
import: Import data written by Export.
importtsv: Import data in TSV format.
rowcounter: Count rows in HBase table
verifyrep: Compare the data from tables in two different clusters. WARNING: It doesn't work for incrementColumnValues'd cells since the timestamp is changed after being appended to the log.
还有更多...
在对大型数据集运行 RowCounter
时,我们建议您调整客户端上的扫描仪缓存设置,如以下代码片断所示:
hac@client1$ vi $HBASE_HOME/conf/hbase-site.xml
<property>
<name>hbase.client.scanner.caching</name>
<value>1000</value>
</property>
这使得 HBase 为扫描仪上的每个下一次调用提取 1000
行。 默认情况下,HBase 在每次调用时提取 1
行。 将其设置为较高的值会提高扫描仪的速度,但会消耗更多内存。
WAL 工具-手动拆分和倾倒 WAL
HBase 编辑将首先写入区域服务器的预写日志(WAL)。 日志写入成功后,会更新地域服务器的 MemStore
。 由于 WAL 是 HDFS 上的序列文件,默认情况下它会自动复制到另外两个 DataNode 服务器上,因此单个区域服务器崩溃不会导致存储在其中的数据丢失。
由于 WAL 由部署在区域服务器上的所有区域共享,因此需要首先拆分 WAL,以便可以在每个相关区域上重放 WAL,以便从区域服务器崩溃中恢复。 HBase 使用此算法自动处理区域服务器故障转移。
HBase 有一个 Wal 工具,提供手动 Wal 拆分和倾倒设施。 我们将在本食谱中介绍如何使用此工具。
做好准备
我们需要将一些数据放入 HBase 表中,以便让 HBase 为我们的演示生成 WAL 文件。 同样,我们将在本食谱中使用 hly_temp
表。 我们将使用 HBase Shell Put
命令将以下数据放入 hly_temp
表中:
hbase> put 'hly_temp', 'AQW000617050101', 't:v25', 'wal tool demo'
hbase> put 'hly_temp', 'AQW000617050101', 't:v26', 'wal tool demo2'
hbase> put 'hly_temp', 'AQW000617050101', 't:v27', 'wal tool demo3'
hbase> delete 'hly_temp', 'AQW000617050101', 't:v27'
这些命令将使 HBase 在其 HDFS 上的 WAL 中添加四个条目。 此外,您还需要找出哪个区域保存我们在前面命令中放入的行,以及哪个区域服务器保存该区域。 要执行此操作,请打开表的管理页面。 在表区域部分,您将发现每个区域的开始键和结束键。 由于 HBase 中的数据是按行键排序的,因此按照词典顺序,一行属于行键的起始/结束键最接近的区域。
下表来自我们的 HBase web 用户界面的表区域部分。 如您所见,我们在前面的代码(AQW000617050101
)中输入的行键比字典序中第一个区域的结束键(USW000128350706
)小;第一个区域是保存我们在前面命令中输入的行的区域:
区域
|
区域服务器
|
开始键
|
结束关键点
|
| --- | --- | --- | --- |
| Hly_temp,,1324196693。 | 奴隶 1:60030 | | USW000128350706 |
| HLY_TEMP,USW000128。 | 奴隶 2:60030 | USW000128350706 | USW000138830523 |
| HLY_TEMP,USW000138。 | 奴隶 1:60030 | USW000138830523 | USW000149221223 |
| HLY_TEMP,USW000149。 | 奴隶 1:60030 | USW000149221223 | |
怎么做……
按照以下说明手动转储和拆分 WAL 日志:
-
使用
hadoop fs
或 HDFS 管理页面查找 WAL 文件的完整路径。 前面put
操作的 WAL 文件将在 HDFS 上的以下目录下生成:${hbase.rootdir}/.logs/target_region_server,port,start_code
-
执行带有转储选项的 WAL 工具以转储 WAL 文件:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.regionserver.wal.HLog --dump /hbase/.logs/ ip-10-168-151-193.us-west-1.compute. internal,60020,1324689025404/ ip-10-168-151-193.us-west-1.compute. internal%3A60020.1324689026748 -p -w 'AQW000617050101' Sequence 96649 from region 9e6dd810550443bb488c871728d5dee0 in table hly_temp Action: row: AQW000617050101 column: t:v25 at time: Sat Dec 24 10:21:59 JST 2011 value: wal tool demo Sequence 96650 from region 9e6dd810550443bb488c871728d5dee0 in table hly_temp Action: row: AQW000617050101 column: t:v26 at time: Sat Dec 24 10:22:09 JST 2011 value: wal tool demo2 Sequence 96651 from region 9e6dd810550443bb488c871728d5dee0 in table hly_temp Action: row: AQW000617050101 column: t:v27 at time: Sat Dec 24 10:22:18 JST 2011 value: wal tool demo3 Sequence 96652 from region 9e6dd810550443bb488c871728d5dee0 in table hly_temp Action: row: AQW000617050101 column: t:v27 at time: Sat Dec 24 10:22:27 JST 2011 value:
-
要手动拆分 WAL 文件,请切换到启动 Hadoop 和 HBase 的用户;在我们的示例中,该用户是
hadoop
用户:hac@client1$ sudo su hadoop
-
Execute the WAL tool with the split option to split the WAL files:
hadoop@client1$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.regionserver.wal.HLog --split hdfs://master1:8020/hbase/.logs/ip-10-160-51-22.us- west-1.compute.internal,60020,1324601160791 ... 11/12/24 10:35:43 INFO wal.HLogSplitter: hlog file splitting completed in 1700 ms for hdfs://master1:8020/hbase/.logs/ip-10-168-151-193.us-west-1.compute.internal,60020,1324689025404
备注
如果托管区域服务器在线,不要手动拆分 WAL,因为拆分 WAL 仅用于关闭区域服务器或从区域服务器崩溃中恢复。
它是如何工作的.
HBase 编辑在内部由 org.apache.hadoop.hbase.KeyValue
类表示。 当区域服务器接收到编辑时,它将在实际更新其 MemStore
数据之前将其(一个 KeyValue
实例)附加到其 WAL 文件中。 如果 MemStore
的大小达到阈值(默认为 128MB),则会通过将编辑刷新到 HFile 文件来将编辑持久保存到 HDFS 上。
WAL 文件存储在 HDFS 的 ${hbase.rootdir}/.logs/region_server, port,start_code
目录下。 在第一步中找到 WAL 文件的完整路径后,我们在第二步中执行 hbase
命令中的 org.apache.hadoop.hbase.regionserver.wal.HLog
类。这个类提供了两个功能-手动转储和拆分 WAL 文件。 要转储 WAL 文件,请将 --dump
选项与要转储的文件的完整路径一起传递到 HLog
。 第三个参数 p
告诉 HLog
也打印编辑值。 最后一个参数 -w 'AQW000617050101'
是一个仅输出行 AQW000617050101
的关联条目的过滤器。
您还可以通过将 -j
传递给命令来转储 JSON 格式的 WAL 文件。 以下是 Wal 转储的 JSON 输出示例:
[{"region":"9e6dd810550443bb488c871728d5dee0", "sequence":96649,"table":"hly_temp", "actions":[{"timestamp":1324689719111,"family":"t", "qualifier":"v25","row":"AQW000617050101"}]}, {"region":"9e6dd810550443bb488c871728d5dee0", "sequence":96650,"table":"hly_temp", "actions":[{"timestamp":1324689729381,"family":"t"," qualifier":"v26","row":"AQW000617050101"}]},
...]
当将 --dump
选项传递给 HLog
类时,它会在内部调用 HLogPrettyPrinter
类来转储 WAL 文件。 还有按地区或序列号过滤的选项。 将 -h
传递给 HLogPrettyPrinter
类以获取帮助:
$hbase org.apache.hadoop.hbase.regionserver.wal.HLogPrettyPrinter -h
usage: HLog <filename...> [-h] [-j] [-p] [-r <arg>] [-s <arg>] [-w <arg>]
-h,--help Output help message
-j,--json Output JSON
-p,--printvals Print values
-r,--region <arg> Region to filter by. Pass region name; e.g. '.META.,,1'
-s,--sequence <arg> Sequence to filter by. Pass sequence number.
-w,--row <arg> Row to filter by. Pass row name.
转储输出包含有关表名、区域、行、列、时间戳和编辑值的信息。 删除操作的值为 Null
。 Wal 的每个条目都有一个序列号,用于保持编辑的顺序。 已持久保存的最高序号编辑被写入每个 HBase 存储文件(HFile)的元字段。
由于手动拆分 wal 文件需要 hbase 安装目录下的写权限,因此我们切换到步骤 3 中启动 hbase 的用户,将 --split
传给 HLog
会使其在特定的 wal 目录上踢开拆分任务。 我们必须将 WAL 文件的目录传递给 Split 命令,因为所有 WAL 文件都必须被拆分并重放,以便从区域服务器崩溃中恢复。 与 dump
命令不同,您还需要为目标 wal 目录指定 HDFS 名称(在我们的示例中为hdfs://master1:8020
)。
已记录在 WAL 文件中但尚未保存的所有编辑将在拆分期间重播。 拆分后,包含编辑的文件将在正确的目录中生成。 下面是我们的 WAL 拆分生成的 HFile 文件:
您可以使用 HFile 工具查看生成的文件的内容:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile -p -f /hbase/hly_temp/9e6dd810550443bb488c871728d5dee0/t/5229337595615563418
K: AQW000617050101/t:v25/1324689719111/Put/vlen=13 V: wal tool demo
K: AQW000617050101/t:v26/1324689729381/Put/vlen=14 V: wal tool demo2
K: AQW000617050101/t:v27/1324689747967/DeleteColumn/vlen=0 V:
K: AQW000617050101/t:v27/1324689738230/Put/vlen=14 V: wal tool demo3
Scanned kv count -> 4
内容与本食谱的准备部分中的内容完全相同。 所有编辑都会成功重播;拆分过程中不会丢失任何数据。
WAL 拆分在关闭地域服务器时使用,或在地域服务器不干净地关闭后恢复日志编辑。 对于几乎所有情况,您都不需要手动拆分 WAL,因为如果区域服务器崩溃,主服务器将自动启动拆分。 对于许多节点不干净地关闭的情况,您可以在多台计算机上并行使用此手动拆分选项,以加快恢复速度。 从 HBase 0.92 开始,有了一个名为分布式日志拆分(Distributed Log Split)的新功能,该 WAL 拆分会自动分发。
另请参阅
- 本章中的HFile 工具-查看文本化 HFile 内容配方。
HFile 工具-查看文本化的 HFile 内容
HFile 是 HBase 存储数据的内部文件格式。 以下是 HFile 源代码中描述的前两行:
HBase 的文件格式。
排序的键/值对的文件。 键和值都是字节数组。
对于我们的管理任务,我们不需要知道 HFile 的详细信息。 但是,通过使用 HFile 工具,我们可以从 HFile 获得一些有用的信息。
HFile 工具提供了查看 HFile 内容的文本化版本的工具。
我们还可以使用此工具获取 HFile 文件的元数据。 某些元数据(如条目计数和平均键/值大小)是性能调优的重要指标。
我们将描述如何使用 HFile 工具来显示 HFile 文件的文本化内容和元数据。
做好准备
登录到您的 HBase 客户端节点。
选择要查看的区域名称或 HFile 文件路径。 区域名称可以在您的 HBase web 用户界面的表区域部分找到。 HFile 文件存储在 HDFS 的 ${hbase.rootdir}/table_name/region_name/column_family
目录下。
出于演示目的,我们将使用本配方中的 hly_temp
表。
怎么做……
请按照以下说明查看 HFile 文件的文本化内容或元数据:
-
键入以下内容以显示 HFile 文件的文本化键/值内容:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile -p -f /hbase/hly_temp/0d1604971684462a2860d43e2715558d/n/1742023046455748097 K: USW000128350706/n:v01/1323026325955/Put/vlen=4 V: 773S K: USW000128350706/n:v02/1323026325955/Put/vlen=4 V: 769S K: USW000128350706/n:v03/1323026325955/Put/vlen=4 V: 764S ...
-
要显示 HFile 文件的元数据,请使用 HFile 工具中的
-m
选项:$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile -m -f /hbase/hly_temp/0d1604971684462a2860d43e2715558d/n/1742023046455748097 Block index size as per heapsize: 12240 reader=/hbase/hly_temp/0d1604971684462a2860d43e2715558d/n/1742023046455748097, compression=lzo, inMemory=false, firstKey=USW000128350706/n:v01/1323026325955/Put, lastKey=USW000138830522/n:v24/1323026325955/Put, avgKeyLen=31, avgValueLen=4, entries=288024, length=2379789 fileinfoOffset=2371102, dataIndexOffset=2371361, dataIndexCount=190, metaIndexOffset=0, metaIndexCount=0, totalBytes=12387203, entryCount=288024, version=1 Fileinfo: MAJOR_COMPACTION_KEY = \x00 MAX_SEQ_ID_KEY = 96573 TIMERANGE = 1323026325955....1323026325955 hfile.AVG_KEY_LEN = 31 hfile.AVG_VALUE_LEN = 4 hfile.COMPARATOR = org.apache.hadoop.hbase.KeyValue$KeyComparator hfile.LASTKEY = \x00\x0FUSW000138830522\x01nv24\x00\x00\x014\x0A\x83\xA1\xC3\x04 Could not get bloom data from meta block
-
使用以下命令获取特定区域的条目总数:
hbase org.apache.hadoop.hbase.io.hfile.HFile -m -r hly_temp,,1324196693253.9e6dd810550443bb488c871728d5dee0\. | grep entries
- 您将获得以下输出:
区域的总条目计数是来自输出的条目的总和。
它是如何工作的.
HFile 格式使用 org.apache.hadoop.hbase.io.hfile.HFile
实现。 我们还可以使用这个类作为 HFile 文件的工具。
在步骤 1 中,我们只显示 HFile 文件的文本化内容。 文件路径由 -f
选项指定。 -p
标志告诉命令将键/值对的内容也包括到其输出中。 HFile 工具只需扫描特定 HFile 文件的所有块,并将所有键/值条目打印到其输出。
备注
请注意,在对该区域执行重大压缩之前,删除的单元格在 HFile 文件中也有一个条目。
在步骤 2 中,我们将 -m
标志传递给 HFile 工具,以显示指定 HFile 文件的元数据。 正如您从输出中看到的,元数据具有关于 HFile 文件的非常有用的信息。 此信息包括块索引大小、平均键/值长度、条目计数等。
一个典型的用例是获取表中每个地区的总条目计数,这样我们就能够知道我们的数据是否在表中的所有地区之间很好地平衡。 步骤 3 显示了如何实现此目的。 将带有 -r
选项的区域名称传递给 HFile 工具,使其在属于该区域的每个 HFile 文件上执行任务。 与 -m
标志协作,它显示区域中每个 HFile 文件的元数据。 在显示了每个 HFile 文件的元数据之后,我们可以简单地将输出条目计数相加,以获得该区域的键/值条目总数。
还有更多...
HFile 工具还有其他有用的选项。 键入以下命令以查看其用法:
$ ${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile
HBase hbck-检查 HBase 群集的一致性
HBase 提供 hbck
命令来检查各种不一致。 名称 hbck
来自 hdfs fsck
命令,该命令是用于检查 HDFS 是否不一致的工具。 下面是从 hbck:
的源代码中非常容易理解的描述
检查主服务器和区域服务器的内存中状态以及 HDFS 中的数据状态之间的一致性。
HBase hbck
不仅具有检查不一致的功能,还具有修复不一致的功能。
在生产中,我们建议您频繁运行 hbck
,以便更早发现不一致并轻松修复。
在本食谱中,我们将介绍如何使用 hbck
检查不一致。 我们还将对集群进行一些不一致的处理,然后演示如何使用 hbck
来修复它。
做好准备
启动您的 HBase 集群,并登录到您的 HBase 客户端节点。
怎么做……
使用 hbck
检查和修复 HBase 群集不一致的说明如下:
-
Check the health of the cluster with the default
hbck
command option:$ $HBASE_HOME/bin/hbase hbck
- 您将获得以下输出:
- 在命令输出的末尾,它会打印Status:OK,表示集群处于一致状态。
要演示 hbck
的修复功能,请通过在 HBase Shell 中键入以下内容来产生一些不一致:
-
通过键入以下命令进入 HBase Shell:
$ $HBASE_HOME/bin/hbase shell
-
使用
close_region
命令手动关闭区域:hbase> close_region 'hly_temp,,1324196693253.9e6dd810550443bb488c871728d5dee0.'
- 将最后一个参数替换为您的区域名称,您可以在 HBase web 用户界面上找到该名称。
-
再次运行
hbck
;您会发现,在其输出的末尾,它报告集群的状态不一致:$ $HBASE_HOME/bin/hbase hbck ERROR: Region hly_temp,,1324196693253.9e6dd810550443bb488c871728d5dee0\. not deployed on any region server. ERROR: (region hly_temp,USW000128350706,1324196693253.0d1604971684462a2860d43e2715558d.) First region should start with an empty key. ERROR: Found inconsistency in table hly_temp ... 2 inconsistencies detected. Status: INCONSISTENT
-
使用带有
-fix
选项的hbck
来修复不一致:$ $HBASE_HOME/bin/hbase hbck -fix ERROR: Region hly_temp,,1324196693253.9e6dd810550443bb488c871728d5dee0\. not deployed on any region server. Trying to fix unassigned region... ... 11/12/25 08:26:31 INFO util.HBaseFsck: Sleeping 10000ms before re-checking after fix... Version: 0.90.4 ... 0 inconsistencies detected. Status: OK
备注
在这段代码中,为了便于理解,我们跳过了命令的一些输出。
它是如何工作的.
在步骤 1 中,我们不带任何参数运行 hbck
命令。 这只是让 hbck
检查集群的不一致性。 如果没有发现不一致,它将在输出结束时报告 Status: OK
;否则,将报告不一致。
为了演示 hbck
的修复功能,我们在步骤 2 中从 HBase Shell 关闭一个地域。 close_region
命令通过 SSH 登录到特定地域所在的地域服务器,并直接将其关闭。 在这种情况下,不会通知主服务器。 这就是为什么我们的集群因为执行 close_region
命令而变得不一致。
当我们在步骤 3 中再次运行 hbck
时,该命令将发现我们的集群的状态不一致。 正如您从该命令的输出中看到的那样,我们在步骤 2 中关闭的区域报告为没有部署在任何区域服务器上-这是我们预期的结果。 如果您运行 get
命令来获取该区域中的一行,HBase 将向您报告一个错误,因为该区域处于脱机状态。
在步骤 4 中,我们使用 -fix
选项执行 hbck
。 正如您从步骤 4 的输出中看到的,指定 -fix
选项会导致 hbck
首先检查集群的不一致;如果发现不一致,它将尝试自动修复它。 之后,再次进行检查,以确保固定工作正常。 现在,您可以再次从固定区域获取数据。
hbck
命令还有其他选项,如显示所有区域的完整报告、仅检查 ROOT
和 META
表等。 键入以下命令以获取帮助:
$ $HBASE_HOME/bin/hbase hbck -h
另请参阅
- 在第 5 章,监控和诊断中,报告群集配方状态的简单脚本
HBase 上的配置单元-使用类似 SQL 的语言查询 HBase
HBase 支持几个接口来访问其表中的数据,例如:
- HBase 外壳
- Java 客户端 API
- 休息、节俭和 Avro
HBase Shell 简单明了,但有点过于简单,无法执行复杂的查询。 其他接口需要编程,这不适合即席查询。
随着数据的不断增长,人们可能想要一种简单的方法来分析存储在 HBase 中的大量数据。 分析应该是高效的、即席的,并且不应该需要编程。 Hive 是目前实现这一目的的最佳方法。
HIVE 是为 Hadoop 构建的数据仓库基础设施。 HIVE 用于即席查询和分析大型数据集,而无需编写 MapReduce 程序。 HIVE 支持一种名为HiveQL(HQL)的类似 SQL 的查询语言来访问其表中的数据。
我们可以集成 HBase 和配置单元,这样我们就可以使用 HQL 语句访问 HBase 表,既可以读也可以写。
在本指南中,我们将介绍如何在 HBase 表上安装和运行配置单元。 我们将演示使用配置单元从 HBase 表读取数据/向 HBase 表写入数据的简单演示。
做好准备
配置单元将其元数据存储在 RDBMS(如 MySQL 或 PostgreSQL)中。 我们将在本食谱中使用 MySQL,因此需要运行 MySQL 安装。 以下是帮助您安装 MySQL 的在线MySQL 5.5 参考手册:
http://dev.mysql.com/doc/refman/5.5/en/index.html。
只需在您的客户端节点上安装配置单元。 我们假设您已经在 HBase 客户端的同一节点上安装了配置单元(在我们的示例中为client1
)。
我们将使用用户 hadoop
作为所有配置单元文件的所有者。 所有配置单元文件和数据都将存储在 /usr/local/hive
下。 提前在客户端节点上创建一个 /usr/local/hive
目录;将该目录的所有者更改为 hadoop
用户。
HQL 由配置单元在内部转换为 MapReduce 作业。 除了正在运行的 HBase 安装之外,您还需要启动 MapReduce 来使用配置单元查询 HBase 数据。 您可以使用以下命令启动 MapReduce:
hadoop@master1$ $HADOOP_HOME/bin/start-mapred.sh
我们将数据从 TSV 文件加载到配置单元,然后将数据从配置单元写入 HBase 表。 仅作为演示,我们的 TSV 文件(hly_temp.tsv)将仅包含以下数据:
AQW000617050101 808C 806C 803C
AQW000617050102 808C 805C 803C
AQW000617050103 807C 805C 802C
将 hly_temp.tsv
文件放在 /home/hac
下;我们稍后会将其加载到配置单元中。
怎么做……
要安装配置单元并在 HBase 上运行,请按照以下说明操作:
-
Download the latest version of Hive from http://www.apache.org/dyn/closer.cgi/hive/.
在写这本书的时候,最新版本是 0.8.0。
-
下载 tarball 并解压缩到配置单元的根目录。 添加符号链接和环境变量:
hadoop@client1$ ln -s hive-0.8.0 current hadoop@client1$ export HIVE_HOME=/usr/local/hive/current
-
在 HDFS 上创建以下目录:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /tmp hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hive/warehouse
-
Change the owner and permission of the created directories:
hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -chmod 777 /tmp hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -chown -R hac:hac /user/hive/warehouse
I.向 Hive 库添加依赖项:从http://www.mysql.com/downloads/connector/j/下载 mysql 的 jdbc 驱动。
二、。 将下载的 JAR 文件移动到
$HIVE_HOME/lib:
hadoop@client1$ mv mysql-connector-java-5.1.18.jar $HIVE_HOME/lib/
四、。 将 HBase 和 ZooKeeper JAR 文件替换为我们正在使用的版本:
hadoop@client1$ rm $HIVE_HOME/lib/hbase-0.89.0-SNAPSHOT*.jar hadoop@client1$ rm $HIVE_HOME/lib/zookeeper-3.3.1.jar hadoop@client1$ cp $HBASE_HOME/hbase-0.92.1.jar $HIVE_HOME/lib hadoop@client1$ cp $HBASE_HOME/lib/zookeeper-3.4.3.jar $HIVE_HOME/lib
-
将依赖项添加到配置单元以运行 MapReduce 作业:
hadoop@client1$ mkdir $HIVE_HOME/auxlib hadoop@client1$ cp $HBASE_HOME/hbase-0.92.1.jar $HIVE_HOME/auxlib hadoop@client1$ cp $HBASE_HOME/lib/zookeeper-3.4.3.jar $HIVE_HOME/auxlib hadoop@client1$ cp $HIVE_HOME/lib/hive-hbase-handler-0.8.0.jar $HIVE_HOME/auxlib
-
Connect to your MySQL server:
$ mysql -h<mysql_server_host> -uroot -p
I.在 MySQL 中为配置单元创建数据库和表:
mysql> create database metastore; mysql> use metastore; mysql> source /usr/local/hive/current/scripts/metastore/upgrade/mysql/hive-schema-0.8.0.mysql.sql
-
为配置单元创建 MySQL 用户:
mysql> create user 'hive'@'%' identified by 'your_password'; mysql> grant select,insert,update,delete on metastore.* to 'hive'@'%'; mysql> revoke alter,create on metastore.* from 'hive'@'%';
-
在配置单元的环境设置文件(
hive-env.sh
)中设置HADOOP_HOME
:hadoop@client1$ cp $HIVE_HOME/conf/hive-env.sh.template $HIVE_HOME/conf/hive-env.sh hadoop@client1$ vi $HIVE_HOME/conf/hive-env.sh HADOOP_HOME=/usr/local/hadoop/current
-
将以下内容添加到配置单元的配置文件(
hive-site.xml
)。 您需要根据您的环境调整 MySQL 服务器、密码和 ZooKeeper 仲裁设置:hadoop@client1$ vi $HIVE_HOME/conf/hive-site.xml <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://mysql_server_host/metastore</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>hive</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>your_password</value> </property> <property> <name>datanucleus.autoCreateSchema</name> <value>false</value> </property> <property> <name>datanucleus.fixedDatastore</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>zoo1,zoo2,zoo3</value> </property> </configuration>
-
在本配方中,我们将使用
hac
用户来运行配置单元作业。 切换到用户并从配置单元命令行界面(CLI)测试配置单元安装。 在这里,我们只需创建、显示和删除一个配置单元表,以确认我们的配置单元安装:
```scala
hac@client1$ $HIVE_HOME/bin/hive
hive> create table pokes (foo int, bar string);
hive> show tables;
OK
pokes
hive> drop table pokes;
```
- 使用以下命令将现有 HBase 表(
hly_temp
)映射到配置单元表(hbase_hly_temp
):
```scala
hive> create external table hbase_hly_temp (key string, v01 string, v02 string, v03 string) stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties ("hbase.columns.mapping" = ":key,n:v01,n:v02,n:v03") tblproperties("hbase.table.name" = "hly_temp");
```
- 从我们在配置单元中映射的表中查询 HBase 数据:
```scala
hive> select * from hbase_hly_temp where v01='808C';
Total MapReduce jobs = 1
Launching Job 1 out of 1
...
2011-12-25 15:25:58,134 Stage-1 map = 100%, reduce = 100%, Cumulative CPU 27.51 sec
MapReduce Total cumulative CPU time: 27 seconds 510 msec
Ended Job = job_201112251140_0001
MapReduce Jobs Launched:
Job 0: Map: 4 Accumulative CPU: 27.51 sec HDFS Read: 1204 HDFS Write: 2345 SUCESS
Total MapReduce CPU Time Spent: 27 seconds 510 msec
OK
AQW000617050101 808C 806C 803C
AQW000617050102 808C 805C 803C
AQW000617050104 808C 805S 803C
...
```
- 创建配置单元表格(
hive_hly_temp2
)以存储来自 TSV 文件的数据:
```scala
hive> create table hive_hly_temp2 (key string, v01 string, v02 string, v03 string) row format delimited fields terminated by '\t' lines terminated by '\n';
```
- 将 TSV 文件加载到配置单元表格中:
```scala
hive> load data local inpath '/home/hac/hly_temp.tsv' overwrite into table hive_hly_temp2;
Copying data from file:/home/hac/hly_temp.tsv
Copying file: file:/home/hac/hly_temp.tsv
Loading data to table default.hive_hly_temp2
Deleted hdfs://master1/user/hive/warehouse/hive_hly_temp2
OK
```
- 创建配置单元管理的 HBase 表并将其与配置单元表映射:
```scala
hive> create table hbase_hly_temp2 (key string, v01 string, v02 string, v03 string) stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties ("hbase.columns.mapping" = ":key,n:v01,n:v02,n:v03") tblproperties ("hbase.table.name" = "hly_temp2");
```
* 在这里,我们在 HBase 中创建了一个名为`hly_temp2`的表;该表由配置单元管理,表名为`hbase_hly_temp2.`
- 通过 HQL:
```scala
hive> insert overwrite table hbase_hly_temp2 select * from hive_hly_temp2;
```
将数据从配置单元表格(`hive_hly_temp2`)写入 HBase 表格(`hbase_hly_temp2`)
- Confirm the data in HBase, which we inserted via HQL in the previous step:
```scala
hac@client1$ $HBASE_HOME/bin/hbase shell
hbase> scan 'hly_temp2'
```
* 您将获得以下输出:

它是如何工作的.
配置单元的表数据存储在 HDFS 上;默认路径为 /user/hive/warehouse
。 这就是我们在步骤 3 和 4 中创建此目录并将其所有者更改为 hac
用户的原因。我们还需要在 HDFS 上创建一个 /tmp
目录,以便配置单元可以将其临时文件保存在该目录下。
在步骤 5 中,我们将 MySQL 的 JDBC 驱动程序添加到配置单元库文件夹。 我们需要这样做,因为我们将配置配置单元以将其元数据存储在 MySQL 中。 从配置单元访问 MySQL 需要 JDBC 驱动程序。 由于配置单元附带的 HBase 和 ZooKeeper JAR 文件已过期,我们将其替换为 HBase 安装中的文件。
在步骤 6 中,我们在 $HIVE_HOME
下创建一个名为 auxlib
的目录。 这是一个特殊的目录。 当配置单元启动 MapReduce 作业时,此目录下的所有 JAR 文件将通过 Hadoop Distributed Cache 发送到 TaskTracker,并添加到作业任务的类路径中。 我们放在 $HIVE_HOME/auxlib
下的 JAR 文件是所有配置单元 MapReduce 作业的依赖项。
我们在步骤 7 中为 MySQL 中的配置单元创建了数据库和表。因为所有的配置单元模式都是在 $HIVE_HOME/scripts/metastore/upgrade/mysql/hive-schema-0.8.0.mysql.sql
中定义的,所以我们只需执行这个 SQL 脚本就可以在 MySQL 中创建配置单元表。 配置单元的 MySQL 用户是在步骤 8 中创建的。我们建议您限制此用户的权限,以防止其在 metastore
数据库方案中创建或更改表。
我们需要在配置单元的环境设置文件(hive-env.sh
)中设置 HADOOP_HOME
,以通知配置单元安装 Hadoop 的位置。 Metastore 数据库和 ZooKeeper 仲裁设置在配置单元的配置文件(hive-site.xml
)中设置。 配置单元将使用这些设置连接到 Metastore 数据库和 HBase 群集。 如果一切设置正确,您应该能够使用配置单元的命令行界面(CLI)连接到配置单元。
在步骤 12 中,我们创建一个外部配置单元表来映射现有的 HBase 表。 外部配置单元表格由 create external table
语句定义。 删除外部表时,数据不会从 HBase 中删除。 要在 HBase 上创建配置单元表,请使用 org.apache.hadoop.hive.hbase.HBaseStorageHandler
作为其存储处理程序。 对于每个配置单元列,必须将其映射到逗号分隔的 hbase.columns.mapping
字符串中的相应 HBase 列或列族。 注意而不是,不要在条目之间包含空格。 必须正好有一个 :key
条目用于映射 HBase 行键。 不需要映射每个 HBase 列,但只有映射的列可以从配置单元访问。 hbase.table.name
属性是可选的。 如果未指定,则该表在配置单元和 HBase 中将具有相同的名称。
在大多数情况下,配置单元查询将被转换为 MapReduce 作业。 如果您打开 MapReduce 管理页面,您将发现从我们在步骤 12 中执行的查询转换而来的 MapReduce 作业。
步骤 14 到 18 显示如何将数据从配置单元插入到 HBase 表中。 在步骤 14 中,我们创建一个常规配置单元表作为数据源,然后在步骤 15 中将我们的 TSV 文件加载到该表中。
目标 HBase 表是在步骤 16 中创建的。请注意,该表不是外部表,因为我们没有在创建者中指定关键字 external
。 从配置单元中删除该表也会从 HBase 中删除该表。 执行完步骤 15 中的命令后,您应该能够在 HBase Shell 中看到新的(空)表。
在创建表时,我们可以使用 insert overwrite
HQL 语句向其中插入数据。 插入到 hbase_hly_temp2
中的数据实际上由 HbaseStorageHandler
存储在 hly_temp2
HBase 表中。 这正是我们在步骤 18 中看到的结果。
如您所见,将配置单元与 HBase 集成可在 HBase 之上提供强大的数据访问接口。 虽然缺点是不支持一些 HBase 特性,例如时间戳/版本支持、复合行键映射以及在来自配置单元的单个 HBase 行上进行更新。
尽管它不是针对延迟敏感型系统的,因为它每次都是 MapReduce 作业,但是配置单元集成是 HBase 的一个很好的附加组件,用于特别的查询功能。
四、备份和恢复 HBase 数据
在本章中,我们将介绍:
- 使用
distcp
进行完全关闭备份 - 使用
CopyTable
将数据从一个表复制到另一个表 - 导出 HBase 表以转储 HDFS 上的文件
- 通过从 HDFS 导入转储文件恢复 HBase 数据
- 备份 NameNode 元数据
- 正在备份区域起始键
- 群集复制
简介
如果您正在考虑在生产中使用 HBase,您可能希望了解 HBase 的备份选项和做法。 挑战在于您需要备份的数据集可能很大,因此备份解决方案必须高效。 预计它将能够扩展到数百 TB 的存储容量,并在合理的时间框架内完成数据恢复。
备份 HBase 有两种策略:
- 通过完全关闭群集来备份它
- 在实时群集上备份它
完全关闭备份必须首先停止 HBase(或禁用所有表),然后使用 Hadoop 的 distcp
命令将 HBase 目录的内容复制到同一 HDFS 上的另一个目录,或复制到不同的 HDFS。 要从完全关闭的备份恢复,只需使用 distcp
将备份的文件复制回 HBase 目录。
实时群集备份有几种方法:
- 使用
CopyTable
实用程序将数据从一个表复制到另一个表 - 将 HBase 表导出到 HDFS 文件,然后将文件导入回 HBase
- HBase 群集复制
CopyTable
实用程序可用于将数据从一个表复制到同一集群上的另一个表,或复制到不同的集群。 Export
实用程序将表的数据转储到同一集群上的 HDFS。 作为一组 Export
, Import
实用程序用于恢复转储文件的数据。
上述每种方法都有其优缺点。 完全关闭备份的好处是在备份过程中没有机会将数据写入群集,因此可以确保备份状态一致。 不利的一面是显而易见的--集群已经停机。 至于实时群集备份方法,由于群集已启动,因此在备份过程中可能会丢失编辑。 此外,由于 HBase 编辑仅在行级别是原子的,如果您的表相互依赖,则在执行 Export
或 CopyTable
的同时修改表可能会导致备份不一致。 当前 Apache 版本不支持创建 HBase 表的快照。
HBase 支持集群复制。 这是在不同的 HBase 部署之间复制数据的一种方式。 可以将群集复制视为 HBase 级别的灾难恢复解决方案。
除了表之外,您可能还需要备份 HDFS 元数据和 HBase 区域起始键。 HDFS 元数据包含 HDFS 的文件系统映像和提交日志。 元数据损坏可能会损坏整个 HDFS 元数据;建议经常备份元数据。 区域起始键代表 HBase 中的数据分布。 备份区域起始键不仅可以恢复数据,还可以恢复数据分布。 如果我们提前拆分表,使用分布均匀的区域起始键,使用 CopyTable/Import
实用程序恢复数据的速度可以显著提高。
在本章中,我们将介绍如何使用上述方法备份 HBase 数据,它们的优缺点,以及根据您的数据集大小、资源和要求选择哪种方法。
使用 Distcp 进行完全关闭备份
distcp
(Distributed Copy)是 Hadoop 提供的一个工具,用于在相同或不同的 HDFS 群集上复制大型数据集。 它使用 MapReduce 并行复制文件、处理错误和恢复,并报告作业状态。
由于 HBase 将其所有文件(包括 HDFS 上的系统文件)都存储在 HDFS 上,因此我们可以简单地使用 distcp
将 HBase 目录复制到同一 HDFS 上的另一个目录,或者复制到不同的 HDFS,以备份源 HBase 集群。
请注意,这是一个完全关闭的备份解决方案。 distcp
工具之所以有效,是因为 HBase 集群已关闭(或所有表都被禁用),并且在此过程中没有对文件进行编辑。 不要在活动的 HBase 群集上使用而不是。 因此,此解决方案适用于能够容忍其 HBase 群集定期完全关闭的环境。 例如,用于后端批处理但不服务于前端请求的集群。
我们将介绍如何使用 distcp
将完全关闭的 HBase 集群备份到不同的 HDFS 集群。 在不同群集上进行备份可用作灾难恢复解决方案,并有助于提高数据可用性。 在生产中,建议将完全关闭的 HBase 集群备份到不同的 HDFS 集群。
在本章后面部分,我们还将演示如何从备份中还原数据。
做好准备
如果要使用 distcp
将 HBase 数据备份到另一个群集,则需要一个辅助群集。 您还可以将 HBase 数据备份到与源 HBase 群集相同的群集。 对于这种情况,不需要另一个群集。
在本食谱中,我们将使用不同的群集进行备份。 备份群集在不同的 EC2 实例上运行。 我们假设备份集群上运行的 HDFS 为 hdfs://l-master1:8020; l-master1
,表示该集群运行在 EC2 大型实例上。 这是因为我们稍后将使用该集群进行性能调优,而小实例规格对于该用途来说太低了。
分别在源群集和备份群集上启动 HDFS。
distcp
工具使用 MapReduce 并行复制文件,因此您还需要在源群集上启动 MapReduce。 可以使用以下命令从 JobTracker 节点启动 MapReduce:
hadoop@master1$ $HADOOP_HOME/bin/start-mapred.sh
我们将备份备份集群上 /backup
目录下的 HBase 目录。 使用以下命令从备份群集的 Hadoop 客户端(l-client1)提前创建此目录:
hadoop@l-client1$ $HADOOP_HOME/bin/hadoop fs -mkdir /backup
怎么做……
按照以下说明使用 distcp:
备份/恢复 HBase 数据
-
关闭源和备份 HBase 群集(如果它们正在运行):
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh
-
通过检查 HMaster 守护程序是否已启动,确保已在源群集上关闭 HBase:
hadoop@master1$ $JAVA_HOME/bin/jps 1567 JobTracker 1416 NameNode 1705 Jps 1690 QuorumPeerMain
- 确保输出中没有列出 HMaster 守护程序。
-
Also, make sure
mapred.map.tasks.speculative.execution
is not set tofinal
andtrue
on the client of the source cluster.此属性在
$HADOOP_HOME/conf
目录下的 MapReduce 配置文件(mapred-site.xml
)中设置。 如果设置为final
和true
,请删除该设置。 这是一个客户端更改;它只会影响从该客户端提交的 MapReduce 作业。 -
使用
distcp
将 HBase 根目录从源群集复制到备份群集。 HBase 根目录由 HBase 配置文件(hbase-site.xml
)中的hbase.rootdir
属性设置。 我们假设它是hdfs://master1:8020/hbase
,在这个配方中:hadoop@client1$ $HADOOP_HOME/bin/hadoop distcp hdfs://master1:8020/hbase hdfs://l-master1:8020/backup 12/01/03 12:27:53 INFO tools.DistCp: srcPaths=[hdfs://master1:8020/hbase] 12/01/03 12:27:53 INFO tools.DistCp: destPath=hdfs://l-master1:8020/backup 12/01/03 12:27:54 INFO tools.DistCp: sourcePathsCount=76 12/01/03 12:27:54 INFO tools.DistCp: filesToCopyCount=34 12/01/03 12:27:54 INFO tools.DistCp: bytesToCopyCount=102.0m 12/01/03 12:27:55 INFO mapred.JobClient: Running job: job_201201031153_0002 12/01/03 12:27:56 INFO mapred.JobClient: map 0% reduce 0% ...
您将从 JobTracker 的管理页面中找到源群集的正在运行的 MapReduce 作业:
在 distcp
作业完成后,您应该能够在备份群集上的 /backup
目录下找到复制的 HBase 目录:
hadoop@l-client1$ $HADOOP_HOME/bin/hadoop fs -lsr /backup/hbase
drwxr-xr-x - hadoop hadoop 0 2012-01-03 12:28 /backup/hbase/-ROOT-
...
从上次备份恢复数据的步骤如下:
-
使用以下命令在备份群集上启动 MapReduce:
hadoop@l-master1$ $HADOOP_HOME/bin/start-mapred.sh
-
确保备份群集客户端上的
mapred.map.tasks.speculative.execution
未设置为final
和true
。 -
确保 HBase 未在源群集和备份群集上运行。 如果 HBase 正在运行,请先停止它:
hadoop@l-master1$ $HBASE_HOME/bin/stop-hbase.sh
-
确保源群集上的
hbase.rootdir
目录下没有文件。 如果存在文件,请将它们移动到另一个路径:hadoop@client1$ $HADOOP_HOME/bin/hadoop fs -mv /hbase /tmp
- 如果您确定不需要这些文件,则可以将其删除。
-
将 HBase 目录从备份群集复制到源群集:
hadoop@l-client1$ $HADOOP_HOME/bin/hadoop distcp hdfs: //l-master1:8020/backup/hbase hdfs://master1:8020/
-
在源群集上启动 HBase;您应该能够通过 HBase Shell 访问恢复的数据:
hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
它是如何工作的.
正如我们提到的, distcp
是一个完全关闭的备份选项;因此,我们在步骤 1 中停止 HBase,并在步骤 2 中确认停止。 jps
是 Java SDK 附带的一个方便的命令。 它显示被执行用户拥有的所有 Java 进程。 在我们的例子中,因为所有与 HBase 相关的守护进程都是由 hadoop
用户启动的,所以我们可以使用 jps
命令来查看 HBase 的主守护进程(HMaster)是否正在运行。
distcp
使用 MapReduce 并行复制文件。 如果属性 mapred.map.tasks.speculative.execution
设置为 true
,则可以并行执行某些映射任务的多个实例,以执行相同的任务。 这是每个作业的客户端属性。 distcp
会将其 MapReduce 作业的此属性设置为 false
,因为 HDFS 不会处理同一文件的多个编写器。 但最终属性不能在单个 MapReduce 作业中更改。 如果将 mapred.map.tasks.speculative.execution
设置为 final
和 true
,则复制结果未定义。 在步骤 3 中,我们删除最后一个设置,以使 distcp
能够在其 MapReduce 作业中配置此属性。
我们只需要复制 hbase.rootdir
目录。 在步骤 4 中,我们执行 distcp
将其从源群集复制到备份群集。参数包括源目录的完整路径(包括其 HDFS 架构)以及备份群集上目标目录的 HDFS 架构和路径。 正如您从其输出中看到的那样, distcp
启动了一个 MapReduce 作业来并行复制数据。
由 distcp
启动的 MapReduce 作业仅在其映射阶段复制数据。 贴图的最大数量由 -m
选项指定。 请注意,这只是对 MapReduce 作业的一个提示;更多的映射不一定会增加同步副本的数量或总体吞吐量。
为了从备份恢复,我们将数据从备份群集复制回源群集,然后在源群集上启动 HBase。
有关 distcp
选项的完整列表,请键入以下命令:
$HADOOP_HOME/bin/hadoop distcp
使用 CopyTable 将数据从一个表复制到另一个表
CopyTable
是一个实用程序,用于将一个表的数据复制到同一集群或不同 HBase 集群上的另一个表。 您可以复制到同一群集中的表;但是,如果您有另一个要作为备份的群集,则可能需要使用 CopyTable
作为实时备份选项,将表的数据复制到备份群集。
CopyTable
可配置开始和结束时间戳。 如果指定,则只复制特定时间范围内带有时间戳的数据。 此功能使得在某些情况下可以增量备份 HBase 表。
备注
“增量备份”是一种只备份上次备份期间更改的数据的方法。
提示
注意:由于群集持续运行,因此在复制过程中可能会丢失编辑。
在本食谱中,我们将介绍如何使用 CopyTable
将一个表的数据复制到不同 HBase 集群上的另一个表中。 我们将演示将数据从 hly_temp
表复制到 hly_temp2
表;我们将只复制列族 n
中的数据。
做好准备
在客户机节点上,您需要将 HBase 配置文件(hbase-site.xml
)添加到 Hadoop 的类路径,以便 MapReduce 作业可以访问 HBase 集群。 您可以通过链接 Hadoop 配置目录下的 hbase-site.xml
来完成此操作,如下所示:
hadoop@client1$ ln -s $HBASE_HOME/conf/hbase-site.xml $HADOOP_HOME/conf/hbase-site.xml
另外,通过编辑 hadoop-env.sh:
,将 HBase 依赖 JAR 添加到 Hadoop 的类路径中
hadoop@client1$ vi $HADOOP_HOME/conf/hadoop-env.sh
export HADOOP_CLASSPATH= /usr/local/zookeeper/current/zookeeper-3.4.3.jar
备注
上述步骤是在 HBase 上运行 MapReduce 的最低要求。 由于这是一个常见的过程,我们将在这本书中跳过它的细节。
在源群集和备份群集上启动 HBase。 如果没有另一个群集,还可以将一个表复制到本地群集上的另一个表。 但是,不建议将其用于生产中的备份目的。 我们假设备份集群的 ZooKeeper 仲裁为 l-master1:2181
,HBase 根目录为 /hbase
。
CopyTable
使用 MapReduce 并行复制数据;您需要使用以下命令在源群集上启动 MapReduce:
hadoop@master1$ $HADOOP_HOME/bin/start-mapred.sh
怎么做……
以下是将列族 n
中的数据从 hly_temp
表复制到备份群集上的 hly_temp2
表的说明:
-
通过 HBase Shell 连接到备份 HBase 群集,如果目标表不存在,则创建目标表:
hbase> create 'hly_temp2', {NAME => 'n'}
-
Run the following command from your client node of the source cluster to copy data from the
hly_temp
table tohly_temp2
on the backup cluster:hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar copytable --families=n --peer.adr=l-master1:2181:/hbase --new.name=hly_temp2 hly_temp 2/01/09 15:24:34 INFO zookeeper.ZooKeeper: Initiating client connection, connectString=l-master1:2181 sessionTimeout=10000 watcher=hconnection 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Opening socket connection to server l-master1/10.170.114.96:2181 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Socket connection established to l-master1/10.170.114.96:2181, initiating session 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Session establishment complete on server l-master1/10.170.114.96:2181, sessionid = 0x134c11bb47c000a, negotiated timeout = 10000 12/01/09 15:24:34 INFO mapreduce.TableOutputFormat: Created table instance for hly_temp2 12/01/09 15:24:34 INFO zookeeper.ZooKeeper: Initiating client connection, connectString=master1:2181 sessionTimeout=10000 watcher=hconnection 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Opening socket connection to server master1/10.166.105.212:2181 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Socket connection established to master1/10.166.105.212:2181, initiating session 12/01/09 15:24:34 INFO zookeeper.ClientCnxn: Session establishment complete on server master1/10.166.105.212:2181, sessionid = 0x134c11d62a20019, negotiated timeout = 10000 12/01/09 15:24:34 INFO mapred.JobClient: Running job: job_201201091517_0001 12/01/09 15:24:35 INFO mapred.JobClient: map 0% reduce 0% 12/01/09 15:25:49 INFO mapred.JobClient: map 50% reduce 0% ...
- 您将从 JobTracker 的管理页面中找到源群集的正在运行的 MapReduce 作业:
-
您应该能够通过 HBase Shell:
hbase> scan 'hly_temp2', { LIMIT => 1 } ROW COLUMN+CELL AQW000617050101 column=n:v01, timestamp=1323026325955, value=808C AQW000617050101 column=n:v02, timestamp=1323026325955, value=806C AQW000617050101 column=n:v03, timestamp=1323026325955, value=803C hbase> count 'hly_temp2' 95630 row(s) in 22.4740 seconds
访问备份群集上
hly_temp2
中的数据 -
键入以下命令以仅复制具有特定时间戳范围的数据:
hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar copytable --families=n --peer.adr=l-master1:2181:/hbase --new.name=hly_temp2 --starttime=1324173586754 --endtime=1324189940000 hly_temp
它是如何工作的.
在步骤 1 中,我们使用要复制的列 MARY(N)在备份集群上创建目标表(hly_temp2
)。
在步骤 2 中,我们将 FAMILY n
列中的数据从 hly_temp
表复制到备份群集上的 hly_temp2
表。 备份集群由 --peer.adr
选项指定,方法是指定 ZooKeeper 仲裁地址,格式为 hbase.zookeeper.quorum:hbase.zookeeper.client.port:zookeeper.znode.parent
,如 server,server2,server3:2181:/hbase
。
--families
选项用于指定要复制的族的逗号分隔列表。 目标表由 --new.name
选项指定;如果两个表具有相同的名称,则跳过此选项。
正如您从输出中看到的,数据是在 MapReduce 作业中复制的。 hadoop jar
命令用于在 MapReduce 中运行 JAR 文件。 MapReduce 作业扫描源表,读取属于目标系列的数据条目,然后使用其普通客户端 API 将它们写入备份群集上的目标表。
从步骤 3 的输出中,您将发现列族 n
中的 hly_temp
表的数据(包括每个条目的时间戳)已被复制到备份集群上的 hly_temp2
表中。 HBase 使用时间戳对编辑进行排序;时间戳较大的编辑具有较新的单元格版本。 默认情况下,时间戳由 HBase 使用长值自动设置,该值表示当前时间和午夜(1970 年 1 月 1 日 UTC)之间的毫秒差。
在步骤 4 中,我们将时间戳范围参数添加到 CopyTable
命令中。在复制作业的扫描阶段,将跳过特定时间范围之外的数据。 由 --starttime
选项指定的最小时间戳值包含在内。 由 --endtime
选项指定的最大时间戳值是独占的。
步骤 2 可用于完全备份,而步骤 3 在某些情况下可用于增量备份。 使用具有特定时间戳范围的 CopyTable
与增量备份不同。 这是因为,在将数据放入 HBase 时,时间戳可以由客户端显式指定,并且不能保证新插入的数据具有更大的时间戳。 另一个原因是 CopyTable
不会处理删除。 没有办法知道数据是否被删除。 我们建议您在应用级别实施自己的增量备份解决方案。
有关 CopyTable
用法的完整说明,请键入以下命令:
$HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar copytable
导出 HBase 表以转储 HDFS 上的文件
HBase export
实用程序将表的内容转储到同一个 HDFS 集群。 转储文件采用 Hadoop 序列文件格式。 将数据导出到 Hadoop 序列文件对于数据备份有好处,因为 Hadoop 序列文件格式支持多种压缩类型和算法。 有了它,我们可以选择最适合我们环境的压缩选项。
与我们在前面的菜谱中提到的 copytable
实用程序一样, export
可配置为具有开始和结束时间戳,因此只有特定时间范围内的数据才会被转储。 此功能使 export
能够增量地将 HBase 表导出到 HDFS。
HBase export
也是一个实时备份选项。 由于群集正在运行,因此在导出过程中可能会丢失编辑。 在本食谱中,我们将介绍如何使用 export
实用程序将表导出到同一集群上的 HDFS。 我们将在下一个配方中介绍 import
实用程序,该实用程序用于从 export
转储恢复数据。
做好准备
首先,启动 HDFS 和 HBase 集群。
我们将把 hly_temp
表导出到 HDFS 群集上的 /backup/hly_temp
。 您需要预先创建一个 /backup
目录。
export
实用程序使用 MapReduce 导出数据。 将 HBase 的可配置文件(hbase-site.xml
)和依赖项 JAR 文件添加到您的客户端节点上 Hadoop 的类路径中。
怎么做……
按照以下说明使用 export
实用程序将 HBase 表导出到 HDFS:
-
Run the following command from your client node to export all the data in the
hly_temp
table:hadoop@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar export -D mapred.output.compress=true -D mapred.output.compression.codec=org.apache.hadoop.io.compress.BZip2Codec -D mapred.output.compression.type=BLOCK hly_temp /backup/hly_temp 2147483647 12/01/08 07:39:35 INFO mapreduce.Export: verisons=2147483647, starttime=0, endtime=9223372036854775807 ... 12/01/08 07:39:37 INFO zookeeper.ClientCnxn: Session establishment complete on server master1/10.160.229.77:2181, sessionid = 0x134ba4a09070013, negotiated timeout = 10000 12/01/08 07:39:38 INFO mapred.JobClient: Running job: job_201201080737_0001 12/01/08 07:39:39 INFO mapred.JobClient: map 0% reduce 0% 12/01/08 07:42:56 INFO mapred.JobClient: map 25% reduce 0%
- 您将从 JobTracker 的管理页面中找到导出 MapReduce 作业:
- MapReduce 作业完成后,您将在 HDFS 上的
/backup/hly_temp
目录下找到生成的转储文件(part-m-000x
):
-
执行以下命令,仅导出具有特定时间戳范围的数据:
hadoop@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar export -D mapred.output.compress=true -D mapred.output.compression.codec=org.apache.hadoop.io.compress.BZip2Codec -D mapred.output.compression.type=BLOCK hly_temp /backup/hly_temp_2 2147483647 1324601918502 1324693970000
它是如何工作的.
运行带有 export
参数的 HBase JAR 文件将执行 export
实用程序来转储 HBase 表的内容。 在步骤 1 中,我们将 hly_temp
表转储到 HDFS 上的 /backup/hly_temp
目录。 最后一个参数指定要导出的数据的最高版本。 HBase 支持在一个单元中存储多个版本的数据。 单元格可以存储的最大版本由其列族的 VERSIONS
属性确定,该属性是在创建表时指定的。 要转储具有多版本列族的表,我们必须将要导出的最高版本传递给 export
命令,否则将只导出最新版本。 在我们的示例中,我们只指定 Integer.MAX_VALUE (2147483647)
来导出所有版本的数据。
许多 -D
属性已传递给 export
命令。 这些被设置为控制其运行时属性的导出 MapReduce 作业。 将 mapred.output.compress
属性设置为 true
以使 MapReduce 压缩其输出(序列文件)。 mapred.output.compression.codec
属性控制用于导出的压缩算法。 我们将其设置为使用压缩比最高的 BZip2 编解码器。
在撰写本书时,这些是 Hadoop 广泛使用的编解码器。 选择哪种编解码器取决于您的需求;所有这些编解码器都有空间/时间权衡:
|压缩格式
|
Hadoop 压缩编解码器
|
压缩比 / 压缩率
|
(解)压缩速度
|
| --- | --- | --- | --- |
| gzip | org.apache.hadoop.io.compress.GzipCodec
| 中等的 / 中号的 / 适中的 / 半生熟的 | 中等的 / 中号的 / 适中的 / 半生熟的 |
| BZip2 | org.apache.hadoop.io.compress.BZip2Codec
| 高的 / 大的 / 高级的 / 高音调的 | 低速的 / 慢的 / 迟钝的 / 不曲折的 |
| LZO | com.hadoop.compression.lzo.LzoCodec
| 洛 (人名) | 快速的 / 牢实的 / 感光快的 / 紧的 |
由于许可问题,LZO 不包含在 Hadoop 包中;请参阅章,基本性能调优中的使用压缩配方来安装 LZO。
mapred.output.compression.type
属性用于指定 Hadoop 序列文件的压缩类型。可用值为 NONE, RECORD
和 BLOCK
。 对于大多数情况,您应该选择 BLOCK
,因为它将记录序列压缩在块中,这是最有效的压缩方式。
正如您从输出中看到的,数据是通过 MapReduce 导出的。 这是通过 hadoop jar
命令完成的,该命令在 MapReduce 中运行一个 JAR 文件。 MapReduce 作业扫描该表,读取所有数据条目,然后将它们写入同一 HDFS 群集上输出路径下的 Hadoop 序列文件。
我们已在步骤 2 中向 export
命令添加了时间戳范围参数。在导出作业的扫描阶段将跳过特定时间范围之外的数据。 最小时间戳值(1324601918502
)是包含的,而最大时间戳值(1324693970000
)是排除的。
就像我们在前面的食谱中提到的 CopyTable
实用程序一样,步骤 1(没有特定的开始/结束时间)可用于完全备份,而步骤 2(具有特定的开始/结束时间)在某些情况下用于增量备份。
还有更多...
在将数据导出到同一群集上的 HDFS 时,如果 HDFS 崩溃,则备份可能不可用。 我们建议您使用 Hadoop distcp
工具将导出的文件复制到不同的 HDFS 群集。 如果您的数据量不是很大,一个更好的选择是将转储的文件复制到磁带上,以便进行脱机备份。
另请参阅
- 在本章中,通过从 HDFS配方导入转储文件来恢复 HBase 数据
- 使用压缩配方,在第 8 章,基本性能调整
通过从 HDFS 导入转储文件恢复 HBase 数据
HBase Import
实用程序用于将 Export
实用程序导出的数据加载到现有 HBase 表中。 这是从 Export
实用程序备份解决方案还原数据的过程。
我们将在本食谱中查看 Import
实用程序的用法。
做好准备
首先,启动 HDFS 和 HBase 集群。
我们将把在上一个配方中导出的文件导入到我们的 hly_temp
表中。 如果您没有这些转储文件,请参考Exporting HBase 表在 HDFS配方上转储文件,以便提前生成转储文件。 我们假设转储文件保存在 /backup/hly_temp
目录中。
Import
实用程序使用 MapReduce 导入数据。 将 HBase 可配置文件(hbase-site.xml
)和依赖项 JAR 文件添加到客户端节点上的 Hadoop 类路径。
怎么做……
要将转储文件导入 hly_temp
表,请执行以下操作:
-
通过 HBase Shell 连接到您的 HBase 群集,如果目标表不存在,则创建目标表:
hbase> create 'hly_temp', {NAME => 'n'}
-
Run the following command from your client node to import data into the
hly_temp
table:hadoop@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar import hly_temp /backup/hly_temp 12/01/08 08:52:42 INFO mapreduce.TableOutputFormat: Created table instance for hly_temp 12/01/08 08:52:42 INFO input.FileInputFormat: Total input paths to process : 4 12/01/08 08:52:42 INFO mapred.JobClient: Running job: job_201201080737_0004 12/01/08 08:52:43 INFO mapred.JobClient: map 0% reduce 0% 12/01/08 08:53:09 INFO mapred.JobClient: map 1% reduce 0% ...
- 您将从 JobTracker 的管理页面找到导入 MapReduce 作业:
-
在 MapReduce 作业完成后,您应该能够通过 HBase Shell 访问
hly_temp
表中的数据:hbase> scan 'hly_temp', {LIMIT => 1} ROW COLUMN+CELL AQW000617050101 column=n:v01, timestamp=1323026325955, value=808C AQW000617050101 column=n:v02, timestamp=1323026325955, value=806C AQW000617050101 column=n:v03, timestamp=1323026325955, value=803C ...
它是如何工作的.
我们在步骤 1 中创建了目标表。该表必须具有转储文件中存在的所有列族;否则,导入作业将失败,并显示 NoSuchColumnFamilyException
错误消息。
从步骤 2 的输出中可以看到,数据是通过 MapReduce 导入的。 这是通过 hadoop jar
命令完成的,该命令执行打包在 JAR 文件中的 MapReduce 代码。 导入 MapReduce 作业从输入目录(/backup/hly_temp
)下的转储文件读取数据条目,然后使用 HBase 的客户端 API 将它们写入目标表(hly_temp
)。
转储文件中的数据(包括每个条目的时间戳)将由 Import
实用程序恢复。 在此之后,我们建议您对表进行重大压缩并手动执行集群平衡,以使集群以最佳状态运行。 这可以通过 HBase Shell 使用以下命令来完成:
hbase> major_compact 'hly_temp'
hbase> balancer
还有更多...
如果要将数据导入到新创建的表中,您可能会发现导入速度没有预期的那么快。 这是因为表格只从一个区域开始,所有编辑都将转到该区域。 这使得托管区域服务器忙于处理这些请求。 同时,其他地域的服务器都是空载的。 群集保持在这种不平衡状态下运行,直到第一个区域中的数据达到阈值,并且该区域被分成两个区域。 新表格需要很长时间才能通过这种自动区域拆分实现良好的平衡。
解决方案是在创建表时预先创建足够的区域。 需要使用适当的边界创建区域,以便可以将编辑内容很好地分发到所有这些预先创建的区域。 为了确定正确的边界,您可能还需要备份区域起始键,这也可用于恢复 HBase 表的区域分布。
从 HBase 0.94 开始, Import
实用程序可以生成用于批量加载的 HBase 内部文件(HFiles)。 使用此功能,我们能够在更短的时间内恢复转储文件。 有关详细信息(https://issues.apache.org/jira/browse/HBASE-5440
),请参阅HBase-5440。
另请参阅
在本章中:
- 导出 HBase 表以转储 HDFS上的文件
- 正在备份区域起始键
在第 9 章中,高级配置和调整:
- 使用您自己的算法预先创建区域
在第 3 章中,使用管理工具:
- 使用 HBase Shell 管理群集
备份 NameNode 元数据
由于 HBase 在 HDFS 中运行,因此除了照顾 HBase 集群之外,保持 HDFS 在健康状态下运行也很重要。 NameNode 是 HDFS 集群中最重要的组件。 NameNode 崩溃会导致整个 HDFS 群集无法访问。 HDFS 集群的元数据(包括文件系统映像和编辑日志)由 NameNode 管理。
我们需要在两种情况下保护 NameNode 元数据:
- 崩溃时丢失的元数据
- 元数据因任何原因损坏
对于第一种情况,我们可以设置 NameNode 将其元数据与 NFS 挂载一起写入其本地磁盘。 正如设置多个高可用性(HA)主节点配方中所述,在第 1 章,设置 HBase 集群中,我们甚至可以设置多个 NameNode 节点来实现高可用性。
对于第二种情况,我们的解决方案是频繁备份元数据,以便在元数据损坏时恢复 NameNode 状态。
在本指南中,我们将介绍如何备份和恢复 NameNode 元数据。
做好准备
启动 HDFS 并登录到客户端节点。 我们假设 NameNode 正在使用默认的 Web UI 端口 50070
在 master1
服务器上运行。 确保为您的客户端节点打开端口。
dfs.name.dir
属性指定 NameNode 元数据的保存位置。 Grep HDFS 配置文件(hdfs-site.xml
)以找出执行恢复任务所需的此属性的值。 在演示中,我们假设该值为 /usr/local/hadoop/var/dfs/name
。
我们将备份保存在客户端节点上的 /backup/namenode
下,并使用 NameNode 服务器上的 /restore/namenode
作为恢复的工作目录。 创建这些目录,并确保运行备份任务的用户对它们具有写入权限。
怎么做……
要备份 NameNode 元数据,请执行以下操作:
-
设置环境变量:
hadoop@client1$ export BACKUP_DIR=/backup/namenode
-
从 NameNode Web 用户界面获取 HDFS 的文件系统映像:
hadoop@client1$ curl http://master1:50070/getimage?getimage=1 > $BACKUP_DIR/fsimage
-
从 NameNode Web 用户界面获取 HDFS 的编辑日志:
hadoop@client1$ curl http://master1:50070/getimage?getedit=1 > $BACKUP_DIR/edits
-
使用以下命令为获取的元数据创建 tarball:
hadoop@client1$ tar cfvj $BACKUP_DIR/metadata.`date +%Y%m%d`.tar.bz2 -C $BACKUP_DIR fsimage edits
为了从元数据备份恢复 NameNode 状态:
-
如果 NameNode 守护程序正在您的 NameNode 服务器上运行,请停止它:
hadoop@master1$ $HADOOP_HOME/bin/hadoop-daemon.sh stop namenode
- 如果前面的命令由于某种原因不起作用,只需终止 NameNode 的 Java 进程即可。
-
将元数据从备份复制到 NameNode 服务器上的
dfs.name.dir
目录:hadoop@master1$ cd /usr/local/hadoop/var/dfs/name/current hadoop@master1$ tar cfvj /restore/namenode/metadata-org.tar.bz2 fsimage edits hadoop@master1$ scp client1:/backup/namenode/metadata.`date +%Y%m%d`.tar.bz2 ./ hadoop@master1$ tar xfvj metadata.`date +%Y%m%d`.tar.bz2 hadoop@master1$ rm metadata.`date +%Y%m%d`.tar.bz2
-
再次启动 NameNode 后台进程:
hadoop@master1$ $HADOOP_HOME/bin/hadoop-daemon.sh start namenode
-
Run
hadoop fsck
to check inconsistencies after the NameNode server has been restarted:hadoop@master1$ $HADOOP_HOME/bin/hadoop fsck / -files | grep CORRUPT
- 如果许多文件已损坏(如您在 Web 用户界面中看到的那样),NameNode 可能会继续处于安全模式,直到您显式将其关闭:
-
Turn off safe mode by explicitly using the following command:
hadoop@master1$ $HADOOP_HOME/bin/hadoop dfsadmin -safemode leave Safe mode is OFF
- Web 用户界面不会声明安全模式已关闭;相反,它可能会在页面上显示一条警告消息:
-
运行
hadoop fsck -move
将损坏的文件(如果有)移动到/lost+found:
hadoop@master1$ $HADOOP_HOME/bin/hadoop fsck / -move The filesystem under path '/' is CORRUPT
-
删除后再次运行
hadoop fsck
以确认不存在不一致:hadoop@master1$ $HADOOP_HOME/bin/hadoop fsck / The filesystem under path '/' is HEALTHY
它是如何工作的.
NameNode 支持从其 Web 用户界面获取其文件系统映像和编辑日志。 从第 1 步到第 4 步,我们使用此功能将 NameNode 元数据提取到客户机节点上的本地磁盘,然后将提取的元数据压缩为一个文件作为备份。
为了从备份中恢复元数据,我们首先在步骤 5 中停止 NameNode 守护进程,然后在步骤 6 中,将原始元数据移动到我们的工作目录中,以防您出于任何原因想要取消恢复。 之后,我们将备份复制到存储 NameNode 元数据的 $dfs.name.dir/current
目录,然后再次启动 NameNode 守护进程。
如果在我们的最新备份之后进行了编辑,我们恢复的元数据将与 DataNode 上的实际数据不匹配,在这种情况下,我们的 HDFS 将变得不一致。 我们在步骤 8 中运行 hadoop fsck
命令来检查 HDFS 不一致。如果提供了路径和 -files
选项, fsck
将检查指定路径下的不一致,并打印它正在检查的文件。
如果检测到损坏的文件,我们需要删除它们以使 HDFS 保持一致。 如果许多文件已损坏,NameNode 将无法达到其将保持在安全模式下的报告块比率的阈值。 在删除损坏的文件之前,我们需要显式关闭 NameNode 安全模式。 这是通过步骤 9 中的 hadoop dfsadmin
命令完成的。 Dfsadmin
是用于管理任务的 HDFS 命令。 使用 -safemode leave
选项运行它会显式关闭 NameNode 安全模式。
当安全模式关闭时,我们可以使用带有 -move
选项的 fsck
命令将损坏的文件移动到 /lost+found
。 在此之后, fsck
应该报告文件系统健康,您应该能够访问 HDFS。
还有更多...
从元数据备份恢复 NameNode 状态可能需要很长时间才能完成。 这是因为重新启动 NameNode 守护进程将首先从磁盘加载文件系统映像,然后重播编辑日志以重建最终的系统状态。 编辑日志可能很大,重放可能需要很长时间。
HDFS 有一个针对此问题的 Second DaryNameNode 组件。 请注意,Second daryNameNode 不是 NameNode 的备份守护进程。 它会定期(默认为 1 小时)将编辑日志压缩到检查点中,以便在重新启动 NameNode 后加载最新的检查点,并加载一个小得多的编辑日志,其中仅包含自检查点以来所做的编辑。 借助这种压缩功能,Second DaryNameNode 使 NameNode 元数据的备份和恢复更加高效。
默认情况下,Second daryNameNode 与 NameNode 在同一节点上运行。 为了提高可伸缩性和持久性,我们建议您在与 NameNode 服务器不同的服务器上运行它。 配置在 Hadoop 配置目录下的 masters
文件中:
hadoop@master1$ vi $HADOOP_HOME/conf/masters
secondary_namenode_host
要在此文件中启动 Second daryNameNode,请添加所需的主机名,然后重新启动 HDFS 以应用更改。
备份区域起始键
除了 HBase 中的表之外,我们还应该备份每个表的区域起始键。 区域起始键决定表中的数据分布,因为区域由区域起始键拆分。 地域是 HBase 中负载均衡和指标采集的基本单位。
如果使用 distcp
执行完全关机备份,则无需备份区域起始密钥,因为 distcp
还会将区域边界复制到备份群集。
但对于实时备份选项,备份区域起始键与表数据一样重要,如果您的数据分布难以提前计算,或者您的区域是手动拆分的,则备份区域起始键尤其重要。 这一点很重要,因为实时备份选项(包括 CopyTable
和 Export
实用程序)使用普通的 HBase 客户端 API 来还原 MapReduce 作业中的数据。 如果我们在运行 Restore MapReduce 作业之前预先创建分割良好的区域,则可以显著提高恢复速度。
在本食谱中,我们将介绍如何备份 HBase 表的区域起始键。 我们将创建一个脚本来从 HBase 表中获取区域起始键。
做好准备
启动您的 HBase 集群,创建一个表,并在其中放入一些数据。 表中需要有几个区域,这样我们才能验证我们的脚本是否正常工作。 如果您的表只有一个区域,请从 HBase web UI 的表页手动将其拆分为几个区域。 我们将在此配方中使用 hly_temp
表;其 Web 用户界面页面类似于以下屏幕截图中所示的网页:
我们将备份客户端节点上 /backup/hly_temp
目录下的 hly_temp
表的区域起始键。 创建此目录,并向将运行备份脚本的用户授予写入权限。
怎么做……
按照以下说明备份 hly_temp
表的区域起始键:
-
为 __T0 创建抯脚本文件:\T1++
-
在客户端节点上运行脚本:
hac@client1 $HBASE_HOME/bin/hbase org.jruby.Main region-starting-keys.rb hly_temp /backup/hly_temp/regions
-
脚本将创建一个包含区域起始键的文件(
/backup/hly_temp/regions
)。 该文件如下所示:hac@client1$ cat /backup/hly_temp/regions USW000128350706 USW000138830523 USW000149221223
它是如何工作的.
要获取 HBase 表的区域起始键,我们需要从脚本访问 HBase Java 客户端 API。 有几种基于 JVM 的脚本语言,比如 Jython、Scala 和 JRuby。 我们在步骤 1 中创建的脚本是用 JRuby 编写的。 我们之所以选择 JRuby,是因为 HBase Shell 是用 JRuby 编写的,不需要引入另一种语言及其依赖项。
在脚本中,我们创建了一个 HTable
实例来访问我们的 hly_temp
表。 HTable
类用于与单个 HBase 表通信。 我们只将表名(hly_temp
)传递给 HTable
类的构造函数。 HBase 集群的 ZooKeeper 法定连接字符串被设置为默认值,这是通过使用 hbase
命令从客户端上的 HBase 配置文件(hbase-site.xml
)实现的。 如果成功创建了 HTable
实例,则将建立与 ZooKeeper 仲裁的连接会话。 之后,我们可以通过 HTable
实例与我们的 hly_temp
表通信。
接下来,我们调用 HTable
实例的 getStartKeys()
方法,该方法返回表的区域起始键数组。 区域起始键是 HBase 中由字节数组表示的行键。
在脚本的下一步中,我们使用 Bytes
类将键转换为字符串。 Bytes
类是将其他对象从字节数组转换为字节数组或将其他对象转换为字节数组的实用程序类。 这个类很有用,因为所有键/值都以字节数组的形式存储在 HBase 中,而不考虑它们的原始数据类型。
在脚本的最后一步,我们将转换后的字符串写入输出文件,最后关闭到集群的连接。
在步骤 2 中,我们通过运行 hbase
命令来执行脚本。 hbase
命令用于在当前 HBase 上下文中运行 Java 类。 要运行 JRuby 脚本,我们将 JRuby 的 org.jruby.Main—the
main 类传递给 hbase
命令。 我们的脚本被传递给 org.jruby.Main
类并在 HBase 上下文中运行。 表名和输出文件的路径也通过命令行传递给脚本。
正如您从步骤 3 的输出中看到的,我们写入输出文件的区域起始键与我们在表的管理网页上看到的相同。
另请参阅
- 在第 9 章,高级配置和调整中,使用您自己的算法配方预先创建区域
群集复制
HBase 支持集群复制,这是在 HBase 集群之间复制数据的一种方式。 例如,它可以用作轻松地将编辑从实时前端集群传送到后端的批处理目的集群的一种方式。
HBase 复制的基本架构非常实用。 主群集捕获预写日志(WAL),并将可复制的键/值(在支持复制的情况下对列族进行编辑)从日志放入复制队列。 然后,复制消息被发送到对等群集,然后使用其正常的 HBase 客户端 API 在该群集上重放。 主集群还会保留 ZooKeeper 中复制的 WAL 的当前位置,以进行故障恢复。
由于 HBase 复制是异步完成的,因此参与复制的群集在地理上可能相距遥远。 如果它们之间的连接离线一段时间,这不是问题,因为主群集会跟踪复制,并在连接再次在线后恢复它。 这意味着 HBase 复制可以作为 HBase 层的灾难恢复解决方案。
在本食谱中,我们将了解如何在两个集群之间启用表的复制。
做好准备
您需要两个 HBase 集群-一个是主集群,另一个是复制对等(从)集群。 这里,假设主机是 master1:2181/hbase
,对等方是 l-master1:2181/hbase
;这两个集群不需要具有相同的大小。
ZooKeeper 应该独立处理,而不是由 HBase 处理。 检查 hbase-env.sh
文件中的 HBASE_MANAGES_ZK
设置,并确保将其设置为 false
。
所有机器,包括 ZooKeeper 集群和 HBase 集群,都需要能够访问其他机器。 确保两个群集具有相同的 HBase 和 Hadoop 主版本。 例如,主服务器上的 0.92.1 和对等机上的 0.92.0 是正确的,但 0.90 是不正确的。
怎么做……
按照以下说明在 HBase 群集之间复制数据:
-
将以下代码添加到 HBase 的配置文件(
hbase-site.xml
)以启用主群集上的复制:hadoop@master1$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.replication</name> <value>true</value> </property>
-
将更改同步到所有服务器,包括集群中的客户端节点,然后重新启动 HBase。
-
连接到主群集上的 HBase Shell,并在要复制的表上启用复制:
hac@client1$ $HBASE_HOME/bin/hbase shell hbase> create 'reptable1', { NAME => 'cf1', REPLICATION_SCOPE => 1}
hbase> disable 'reptable1' hbase> alter 'reptable1', NAME => 'cf1', REPLICATION_SCOPE => '1' hbase> enable 'reptable1'
- 如果您使用的是现有表,请更改它以支持复制:
-
在对等(从)群集上也执行步骤 1 到 3。 这包括启用复制、重新启动 HBase 和创建表的相同副本。
-
通过 HBase Shell 从主群集添加对等复制群集:
hbase> add_peer '1', 'l-master1:2181:/hbase'
-
通过运行以下命令在主群集上启动复制:
hbase> start_replication
-
向主群集中添加一些数据:
hbase> put 'reptable1', 'row1', 'cf1:v1', 'foo' hbase> put 'reptable1', 'row1', 'cf1:v2', 'bar' hbase> put 'reptable1', 'row2', 'cf1:v1', 'foobar'
- 您应该能够在短时间内看到数据出现在对等群集表中。
-
连接到对等群集上的 HBase Shell 并对表进行扫描,以查看数据是否已复制:
hac@l-client1$ $HBASE_HOME/bin/hbase shell hbase> scan ' reptable1' ROW COLUMN+CELL row1 column=cf1:v1, timestamp=1326095294209, value=foo row1 column=cf1:v2, timestamp=1326095300633, value=bar row2 column=cf1:v1, timestamp=1326095307619, value=foobar 2 row(s) in 0.0280 seconds
-
通过在主群集上调用
verifyrep
命令来验证两个群集上的复制数据:hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar verifyrep 1 reptable1 12/01/09 16:50:22 INFO replication.ReplicationZookeeper: Replication is now started ... 12/01/09 16:50:24 INFO mapred.JobClient: Running job: job_201201091517_0005 12/01/09 16:50:25 INFO mapred.JobClient: map 0% reduce 0% 12/01/09 16:50:46 INFO mapred.JobClient: map 100% reduce 0% 12/01/09 16:50:51 INFO mapred.JobClient: Job complete: job_201201091517_0005 12/01/09 16:50:51 INFO mapred.JobClient: Counters: 19 ... 12/01/09 16:50:51 INFO mapred.JobClient: File Output Format Counters 12/01/09 16:50:51 INFO mapred.JobClient: Bytes Written=0 12/01/09 16:50:51 INFO mapred.JobClient: org.apache.hadoop.hbase.mapreduce.replication.VerifyReplication$Verifier$Counters 12/01/09 16:50:51 INFO mapred.JobClient: GOODROWS=2 ...
- 我们跳过了 verifyrep 命令的一些输出,以使其更清晰。
-
通过运行以下命令停止主群集上的复制:
```scala
hbase> stop_replication
```
- 使用以下命令从主群集中删除复制对等:
```scala
hbase> remove_peer '1'
```
它是如何工作的.
复制仍被视为试验性功能,默认情况下该功能处于禁用状态。 为了启用它,我们在 HBase 的配置文件(hbase-site.xml
)中添加了 hbase.replication
属性,并将其设置为 true
。 为了应用更改,我们将其同步到所有节点,包括集群中的客户端节点,然后在步骤 2 中重新启动 HBase。数据复制配置在列系列级别。 使用 REPLICATION_SCOPE => '1'
属性设置柱族可以使该柱族支持复制。 我们在步骤 3 中实现了这一点,方法是更改现有表,或者创建一个复制范围设置为 1
的新表。
对于对等群集,我们在步骤 4 中执行了相同的过程-为那些复制族启用复制支持并创建具有完全相同名称的相同表。
在步骤 1 和步骤 4 之间完成准备之后,我们在步骤 5 中将复制对等群集添加到主群集,以便随后可以向其发送编辑内容。 复制对等点由 ID(在我们的示例中为 1)和集群的 ZooKeeper 仲裁的完整描述(格式为 hbase.zookeeper.quorum:hbase.zookeeper.client.port:zookeeper.znode.parent
,如 server,server2,server3:2181:/hbase
)标识。 之后,我们开始将编辑记录实际传送到对等集群。
为了测试我们的复制设置,我们将一些数据放入表中,一段时间后,正如您从对等群集上的 scan
命令的输出中看到的那样,数据已经正确地传送到对等群集。 虽然在只查看几行时很容易做到这一点,但更好的方法是使用 verifyrep
命令在两个表之间进行比较。 以下是 verifyrep
命令的帮助说明:
hac@client1$ $HADOOP_HOME/bin/hadoop jar $HBASE_HOME/hbase-0.92.1.jar verifyrep
Usage: verifyrep [--starttime=X] [--stoptime=Y] [--families=A] <peerid> <tablename>
Options:
starttime beginning of the time range
without endtime means from starttime to forever
stoptime end of the time range
families comma-separated list of families to copy
Args:
peerid Id of the peer used for verification, must match the one given for replication
tablename Name of the table to verify
Examples:
To verify the data replicated from TestTable for a 1 hour window with peer #5
$ bin/hbase org.apache.hadoop.hbase.mapreduce.replication.VerifyReplication --starttime=1265875194289 --stoptime=1265878794289 5 TestTable
从 hadoop jar
命令运行 verifyrep
,使用对等体 ID(在步骤 5 中用于建立复制流)和表名的参数,将启动 MapReduce 作业,以比较原始表和复制表中的每个单元。 使用 verifyrep
命令提供两个计数器-Verifier.Counters.GOODROWS
和 Verifier.Counters.BADROWS
。 好行意味着两个表之间的行是完全匹配的,而坏行是不匹配的行。 当我们的数据被成功复制时,我们得到了以下输出:
12/01/09 16:50:51 INFO mapred.JobClient: GOODROWS=2
如果有一些错误的行,请检查 MapReduce 作业的映射日志以了解原因。
最后,我们停止复制并从主群集中删除对等项。 停止复制仍将完成将所有排队的编辑传送到对等方,但不接受进一步处理。
还有更多...
要深入了解用于 HBase 集群复制的体系结构,请参阅以下文档-http://hbase.apache.org/replication.html。
五、监控与诊断
在本章中,我们将重点介绍:
- 显示 HBase 表的磁盘利用率
- 设置 Ganglia 以监视 HBase 群集
- OpenTSDB-使用 HBase 监视 HBase 群集
- 设置 Nagios 以监视 HBase 进程
- 使用 Nagios 检查 Hadoop/HBase 日志
- 用于报告群集状态的简单脚本
- 热区-写入诊断
简介
监视 HBase 群集的状态以确保其按预期运行是至关重要的。 除了单独考虑每台服务器的情况外,监视分布式系统的挑战还在于您还需要查看集群的整体状态。
HBase 继承了 Hadoop 指标框架的监控 API。 它公开了大量的指标,提供了集群的洞察信息。 这些指标随后被配置为公开其他监视系统,如 Ganglia 或 OpenTSDB,以收集它们并使其通过图形可见。 Ganglia/OpenTSDB 图帮助我们理解集群的洞察力,包括单个服务器和整个集群。
图形有助于获得历史状态的概述,但我们还需要一种机制来检查群集的当前状态,并在群集出现问题时向我们发送通知或采取一些自动操作。 Nagios 是这类监控任务的一个很好的解决方案。 Nagios 位于监控系统的中心,用于监视集群资源并提醒用户。
我们将介绍如何使用 Ganglia、OpenTSDB、Nagios 和其他工具监视和诊断 HBase 集群。 我们将从一个简单的任务开始,显示 HBase 表的磁盘利用率。 我们将安装和配置 Ganglia 以监视 HBase 指标,并使用 Ganglia 图显示一个示例。 我们还将设置 OpenTSDB,它类似于 Ganglia,但可伸缩性更好,因为它构建在 HBase 之上。
我们将设置 Nagios 来检查我们想要检查的所有内容,包括与 HBase 相关的守护进程运行状况、Hadoop/HBase 日志、HBase 不一致、HDFS 运行状况和空间利用率。
在最后一个菜谱中,我们将描述一种诊断和修复常见热点区域问题的方法。
显示 HBase 表的磁盘利用率
在本食谱中,我们将显示以下简单问题的答案:
HBase 或单个 HBase 表在 HDFS 上使用了多少空间?
这是一项非常简单的任务,但您可能需要经常回答这个问题。 我们会给您一些小费,让您轻松一点。
做好准备
启动您的 HBase 群集并登录到您的 HBase 客户端节点。 我们假设 HDFS 上的 HBase 根目录为 /hbase
。
怎么做……
显示 HBase 表磁盘利用率的说明如下:
-
通过执行以下命令显示所有 HBase 对象的磁盘利用率:
$ $HADOOP_HOME/bin/hadoop fs -dus /hbase hdfs://master1:8020/hbase 1016842660
-
通过执行以下命令显示特定 HBase 表(
hly_temp
)的磁盘利用率:$ $HADOOP_HOME/bin/hadoop fs -dus /hbase/hly_temp hdfs://master1:8020/hbase/hly_temp 54738763
-
通过执行以下命令,显示 HBase 表的区域及其磁盘利用率的列表:
$ $HADOOP_HOME/bin/hadoop fs -du /hbase/hly_temp Found 3 items 27709729 hdfs://master1:8020/hbase/hly_temp/ 0b593d9dc044f1fe011ec0c902f68fc5 13545245 hdfs://master1:8020/hbase/hly_temp/ 5740a39d9eaa59c4175487c14e0a272a 13483789 hdfs://master1:8020/hbase/hly_temp/ 5ef67f6d2a792fb0bd737863dc00b6a7
它是如何工作的.
所有 HBase 对象都存储在 HDFS 上的 HBase 根目录下。 HBase 根目录由 HBase 配置文件(hbase-site.xml
)中的 hbase.rootdir
属性配置。 默认情况下,根目录为 /hbase
。 因此,所有 HBase 对象的磁盘使用率等于 HBase 根目录下的 HDFS 使用率。
在步骤 1 中,为了显示 HDFS 的使用情况,我们运行 hadoop fs -dus
命令,将我们的 HBase 根目录(/HBase)传递给它。 /hbase
目录下的空间量以字节为单位显示在输出中。
在步骤 2 中,为了显示 hly_temp
表的磁盘利用率,我们只将 /hbase/hly_temp
传递给 hadoop fs -dus
命令。 这是可能的,因为特定 HBase 表的对象存储在 HDFS 上的 ${hbase.rootdir}/<table_name>
目录下。
第三步有点不同。 我们运行 hadoop fs -du
命令以显示 hly_temp
表的区域及其磁盘利用率的列表。 hadoop fs -du
命令类似于 hadoop fs -dus
命令,但会显示特定路径下每个目录/文件的空间量。
还有更多...
正如您从输出中看到的,磁盘利用率是以字节为单位显示的,这对用户不友好,特别是当它是一个大数字时。 下面是一个简单的 JRuby 脚本,用于将输出转换为人类可读的格式:
-
创建包含以下内容的
dus.rb
文件:$ vi dus.rb include Java import org.apache.hadoop.util.StringUtils path = ARGV[0] dus = %x[$HADOOP_HOME/bin/hadoop fs -dus #{path}] splited = dus.split byteDesc = StringUtils.byteDesc(splited[1].to_i) puts splited[0] + "\t" + byteDesc
-
Run
dus.rb
to show HBase's disk utilization with a humanly readable output:$ $HBASE_HOME/bin/hbase org.jruby.Main dus.rb /hbase hdfs://master1:8020/hbase 969.74 MB
在
dus.rb
中,我们只需执行相同的hadoop fs -dus
命令来确定 HBase 正在使用的空间量。 之后,我们使用org.apache.hadoop.util.StringUtils
类将值转换为友好的格式,然后将转换后的值显示为输出。
设置 Ganglia 以监视 HBase 群集
HBase 操作任务中最重要的部分之一是监视集群并确保其按预期运行。 HBase 从 Hadoop 继承其监控 API。 它公开了许多指标,这些指标提供了集群当前状态的洞察信息,包括基于区域的统计信息、RPC 详细信息以及Java Virtual Machine(JVM)内存和垃圾收集数据。
然后将这些指标配置为向 JMX 和 Ganglia 公开,从而使指标通过图形可见。 Ganglia 是监控大规模集群的推荐工具。 Ganglia 本身是一个可伸缩的分布式系统;据说它能够处理具有 2000 个节点的集群。
在本食谱中,我们将描述如何使用 Ganglia 监视 HBase 集群。 我们将在集群中的每个节点上安装Ganglia Monitoring Daemon(Gmond),它将收集该节点的服务器和 HBase 指标。 这些指标随后被轮询到Ganglia Meta Daemon(Gmetad)服务器,在那里使用轮询数据库工具(RRDtool)计算指标并将其保存在轮询的时间序列数据库中。 我们在这里只设置一个 Gmetad 节点,但也可以向外扩展到多个 Gmetad 节点,在每个 Gmetad 节点上聚合其分配的 Gmond 节点的结果。
我们还将在同一个 Gmetad 服务器上安装 PHP Web 前端,这样我们就可以从 Web 浏览器访问 Ganglia。 最后,我们将描述如何配置 HBase 以将其指标公开给 Ganglia。
做好准备
除了集群中的服务器之外,您还需要一个 Gmetad 服务器来在其上运行 Gmetad 守护进程。 在我们的演示中,我们将使用 master2
作为 Gmetad 服务器。
添加 ganglia
用户作为 Ganglia 守护进程的所有者:
# adduser --disabled-login --no-create-home ganglia
在您想要监视的所有节点上,从http://downloads.sourceforge.net/project/ganglia/ganglia%20monitoring%20core/3.0.7%20%28Fossett%29/ganglia-3.0.7.tar.gz下载 Ganglia-3.0.7。
解压下载的 tarball。 您需要所有服务器上的 root 权限才能安装 Ganglia。
怎么做……
设置 Gmond 的说明如下;需要在要监视的所有节点上执行:
-
安装依赖项:
# apt-get install build-essential libapr1-dev libconfuse-dev libexpat1-dev python-dev
-
构建并安装 Ganglia:
# cd ganglia-3.0.7 # ./configure # make # make install
-
生成默认配置文件:__T0++
-
对生成的配置文件进行以下更改:
# vi /etc/gmond.conf globals { user = ganglia } cluster { name = "hbase-cookbook" } udp_send_channel { # mcast_join = 239.2.11.71 host = master2 port = 8649 # ttl = 1 } udp_recv_channel { # mcast_join = 239.2.11.71 port = 8649 # bind = 239.2.11.71 }
-
启动 Gmond:
# gmond
- 按照以下说明设置 Gmetad。 仅在 Gmetad 节点上执行它们。
-
安装依赖项:
# apt-get install build-essential libapr1-dev libconfuse-dev libexpat1-dev python-dev librrd2-dev
-
构建并安装 Gmetad:
# cd ganglia-3.0.7 # ./configure --with-gmetad # make # make install
-
将示例配置文件(
gmetad.conf
)复制到其默认位置:# cp ganglia-3.0.7/gmetad/gmetad.conf /etc/gmetad.conf
-
找到并更改以下设置,如下面的代码片断所示:
# vi /etc/gmetad.conf data_source "hbase-cookbook" master2 gridname "hbase-cookbook" setuid_username "ganglia"
-
为循环数据库创建目录,以存储收集的数据:
```scala
# mkdir -p /var/lib/ganglia/rrds
# chown -R ganglia:ganglia /var/lib/ganglia
```
- 启动 Gmetad:
```scala
# gmetad
```
* 以下是设置 Ganglia Web 前端的说明。 仅在 Web 前端节点上执行这些步骤。 它通常与 Gmetad 节点相同。
- 安装依赖项:
```scala
# apt-get install rrdtool apache2 php5-mysql libapache2-mod-php5 php5-gd
```
- 将 Ganglia Web 前端的 PHP 文件复制到存储 Apache Web 文件的位置:
```scala
# cp -r ganglia-3.0.7/web /var/www/ganglia
```
- Restart the Apache web server:
```scala
# /etc/init.d/apache2 restart
```
* You should be able to access your Ganglia web frontend page at: `http://master2/ganglia/.`
Ganglia 前端页面包含 Ganglia 收集的所有指标图,如以下屏幕截图所示:

* 最后,以下是 HBase 将其指标导出到 Ganglia 的说明:
- 编辑 HBase 指标配置文件(
hadoop-metrics.properties
),如下所示:
```scala
hadoop@master1$ vi $HBASE_HOME/conf/hadoop-metrics.properties
hbase.extendedperiod = 3600
hbase.class=org.apache.hadoop.metrics.ganglia.GangliaContext
hbase.period=10
hbase.servers=master2:8649
jvm.class=org.apache.hadoop.metrics.ganglia.GangliaContext
jvm.period=10
jvm.servers=master2:8649
rpc.class=org.apache.hadoop.metrics.ganglia.GangliaContext
rpc.period=10
rpc.servers=master2:8649
```
- Sync
hadoop-metrics.properties
to the slave nodes and restart HBase. You will find that HBase metrics are now collected by Ganglia automatically, as shown in the following screenshot:

它是如何工作的.
在撰写本书时,HBase 只支持 Ganglia 3.0.x 版本。 这是因为在较新的 3.1.x 版本中更改了网络协议。 这就是为什么我们必须从源代码安装 Ganglia,而不是使用包管理系统。
我们通过在步骤 3 中执行 gmond --default_config
命令生成了一个默认配置文件。该文件在 /etc/gmond.conf
处创建,这是 gmond 守护进程将读取的默认配置文件。
在步骤 4 中,我们将 gmond 守护进程的所有者设置为 ganglia
用户,并将群集名称设置为 hbase-cookbook
。 我们将 Gmond 之间使用的默认 UDP 多播通信方法配置为使用单播消息,而不是使用默认的 UDP 多播通信方法。 这是通过注释掉多播地址和生存时间(TTL)设置,并在 gmond.conf
文件中添加专用主 Gmond 节点(master2
)来实现的。 我们选择单播是因为 Amazon EC2 环境不支持多播。 另一个原因是,使用单播对于大型集群是有好处的,比如拥有 100 个以上节点的集群。
需要在要监视的所有节点上安装并启动 Gmond。 默认情况下,Gmond 监视该节点上的一些基本服务器指标,例如平均负载和 CPU/内存/磁盘使用率。
要安装 Gmetad,我们添加了 --with-gmetad
选项,以便从源代码编译 Gmetad。 在步骤 8 和 9 中,我们将示例配置文件(gmetad.conf
)复制到默认位置 /etc/gmetad.conf
,并添加了一些更改,包括设置 Gmetad 守护进程的数据源、网格名称和所有者用户。 请注意,当使用单播时,您需要将 data_source
设置为在 Gmond 设置中配置的专用 Gmond 服务器。
在步骤 10 中,我们创建了默认目录(/var/lib/ganglia/rrds
),Gmetad 将收集的数据存储在循环数据库中。 之后,我们在 Gmetad 服务器(master2
)上启动了 gmetad 守护进程。
Ganglia 设置的最后一部分是 PHP Web 前端。 它通常设置在相同的 Gmetad 节点上,以便它可以访问由 Gmetad 守护进程创建的循环数据库。 这一点在前面说明的步骤 12 到 14 中进行了说明。 您现在应该能够访问位于 http://master2/ganglia/
的 Ganglia 网页,其中 master2
是运行 Gmetad 的主机。 您将只看到 Gmond 节点的基本图表,因为 HBase 还没有配置为向 Ganglia 公开其指标。
它是在 hadoop-metrics.properties
配置文件中设置的,用于集成 HBase 和 Ganglia。 请勿更改文件名,因为 HBase 从 Hadoop 继承其监控 API。 在该文件中,我们指示 HBase 使用 org.apache.hadoop.metrics.ganglia.GangliaContext
类将服务器进程收集的指标发送到在 master2:8649
上运行的 gmond 守护进程,这是我们在前面指定的专用 gmond 节点。
将 hadoop-metrics.properties
同步到从节点,然后重新启动 HBase;您现在可以在 Ganglia 网页上找到 HBase 指标图。
有一些图表您应该特别注意,如下图所示:
- CPU 和内存使用率
- JVM GC 计数和时间
- HBase RegionServer 压缩队列大小
- HBase RegionServer 刷新队列大小
例如,压缩队列大小指示区域服务器中有多少存储已排队等待压缩。 通常这应该相当低(最多几十个 RegionServer)。 当服务器过载或出现 I/O 问题时,您将在图表上看到一个峰值。
下面的屏幕截图显示了在几轮繁重的写入高峰之后,区域服务器的 CPU 使用率、JVMGC 时间和 HBase 压缩队列大小。 如您所见,CPU 使用率和较长的垃圾收集时间表明服务器超载。 这会显著增加压缩队列大小。
还有更多...
Ganglia 也可以用来监控 Hadoop。 其设置类似于我们在本食谱中所描述的。 Ganglia 可以监控以下 Hadoop 指标:
- HDFS 指标
- MapReduce 指标
- JVM 指标
- Hadoop RPC 指标
另请参阅
在本章中:
- OpenTSDB-使用 HBase 监控 HBase 群集
OpenTSDB-使用 HBase 监控 HBase 群集
OpenTSDB是构建在 HBase 之上的高度可伸缩的时间序列数据库(TSDB)。 与 Ganglia 类似,OpenTSDB 可用于监视各种系统,包括 HBase。 与将数据存储在 RRDtool 中的 Ganglia 相比,OpenTSDB 利用 HBase 的可伸缩性对其进行更大规模的监控。 以下是 OpenTSDB 主页(http://opentsdb.net/)的介绍:
由于 HBase 的可伸缩性,OpenTSDB 允许您以高速率(每隔几秒钟)从数千台主机和应用收集数千个指标。 OpenTSDB 永远不会删除或下采样数据,可以轻松存储数十亿个数据点。
要使用 OpenTSDB,我们需要编写一些小脚本来从我们的系统中收集数据,并每隔几秒钟将它们推送到 OpenTSDB 中。 TCollector是一个用于从 Linux、MySQL、Hadoop、HBase 等收集 OpenTSDB 指标的框架。 有趣的是,OpenTSDB 使用 HBase(存储指标)来监控 HBase 本身。
在本食谱中,我们将介绍如何设置 OpenTSDB 和 TCollector,并使用它们来监控我们的 HBase 集群。 在演示中,我们将只启动一个时序守护进程(TSD)。 但是,可以在多台服务器上运行更多 TSD,因为它们彼此独立。
做好准备
您需要一台服务器来运行 TSD。 在我们的演示中,我们将使用 master2
作为 TSD 服务器。
在 TSD 服务器(master2
)上,添加 tsdb
用户作为 TSD 守护进程的所有者。 我们将所有 OpenTSDB 文件和数据存储在 /usr/local/opentsdb:
下
root# adduser --disabled-login --no-create-home tsdb
root# mkdir /usr/local/opentsdb
root# chown tsdb:tsdb /usr/local/opentsdb
我们将在 /usr/local/tcollector
目录下安装 TCollector。
您需要在 TSD 服务器和要监视的所有节点(如 Hadoop DataNodes 和 HBase RegionServers)上拥有 root 权限。
最后,确保您的 HBase 集群正在运行。 我们假设您的 HBASE_HOME
环境变量是 /usr/local/hbase/current
,并且 ZooKeeper 仲裁运行在 master1:2181
。
怎么做……
OpenTSDB 和收集器的设置说明如下:
-
在 TSD 服务器上设置 OpenTSDB:首先,我们将在 TSD 服务器(
master2
)上安装和配置 OpenTSDB,如下所示:-
安装依赖项:
hac@master2$ sudo apt-get install gnuplot hac@master2$ sudo apt-get install autoconf
-
从https://github.com/stumbleupon/opentsdb下载最新版本的 OpenTSDB。 我们假设下载的目录是
$TSDB_INSTALL
。 要构建和安装 OpenTSDB,请执行以下命令:hac@master2$ cd $TSDB_INSTALL hac@master2$ PATH=$PATH:$JAVA_HOME/bin hac@master2$ ./build.sh hac@master2$ cd build hac@master2$ sudo make install
-
在 HBase 中创建 OpenTSDB 表:
hac@master2$ export COMPRESSION=LZO hac@master2$ export HBASE_HOME=/usr/local/hbase/current hac@master2$ sh $TSDB_INSTALL/src/create_table.sh
- 此步骤要求 HBase 群集支持 LZO 压缩。 如果您的集群不支持 LZO,请不要调用第一个命令。 有关如何向 HBase 添加 LZO 支持的信息,请参阅第 8 章基本性能调整中的使用压缩配方。
-
创建 OpenTSDB 存储其文件的目录,然后启动 TSD 守护进程:
hac@master2$ sudo su tsdb tsdb@master2$ cd /usr/local/opentsdb tsdb@master2$ PATH=$PATH:$JAVA_HOME/bin tsdb@master2$ tsdtmp=/usr/local/opentsdb/var tsdb@master2$ mkdir -p "$tsdtmp" tsdb@master2$ tsdstatic=/usr/local/share/opentsdb/static tsdb@master2$ nohup tsdb tsd --port=4242 --staticroot="$tsdstatic" --cachedir="$tsdtmp" --zkquorum=master1:2181 &
-
要验证我们的安装,请通过执行以下命令创建 OpenTSDB 自己的指标:
tsdb@master2$ echo stats | nc -w 1 localhost 4242 \ | awk '{ print $1 }' | sort -u \ | xargs tsdb mkmetric metrics tsd.compaction.count: [0, 0, 2] metrics tsd.connectionmgr.connections: [0, 0, 3] metrics tsd.connectionmgr.exceptions: [0, 0, 4] metrics tsd.hbase.latency_50pct: [0, 0, 5] metrics tsd.hbase.latency_75pct: [0, 0, 6] metrics tsd.hbase.latency_90pct: [0, 0, 7] metrics tsd.hbase.latency_95pct: [0, 0, 8] metrics tsd.hbase.meta_lookups: [0, 0, 9] metrics tsd.hbase.root_lookups: [0, 0, 10] metrics tsd.http.graph.requests: [0, 0, 11] metrics tsd.http.latency_50pct: [0, 0, 12] metrics tsd.http.latency_75pct: [0, 0, 13] metrics tsd.http.latency_90pct: [0, 0, 14] metrics tsd.http.latency_95pct: [0, 0, 15] metrics tsd.jvm.ramfree: [0, 0, 1] metrics tsd.jvm.ramused: [0, 0, 16] metrics tsd.rpc.errors: [0, 0, 17] metrics tsd.rpc.exceptions: [0, 0, 18] metrics tsd.rpc.received: [0, 0, 19] metrics tsd.uid.cache-hit: [0, 0, 20] metrics tsd.uid.cache-miss: [0, 0, 21] metrics tsd.uid.cache-size: [0, 0, 22]
-
创建一个简单的脚本来收集 OpenTSDB 公开的统计数据,并使用 OpenTSDB 监视自身:
tsdb@master2$ vi tsdb-status.sh #!/bin/bash INTERVAL=15 while :; do echo stats || exit sleep $INTERVAL done | nc -w 30 localhost 4242 \ | sed 's/^/put /' \ | nc -w 30 localhost 4242 tsdb@master2$ chmod 755 tsdb-status.sh tsdb@master2$ nohup ./tsdb-status.sh &
-
By opening your browser and accessing
http://master2:4242/
, you will get the OpenTSDB web UI (make sure the 4242 port is open to your client). Enter a time period of the graph to show in the From and To fields, and metric name in the Metric (typetsd.
, and metrics that start withtsd
. will pop up) field, you should get a graph, as shown in the following screenshot:
-
-
设置收集器:对于您想要监控的所有节点,例如 Hadoop DataNodes 和 HBase RegionServers,我们需要在它们上设置收集器。 需要在要监视的每个节点上执行以下步骤:
-
从http://github.com/stumbleupon/tcollector下载最新版本的收集器,并执行以下命令:
$ sudo mv tcollector /usr/local $ cd /urs/local/tcollector
-
有一个收集器,它附带了一个收集器,用于从 HBase RegionServer 收集指标。 Grep
hbase_regionserver_jmx.py
中的以下行并修改它们以适合您的环境:$ sudo vi collectors/0/hbase_regionserver_jmx.py #USER = "hadoop" USER = "your_running_user" CLASSPATH = [ # "/usr/lib/jvm/java-6-sun/lib/tools.jar", "/your/java_home/lib/tools.jar", ] jmx = subprocess.Popen( # ["java", "-enableassertions", ... ["/your/java_home/bin/java", "-enableassertions", ...
-
在收集器的启动脚本中,添加运行 TSD 守护程序的服务器的 DNS 名称:
$ sudo vi startstop TSD_HOST=master2
-
在您的 TSD 服务器上,使用
--auto-metric
选项重新启动 TSD 守护进程:tsdb@master2$ nohup tsdb tsd --port=4242 --staticroot="$tsdstatic" --cachedir="$tsdtmp" --zkquorum=master1:2181 --auto-metric &
-
Start Tcollector to collect metrics and send them to TSD:
$ sudo /usr/local/tcollector/startstop start
一段时间后,您应该能够在 OpenTSDB web UI 上查看 TCollector 收集的指标。 下面的屏幕截图显示了我们的 HBase 集群由收集器收集的指标:
-
它是如何工作的.
在前面说明的步骤 1 和 2 中,我们安装了 OpenTSDB 及其依赖项。 因为 OpenTSDB 构建在 HBase 之上,所以我们需要首先在 HBase 中创建 OpenTSDB 的表。 在步骤 3 中,我们将 OpenTSDB 配置为创建支持 LZO 压缩的表。 这需要 HBase 群集支持 LZO。 您还可以使用其他 HBase 支持的压缩,如 Snappy 和 Gzip,但只要确保您的 HBase 集群上有可用的压缩即可。
之后,我们通过设置 HBASE_HOME
环境变量来指定 HBase 的安装位置。 然后,我们调用 create_table.sh
在 HBase 中实际创建表。 此脚本随 OpenTSDB 一起提供;它创建两个表-TSDB 和 tsdb-uid
。 您可以在 HBase 主用户界面上找到这些表。
在步骤 4 中,我们创建了必要的目录,并启动了 OpenTSDB,并提供了使用这些目录的选项。 我们还传递了 --zkquorum=master1:2181
选项来指定放置动物园管理员仲裁的位置。
为了验证我们的安装,我们在步骤 5 和 6 中将 OpenTSDB 设置为收集自己的指标。默认情况下,我们需要使用 tsdb mkmetric
命令创建一个指标,然后才能将该指标存储到 OpenTSDB 中。 这是为了防止制定太多指标而设计的,因为过多的指标会使指标名称空间变得一团糟。 如我们在步骤 11 中所示,使用 --auto-metric
选项启动 TSD 守护进程会将 OpenTSDB 配置为自动生成它收到的指标。
TSD 守护进程接受 stats
命令以公开其自己的指标。 在步骤 5 中,我们使用这一点来制定指标。
在步骤 6 中,我们创建了一个简单的脚本,使用 stats
命令定期获取 TSD 守护进程的指标,然后使用 put
命令将它们放入 OpenTSDB。 如步骤 7 所示,收集的 OpenTSDB 自己的指标在其 Web UI 上显示为图形。
在步骤 8 到 12 中,我们在要监视的每台服务器上设置了收集器。 TCollector 从 Hadoop、HBase 等收集指标,并将它们发送到 TSD 守护进程。 在收集器的启动脚本(startstop
)中,我们将 TSD_HOST
设置为 TSD 服务器的 DNS 名称,以指定我们应该将指标发送到哪个服务器。
在步骤 11 中,我们使用 --auto-metric
选项重新启动了 TSD 守护进程,以便在 OpenTSDB 中自动创建从 TCollector 接收的指标。 正如我们前面提到的,自动创建指标不被认为是一种良好的做法。 您应该知道系统正在收集哪些指标,并手动创建这些指标以确保指标名称空间是干净的。
最后,我们启动了 TCollector,开始收集指标并将其发送到 TSD 守护进程。 指标将存储在 HBase 中,并在 OpenTSDB 的 Web 用户界面上显示为图表。
还有更多...
TSDash是 OpenTSDB 的替代 Web UI/仪表板。 TSDash 提供了与 OpenTSDB web UI 相同的功能,但用户界面略有不同。 以下屏幕截图显示了 TSDash 的 UI 和示例图形:
TSDash 可在https://github.com/facebook/tsdash/获得。
设置 Nagios 以监视 HBase 进程
监控集群中的 HBase 相关进程是运行 HBase 的重要组成部分。 基本监控是通过对 HBase 进程运行运行状况检查并在任何进程关闭时通知管理员来完成的。
Nagios是一款流行的开源监控软件,用于监视主机、服务和资源,并在出现故障和再次恢复时向用户发出警报。 定制模块可以很容易地扩展 Nagios,这些模块被称为插件。 check_tcp
插件随 Nagios 安装一起提供。 我们可以使用此插件向 Hadoop/HBase 守护进程的 RPC 端口发送 ping 命令,以检查该守护进程是否处于活动状态。
在本食谱中,我们将设置一个运行 Nagios 的监视服务器来监视整个集群中所有与 HBase 相关的进程。 我们将 Nagios 配置为在任何 Hadoop/HBase/zooKeeper 进程关闭时向我们发送电子邮件通知。
做好准备
您需要一个监控服务器来运行 Nagios。 我们假设您使用的是安装最少的 Debian 机器。 监控服务器需要能够通过正确的 TCP 端口与集群中的所有机器通信。 在我们的演示中,我们将使用 master2
作为监控服务器。
启动要监视的 HBase 集群,然后登录到监视服务器。 确保您在监视服务器上具有 root 权限。
怎么做……
安装和配置 Nagios 以监视您的 HBase 集群的说明如下:
-
使用 Linux 发行版的包管理工具在监视服务器上安装 Nagios:
root@master2# apt-get install nagios3 nagios-plugins
- 在安装过程中,会要求您输入
nagiosadmin
密码和 Samba 工作组设置;输入您的密码,并将 Samba 工作组设置设为默认设置。
- 在安装过程中,会要求您输入
-
You should be able to access the Nagios admin page from your web browser using the URL
http://<monitor_host>/nagios3/
.系统将要求您输入帐号和密码;使用 nagiosadmin 和您在安装中设置的密码登录到 Nagios 管理页面。 Nagios 管理页面类似于以下屏幕截图:
-
将 HBase 集群中的所有主机添加到 Nagios 主机配置文件:
root@master2# vi /etc/nagios3/conf.d/hosts_nagios2.cfg #master1 define host{ use generic-host host_name master1 alias master1 address 10.160.41.205 hostgroups masters } #slave1 define host{ use generic-host host_name slave1 alias slave1 address 10.168.71.224 hostgroups slaves } #slave2 define host{ use generic-host host_name slave2 alias slave2 address 10.168.107.28 hostgroups slaves } #slave3 define host{ use generic-host host_name slave3 alias slave3 address 10.160.39.197 hostgroups slaves }
-
通过执行以下命令将
masters
和slaves
主机组添加到 Nagios:root@master2# vi /etc/nagios3/conf.d/hostgroups_nagios2.cfg # A list of your master servers define hostgroup { hostgroup_name masters alias Master servers members master1 } # A list of your slave servers define hostgroup { hostgroup_name slaves alias Slave servers members slave1,slave2,slave3 }
-
配置 Nagios 以监视主节点上的主守护进程。 我们假设 NameNode、ZooKeeper 和 HMaster 守护进程在同一主节点(
master1
)上运行。root@master2# vi /etc/nagios3/conf.d/services_nagios2.cfg # check that NameNode is running define service{ use generic-service host_name master1 normal_check_interval 1 service_description NameNode health check_command check_tcp!8020! } # check that ZooKeeper is running define service{ use generic-service host_name master1 normal_check_interval 1 service_description Zookeeper health check_command check_tcp!2181! } # check that HMaster is running define service{ use generic-service host_name master1 normal_check_interval 1 service_description HMaster health check_command check_tcp!60000! }
-
将 Nagios 配置为监视所有从节点上的从守护进程:
root@master2# vi /etc/nagios3/conf.d/services_nagios2.cfg # check that DataNodes are running define service{ use generic-service hostgroup_name slaves normal_check_interval 1 service_description DataNode health check_command check_tcp!50010! } # check that HRegionServers are running define service{ use generic-service hostgroup_name slaves normal_check_interval 1 service_description HRegionServer health check_command check_tcp!60020! }
-
更改通知电子邮件地址;将其设置为您自己的地址:
root@master2# vi /etc/nagios3/conf.d/contacts_nagios2.cfg define contact{ contact_name root alias Root service_notification_period 24x7 host_notification_period 24x7 service_notification_options w,u,c,r host_notification_options d,r service_notification_commands notify-service-by-email host_notification_commands notify-host-by-email email address@example.com }
-
安装 Postfix 电子邮件服务器,让 Nagios 发送电子邮件:
root@master2# apt-get install postfix
- 在安装过程中,将 Postfix 配置为Internet Site,并设置将从其接收 Nagios 邮件的域。
-
通过执行以下命令验证配置更改:
root@master2# /usr/sbin/nagios3 -v /etc/nagios3/nagios.cfg Total Warnings: 0 Total Errors: 0
-
If everything is fine, restart Nagios to apply the configuration changes:
```scala
root@master2# /etc/init.d/nagios3 restart
```
* 如下面的 Nagios 管理页面屏幕截图所示,Nagios 开始检查我们刚刚配置的与 HBase 相关的进程。 所有主机、Hadoop/HBase/zooKeeper 守护程序及其状态现在都显示在 Nagios 管理页面上:

- To test our Nagios settings, we stop the HRegionServer daemon from one of the slave nodes:
```scala
hadoop@slave1$ hbase-daemon.sh stop regionserver
```
* 一段时间后,Nagios 将检测到停机的区域服务器,并在 Nagios 管理页面上显示**Critical**状态,如以下屏幕截图所示:

* 您还将收到一封来自 Nagios 的警报电子邮件,报告某个区域服务器已关闭,如以下屏幕截图所示:

- 现在再次重新启动 HRegionServer 守护进程,以测试 Nagios 的恢复通知:
```scala
hadoop@slave1$ hbase-daemon.sh start regionserver
```
* 您会发现状态再次更改为**OK**。 您还将收到恢复电子邮件通知。
它是如何工作的.
Nagios 可用于许多 Linux 发行版。 使用发行版的包管理系统可以很容易地安装 Nagios。
Nagios 有一个管理网页,您可以在其中看到正在监视的集群的当前状态。 您可以按主机、主机组、服务和服务组查看状态。 您还可以在管理页面上生成非常详细的可用性和趋势报告。 管理页面的 URL 是 http://<monitor_host>/nagios3/
,其中 monitor_host
是监视服务器的主机名。
在步骤 3 中,我们将集群的主机定义添加到 Nagios 主机配置文件(hosts_nagios2.cfg
),以便 Nagios 可以监控这些远程节点。 在步骤 4 中,我们还设置了两个主机组: masters
和 slaves
主机组。 此设置允许我们以主机组为基础监视服务器。
在步骤 5 中,我们向 services_nagios2.cfg
文件添加了几个服务定义,以配置 Nagios 来监视主节点(master1
)上运行的主守护进程。 服务由 check_command
监视,其中我们使用预定义的 check_tcp
命令,将 NameNode/zooKeeper/HMaster 守护进程的 RPC 端口传递给它。
以类似的方式,我们定义了服务来监视在从节点上运行的从守护进程。 请注意,我们使用了之前在这些服务定义中添加的主机组(slaves
)。
在步骤 7 中,我们通过编辑 contacts_nagios2.cfg
文件将 Nagios 配置为向特定的电子邮件地址发送通知。 在那之后,我们安装了 Postfix 电子邮件服务器,让 Nagios 发送电子邮件。 最后,我们重新启动 Nagios 以应用我们的更改。
Nagios 定期向守护程序的 RPC 端口发送 ping,它正在监视该端口以检查守护程序是否处于活动状态。 如果 Nagios 无法从守护进程获得响应,它将在其管理页面上将守护进程的状态标记为Critical,并向我们指定的地址发送一封警报电子邮件。 此电子邮件包含有关主机名、守护进程状态、何时检测到严重状态以及其他其他信息的详细信息。
还有更多...
使用适当的端口,您还可以配置 Nagios 来监控其他 Hadoop/HBase/zooKeeper 守护进程,比如 MapReduce 的 JobTracker 和 TaskTracker 守护进程。 下表显示了您可能要监视的 Hadoop 守护程序及其默认 RPC/HTTP 端口的快速参考:
|精灵 / 恶魔 / 守护进程 / 后台程序
|
默认端口是默认端口
|
配置文件配置文件
|
配置参数
|
| --- | --- | --- | --- |
| NameNode | 8020 | core-site.xml
| fs.default.name
|
| Second DaryNameNode | 50090 | hdfs-site.xml
| dfs.secondary.http.address
|
| JobTracker | 50030 | mapred-site.xml
| mapred.job.tracker.http.address
|
| HMaster | 60000 | hbase-site.xml
| hbase.master.port
|
| 动物园管理员法定人数 | 2181 | zoo.cfg
| clientPort
|
| 数据节点 | 50010 | hdfs-site.xml
| dfs.datanode.address
|
| 任务跟踪器 | 50060 | mapred-site.xml
| mapred.task.tracker.http.address
|
| HRegionServer | 60020 | hbase-site.xml
| hbase.regionserver.port
|
使用 Nagios 检查 Hadoop/HBase 日志
Hadoop、ZooKeeper 和 HBase 都会生成日志。 这些日志包括有关正常操作、警告/错误输出以及内部诊断数据的信息。 理想的做法是让系统收集和处理所有这些日志,以提取有关集群的有用洞察信息。 最基本的任务是检查这些日志,如果其中显示任何异常情况,就会得到通知。 只需几个简单的步骤,NRPE 和 check_log
Nagios 插件就可以实现这个简单的目标。
NRPE 插件主页(http://exchange.nagios.org/directory/Addons/Monitoring-Agents/NRPE--2D-Nagios-Remote-Plugin-Executor/details)的说明如下:
NRPE 允许您在其他 Linux/Unix 计算机上远程执行 Nagios 插件。 这允许您监视远程机器指标(磁盘使用情况、CPU 负载等)。
使用 NRPE,我们可以在集群节点上远程执行 check_log
Nagios 插件,以检查该节点生成的 Hadoop/HBase 日志。
check_log
插件在指定的日志文件中递增地显示特定的查询词。 如果日志文件中的任何行与查询字匹配, check_log
将向运行在同一节点上的 NRPE 服务器报告严重状态。 然后,NRPE 将其报告给 Nagios 服务器。
由于本书的范围有限,在本食谱中,我们将只演示如何设置 Nagios 来检查 HBase 的主守护进程日志。 您可以轻松地复制它并进行一些更改,以设置 Nagios 来监视其他日志。
做好准备
我们假设您已经正确设置了 Nagios 监控服务器,如前面的菜谱所述。
check_log
插件将保存以前检查的日志,这样它只需要检查新添加的日志。 在主节点上为 check_log
插件创建一个目录,以保存其先前检查的日志:
root@master1# mkdir -p /var/nagios/oldlog
检查您的 HBase log4j 配置文件(log4j.propertie
),找出您的 HBase 生成其日志文件的位置。 我们假设它在这个配方中是 /usr/local/hbase/logs/hbase-hadoop-master-master1.log
。
怎么做……
要使用 Nagios 检查 Hadoop/HBase 日志,请执行以下步骤:
-
在监视服务器上安装 NRPE 插件(
master2
):root@master2# apt-get install nagios-nrpe-plugin
-
在 HBase 群集的主节点上安装 NRPE 服务器和插件:
root@master1# apt-get install nagios-nrpe-server nagios-plugins
-
在主节点上设置 NRPE,以允许监视服务器与其上运行的 NRPE 守护进程通信。 在
nrpe.cfg
文件中更改监视服务器的allowed_hosts
设置,然后重新启动 NRPE 守护进程。 此步骤实现如下:root@master1# vi /etc/nagios/nrpe.cfg #allowed_hosts=127.0.0.1 allowed_hosts=master2 root@master1# /etc/init.d/nagios-nrpe-server restart
-
在我们继续更改配置之前,请检查 NRPE 服务。 从监控服务器执行此操作。
root@master2# /usr/lib/nagios/plugins/check_nrpe -H master1 -c check_users
USERS OK - 2 users currently logged in |users=2;5;10;0
- 输出应如下所示:
-
在主节点上配置
check_log
命令以检查 HBase 的主守护进程日志。 将以下命令添加到主节点上的nrpe.cfg
文件中。 您需要更改命令定义的日志文件路径以适合您的环境。root@master1# vi /etc/nagios/nrpe.cfg command[check_log_hmaster]=/usr/lib/nagios/plugins/ check_log -F /usr/local/hbase/logs/hbase-hadoop- master-master1.log -O/var/nagios/oldlog/hbase-hadoop- master-master1.log -q "ERROR|FATAL"
-
重新启动主节点上的 NRPE 守护进程以应用我们的更改:
root@master1# /etc/init.d/nagios-nrpe-server restart
-
在监控服务器上添加以下服务定义以检查主日志:
root@master2# vi /etc/nagios3/conf.d/services_nagios2.cfg # check HMaster log define service{ use generic-service host_name master1 normal_check_interval 1 service_description HMaster log check max_check_attempts 1 notification_options w,u,c check_command check_nrpe_1arg!check_log_hmaster }
-
Restart Nagios on the monitor server to apply the service definitions:
root@master2# /etc/init.d/nagios3 restart
- 您会发现添加的服务显示在您的 Nagios 管理页面上。 Nagios 开始检查主节点上的 HMaster 守护进程的日志。
-
Test the setup by adding a
FATAL
entry to the master daemon log:hadoop@master1$ echo "FATAL ..." >> /usr/local/hbase/logs/hbase- hadoop-master-master1.log
- 一段时间后,Nagios 会检测到这一情况,并在 Nagios 管理页面上显示Critical状态,如以下屏幕截图所示:
- 您还会收到一封来自 Nagios 的警报电子邮件,报告 HBase 的主守护程序日志中有问题,如以下屏幕截图所示:
它是如何工作的.
首先,我们需要在监视服务器上安装 NRPE 插件,在 HBase 集群中的所有节点上安装 NRPE 服务器和 Nagios 插件。
在步骤 3 中,为了允许监视服务器与远程节点上的 NRPE 守护进程对话,我们编辑了该节点上的 nrpe.cfg
文件,将 allowed_hosts
设置更改为监视服务器(master2
),然后重新启动了 NRPE 守护进程。
在步骤 4 中测试了 NRPE 设置之后,我们在步骤 5 中添加了 check_log_hmaster
命令定义。该命令定义被添加到我们要检查的节点(HBase 主节点)上的 NRPE 配置文件(nrpe.cfg
)中。 check_log_hmaster
命令执行 check_log
插件以检查 HBase 的主守护进程日志。
以下语法显示了 check_log
插件的用法:
check_log <log_file> <old_log_file> <pattern>
我们传递了 HBase 主日志文件的完整路径、临时旧日志文件名和用于匹配异常状态日志的正则表达式。 我们只在日志文件中搜索 ERROR
或 FATAL
,它们是 log4j 记录错误或致命信息时的关键字。
在步骤 7 中,我们在监控服务器上的 services_nagios2.cfg
文件中定义了一个服务。 请注意,在服务定义中:
max_check_attempts
选项应为1
- 不要将
r
选项添加到notification_options
这些都是必要的,因为 check_log
插件的操作方式。
服务的 check_command
选项是 check_nrpe_1arg!check_log_hmaster
,这意味着它使用 NRPE 在远程服务器上执行 check_log_hmaster
命令。
重新启动 HBase 主节点上的 NRPE 服务器和监控服务器上的 Nagios 守护进程。 您会发现 Nagios 开始监视 HBase 主日志,如果日志文件中出现 ERROR
或 FATAL
,则会向我们发送通知。
还有更多...
以下是您可能希望由 Nagios 监视的其他一些日志:
- 主节点上的 NameNode 日志
- 第二个 daryNameNode 记录在主节点上
- 主节点上的 JobTracker 日志
- 动物园管理员登录所有动物园管理员节点
- RegionServer 登录所有区域服务器
- DataNode 记录在所有从节点上
- TaskTracker 记录在所有从节点上
您只需复制我们在此配方中所做的设置并更改日志文件路径,即可配置 Nagios 来监视这些日志。 如果所有这些日志都由 Nagios 监控,则您的 Nagios 管理页面应该类似于以下屏幕截图:
另请参阅
在本章中:
- 设置 Nagios 以监视 HBase 进程
报告群集状态的简单脚本
除了与 HBase 相关的守护进程及其日志的运行状况外,您可能需要监视集群的当前状态的概览。 该状态主要包括:
- 显示 HBase 表是否一致的 HBase
hbck
结果 - 显示 HDFS 是否正常的 Hadoop
fsck
结果 - 剩余的 HDFS 空间
在本食谱中,我们将创建一个 check_hbase
Nagios 插件来执行监视任务。 我们将在集群的主节点上安装我们的 check_hbase
插件,并使用 NRPE Nagios 插件在监控服务器上使用 Nagios 远程执行它。
做好准备
我们假设您已经在监视器和主服务器上安装并配置了 Nagios NRPE 插件。 如果您尚未安装,请参考前面的食谱了解详细的安装说明。
怎么做……
以下是获取 HBase 集群状态并通过 Nagios 对其进行监控的说明:
-
创建如下所示的
check_hbase
脚本:$ vi check_hbase #/bin/bash bin=`dirname $0` bin=`cd $bin;pwd` . $bin/utils.sh HADOOP_HOME=/usr/local/hadoop/current HBASE_HOME=/usr/local/hbase/current DFS_REMAINING_WARNING=15 DFS_REMAINING_CRITICAL=5 ABNORMAL_QUERY="INCONSISTENT|CORRUPT|FAILED|Exception" # hbck and fsck report output=/tmp/cluster-status $HBASE_HOME/bin/hbase hbck >> $output $HADOOP_HOME/bin/hadoop fsck /hbase >> $output # check report count=`egrep -c "$ABNORMAL_QUERY" $output` if [ $count -eq 0 ]; then echo "[OK] Cluster is healthy." >> $output else echo "[ABNORMAL] Cluster is abnormal!" >> $output # Get the last matching entry in the report file last_entry=`egrep "$ABNORMAL_QUERY" $output | tail -1` echo "($count) $last_entry" exit $STATE_CRITICAL fi # HDFS usage dfs_remaining=`curl -s http://master1:50070/dfshealth.jsp |egrep - o "DFS Remaining%.*%" | egrep -o "[0-9]*\.[0-9]*"` dfs_remaining_word="DFS Remaining%: ${dfs_remaining}%" echo "$dfs_remaining_word" >> $output # check HDFS usage dfs_remaining=`echo $dfs_remaining | awk -F '.' '{print $1}'` if [ $dfs_remaining -lt $DFS_REMAINING_CRITICAL ]; then echo "Low DFS space. $dfs_remaining_word" exit_status=$STATE_CRITICAL elif [ $dfs_remaining -lt $DFS_REMAINING_WARNING ]; then echo "Low DFS space. $dfs_remaining_word" exit_status=$STATE_WARNING else echo "HBase check OK - DFS and HBase healthy. $dfs_remaining_word" exit_status=$STATE_OK fi exit $exit_status
-
将脚本复制到主节点上的 Nagios 插件文件夹,并更改其执行权限:
root@master1# cp check_hbase /usr/lib/nagios/plugins root@master1# chmod 755 /usr/lib/nagios/plugins/check_hbase
-
通过编辑
nrpe.cfg
文件root@master1# vi /etc/nagios/nrpe.cfg command[check_hbase]=/usr/lib/nagios/plugins/check_hbase
,将
check_hbase
命令添加到主节点上的 NRPE 配置中 -
重新启动主节点上的 NRPE 守护进程以应用更改:
root@master1# /etc/init.d/nagios-nrpe-server restart
-
在监控服务器上添加
check_hbase
服务定义:root@master2# vi /etc/nagios3/conf.d/services_nagios2.cfg # check hbck, fsck, and HDFS usage define service{ use generic-service host_name master1 normal_check_interval 5 service_description hbck/fsck report and DFS usage check check_command check_nrpe_1arg!check_hbase }
-
Restart Nagios to apply the changes:
root@master2# /etc/init.d/nagios3 restart
- 您会发现此服务已添加到您的 Nagios 管理页面,如以下屏幕截图所示:
-
更改警告/严重阈值或异常查询词以测试插件。 例如,如果您将插件中的
DFS_REMAINING_CRITICAL
更改为非常高的值,您将在一段时间后收到来自 Nagios 的警报通知。 具体实现如下:$ vi check_hbase #DFS_REMAINING_CRITICAL=10 DFS_REMAINING_CRITICAL=40
如果剩余 HDFS 空间低于 40%,Nagios 将检测到此临界状态,如以下屏幕截图所示:
它是如何工作的.
在 check_hbase
脚本中,我们首先执行 hbase hbck
命令来获取 HBase 部署的当前状态报告。 我们还运行 hadoop fsck /hbase
命令来检查 /hbase
目录下的 HDFS 运行状况,该目录是 HBase 的默认根目录。 这些命令的输出被重定向到临时文件。
我们随后 grep 临时文件(如果找到任何 ABNORMAL_QUERY
),并将匹配计数和最后一个匹配条目显示为标准输出,然后以 STATE_CRITICAL
状态退出。
剩余的 HDFS 空间从 HDFS 管理 Web URL 获取。 我们访问 URL,并使用正则表达式从 HTML 输出中提取剩余的空间值。 最后,我们将该值与警告/严重阈值进行比较,并生成脚本的正确输出和退出状态。 输出和退出状态随后将用作 Nagios 的监控结果。
在步骤 2 中,我们只是将脚本复制到 Nagios 插件目录,并使其具有足够的可执行性,可以作为 Nagios 插件安装。 然后,在步骤 3 和 4 中,我们添加了 NRPE 命令定义,并在主节点上重新启动了 NRPE 服务器。
在监控服务器上,我们在 services_nagios2.cfg
文件中定义了 Nagios 服务,然后重新启动 Nagios 守护进程以应用更改。
如您所见,Nagios 开始使用我们的 check_hbase
插件监视集群的状态,如果 hbck
结果、 fsck
结果或 HDFS 使用情况有任何异常,则发送通知。
还有更多...
您可能还希望监视群集的以下状态:
- NameNode 存储文件系统映像的目录(
hdfs-site.xml)
中的dfs.name.di
)中的可用空间 - NameNode 存储事务(编辑)文件的目录中的可用空间(
hdfs-site.xml
中的dfs.name.edits.dir
,默认情况下与dfs.name.dir
相同) - 区域服务器托管的区域数量;上限应为 100 左右
这些可以用类似于我们刚才讨论的方式来完成。
另请参阅
- 在本章中设置 Nagios 以监视 HBase 进程[t1
- 使用 Nagios 检查本章中的 Hadoop/HBase 日志
- HBase hbck-使用管理工具检查第 3 章、中的 HBase 群集的运行状况
热区-写入诊断
随着数据的不断增长,HBase 集群可能会因为表架构或行键设计不佳或其他一些原因而变得不平衡。 许多请求可能会到达表的一小部分区域。 这通常称为热点区域问题。
有两种类型的热点区域问题-热写和热读问题。 热写通常对我们更重要,因为热读将极大地受益于 HBase 内部缓存机制。 热写区问题的一种解决方案是找出热点区域,手动拆分,然后将拆分后的区域分发给其他地域服务器。
HBase 编辑将首先写入区域服务器的预写日志(WAL)。 一旦成功追加 WAL,就会发生对表数据的实际更新。 这种架构使得能够容易地获得近似的写入诊断。
在本食谱中,我们将创建一个 WriteDiagnosis.java
Java 源,以从 Wal 获取写入诊断信息。 在某些情况下,此信息可用于找出 HBase 集群中的热点写入区域。
做好准备
启动您的 HBase 群集并登录到您的 HBase 客户端节点。
怎么做……
诊断您的 HBase 热点区域问题:
-
创建一个
WriteDiagnosis.java
文件,它有一个printWriteDiagnosis()
方法,如下所示:private static void printWriteDiagnosis(String logPath) throws IOException { Configuration conf = HBaseConfiguration.create(); FileSystem fs = FileSystem.get(conf); FileStatus[] regionServers = fs.listStatus(new Path(logPath)); HLog.Reader reader = new SequenceFileLogReader(); Map<String, Long> result = new HashMap<String, Long>(); for (FileStatus regionServer : regionServers) { Path regionServerPath = regionServer.getPath(); FileStatus[] logs = fs.listStatus(regionServerPath); Map<String, Long> parsed = new HashMap<String, Long>(); for (FileStatus log : logs) { System.out.println("Processing: " + log.getPath().toString()); reader.init(fs, log.getPath(), conf); try { HLog.Entry entry; while ((entry = reader.next()) != null) { String tableName = Bytes.toString(entry.getKey().getTablename()); String encodedRegionName = Bytes.toString(entry.getKey().getEncodedRegionName()); String mapkey = tableName + "/" + encodedRegionName; Long editNum = parsed.get(mapkey); if (editNum == null) { editNum = 0L; } editNum += entry.getEdit().size(); parsed.put(mapkey, editNum); } } finally { reader.close(); } } for (String key : parsed.keySet()) { result.put(key, parsed.get(key)); } } System.out.println(); System.out.println("==== HBase Write Diagnosis ===="); for (String region : result.keySet()) { long editNum = result.get(region); System.out.println(String.format("Region: %s Edits #: %d", region, editNum)); } }
-
前一个 Java 源代码的
main()
方法条目如下所示:public static void main(String[] args) { try { if (args.length < 1) { usage(); System.exit(-1); } String logPath = args[0]; printWriteDiagnosis(logPath); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } private static void usage() { System.err.println("Usage: WriteDiagnosis <HLOG_PATH>"); System.err.println("HLOG_PATH:"); System.err .println(" Path on HDFS where HLogs are stored. For example: /hbase/.logs"); }
-
将其打包为 JAR 文件(
hac-chapter5.jar
),并使用hadoop jar
命令运行它:$HADOOP_HOME/bin/hadoop jar hac-chapter5.jar hac.chapter5.WriteDiagnosis /hbase/.logs
-
You will get an output as shown in the following screenshot:
它是如何工作的.
HBase WAL 存储在 HDFS 的 ${hbase.rootdir}/.logs/<region_server>/
目录下。 WAL 以区域服务器为基础进行存储和轮换,这意味着单个 WAL 文件仅包含到同一托管区域服务器的区域的条目,而不包含到其他区域服务器的条目。 WAL 条目包含有关编辑的表格和区域名称以及编辑量的信息。
我们的 WriteDiagnosis.java
文件只需遍历特定路径下的所有 WAL,提取表名、区域名称,并从 WAL 中编辑计数,然后打印每个区域的总编辑计数。
主要逻辑在 printWriteDiagnosis()
方法中完成。 我们首先创建一个 HLog.Reader
实例。 对于每个 WAL 文件,我们调用实例的 init()
方法从 WAL 文件读取数据。 WAL 文件的条目由 HLog.Entry
类表示。 对于每个条目,我们从其键中获取该条目所属的表名和区域名。 我们还使用 HLog.Entry
类的 getEdit().size()
方法获取条目的编辑次数。 有了所有这些信息,我们可以按区域汇总编辑总数。
WriteDiagnosis.java
的 main()
方法只从命令行检查日志路径参数,并将其传递给 printWriteDiagnosis()
方法。
将 Java 源代码打包为 JAR 文件,并使用 hadoop jar
命令运行它。 正如您从输出中看到的, hly_temp
表有许多写请求,而表中的一个区域处理的请求是其他两个区域的两倍。 这是数据分布不均匀的典型情况。 您需要对我们刚刚找到的热点区域采取操作,例如手动将其一分为二。
请注意,输出中显示的编辑次数是近似值。 这是因为,如果 Wal 中的所有编辑都已持久化,则 Wal 可能会被后台线程删除;无法知道该已删除 Wal 中的编辑次数。
但是,对于许多情况,近似的写入诊断是足够好的解决方案。 您可以使用 WriteDiagnosis.java
帮助查找热点写入区域。 当您关闭簇的自动区域分割时,它特别有用。
还有更多...
编译和执行我们的 WriteDiagnosis.java
的一种更方便的方法是通过以下命令:
$ $HBASE_HOME/bin/hbase com.sun.tools.javac.Main WriteDiagno sis.java
$ $HBASE_HOME/bin/hbase WriteDiagnosis /hbase/.logs
另请参阅
- 使用管理工具中的第 3 章、中的使用 HBase Shell 管理群集
六、设备维护和安全
在本章中,我们将重点介绍:
- 启用 HBase RPC 调试级日志记录
- 宽限节点停用
- 将节点添加到群集
- 滚动重启
- 用于管理 HBase 进程的简单脚本
- 简化部署的简单脚本
- 针对 Hadoop 和 HBase 的 Kerberos 身份验证
- 使用 Kerberos 配置 HDFS 安全性
- HBase 安全配置
简介
群集交付运行后,在群集使用期间,维护将是一项必要的持续任务。 典型的维护任务包括查找和纠正故障、更改集群大小、更改配置等。
HBase 最重要的特性之一是它非常容易扩展。 随着您的服务和数据不断增长,您可能需要向群集添加节点。
正常的节点停用和滚动重启也是必要的。 最大限度地减少退役和重启过程中的离线时间是一项重要的任务。 重要的是保持数据分布与重启前相同,保持数据的局部性。
另一项维护任务是管理 HBase 部署。 有很多方法可以将您的 HBase 部署到集群。 最简单的方法是使用基于脚本的方法在整个集群中同步 HBase 安装和配置。
我们将在本章的前六个食谱中介绍这些主题。
本章还将介绍安全性。 随着 HBase 变得越来越流行,不同的用户和群体可能会在一个共享的 HBase 集群中存储更多的数据。 您可能不希望所有用户都拥有对每个 HBase 表的完全权限。 这会给您的数据带来风险;例如,安全风险或丢失的数据操作。 您可能希望验证用户的身份,并根据用户的身份拥有对 HBase 表的访问控制。
在 Hadoop 0.20.203 发布之前,Hadoop 中没有验证用户身份的机制。 Hadoop 使用用户的当前登录名作为其 Hadoop 用户名(即,相当于 whoami)
)。 HDFS 本身不会验证此用户名是否真实以及是否属于实际操作员。
较新版本的 Hadoop(0.20.203 及更高版本)支持客户端的可选 Kerberos 身份验证。 通过此安全支持,它可以在共享 HDFS 群集上存储敏感数据(如财务数据)。
HBase 利用 HDFS 安全性为其客户端提供安全访问。 HBase 在其 0.92 版本中增加了安全支持。 只有经过身份验证的用户才能访问受保护的 HBase。 还可以在 HBase 中添加基于表或基于列族的访问控制。
在本章的最后三个步骤中,我们将安装 Kerberos,然后使用 Kerberos 设置 HDFS 安全性,最后设置安全的 HBase 客户端访问。
启用 HBase RPC 调试级日志记录
Hadoop 和 HBase 使用 log4j 库来写入它们的日志。 日志记录级别在 log4j.properties
文件中设置。 在生产中,日志记录级别通常设置为 INFO 级别,这适用于许多情况。 但是,在某些情况下,您可能希望查看特定 Hadoop/HBase 守护进程的调试信息。
HBase 继承了 Hadoop 的在线日志级别更改功能。 可以从其 Web 用户界面更改 HBase 守护程序的日志记录级别,而无需重新启动该守护程序。
当您需要知道 HBase 守护进程的调试信息但无法重新启动它时,此功能非常有用。 一种典型的情况是对生产 HBase 群集进行故障排除。
在本指南中,我们将介绍如何启用 HBase RPC 调试级日志记录。
做好准备
启动 HBase 群集并从以下 URL 打开 HBase Web UI:
http://<master_host>:60010/master.jsp
怎么做……
无需重新启动 HBase 守护程序即可启用 HBase RPC 调试级日志记录的说明如下:
-
通过单击 Region Server 链接,显示 HBase Web UI 中的 Region Server Web UI。
-
单击区域服务器 Web 用户界面左上角的Log Level链接。
-
获取特定包或类的当前日志记录级别。
-
Enter a package name (for example,
org.apache.hadoop.ipc)
in the Log textbox and click on the Get Log Level button. You will get an output as shown in the following screenshot: -
输入程序包名和日志记录级别,然后单击Set Log Level按钮以设置程序包的日志记录级别(例如,DEBUG)。
-
You will get an output page, as shown in the following screenshot, that shows Effective level: DEBUG, which means that the package's logging level has been changed to DEBUG:
-
现在您应该能够检查区域服务器的日志文件中的调试日志,如下所示:
2012-02-10 22:14:42,878 DEBUG org.apache.hadoop.ipc.HBaseClient: IPC Client (47) connection to ip-10-176-201-128.us-west- 1.compute.internal/10.176.201.128:60000 from hadoop sendi 2012-02-10 22:14:42,879 DEBUG org.apache.hadoop.ipc.HBaseClient: IPC Client (47) connection to ip-10-176-201-128.us-west-1.compute.internal/10.176.201.128:60000 from hadoop got v 2012-02-10 22:14:42,880 DEBUG org.apache.hadoop.ipc.RPCEngine: Call: regionServerReport 2
它是如何工作的.
Hadoop 有自己的在线日志更改工具。 可以从 Hadoop 守护程序的 Web 用户界面获取或设置其日志记录级别。 这对于调试不允许重新启动的生产群集很有用。
HBase 只是从 Hadoop 继承了这个功能。 要在特定区域服务器上启用特定 HBase 守护进程的调试级日志记录,我们需要找到该区域服务器的 Web UI,然后显示其日志级别页面。 在此页面上,我们可以获取或设置特定 Java 包的日志记录级别。
在我们的演示中,我们将 HBase IPC(org.apache.hadoop.ipc)包的日志记录级别设置为 DEBUG。 因此,HRegionServer 守护进程开始将其 IPC 调试信息写入其日志文件。
Hadoop/HBase 守护程序可能会在短时间内生成大量调试日志。 在获得足够的调试信息后,不要忘记将其设置回信息级别。
还有更多...
我们还可以使用 hadoop daemonlog
命令获取/设置日志记录级别。 以下命令获取在 localhost 上运行的 HMaster 守护程序的 IPC 日志级别:
$ $HADOOP_HOME/bin/hadoop daemonlog -getlevel localhost:60010 org.apache.hadoop.ipc
Connecting to http://localhost:60010/logLevel?log=org.apache.hadoop.ipc
Submitted Log Name: org.apache.hadoop.ipc
Log Class: org.apache.commons.logging.impl.Log4JLogger
Effective level: INFO
不带参数执行 hadoop daemonlog
将打印命令的用法。
$ $HADOOP_HOME/bin/hadoop daemonlog
USAGES:
java org.apache.hadoop.log.LogLevel -getlevel <host:port><name>
java org.apache.hadoop.log.LogLevel -setlevel <host:port><name><level>
平稳节点退役
在本食谱中,我们将介绍如何优雅地停止区域服务器。
只需调用以下命令即可停止区域服务器上的 RegionServer 守护程序:
hadoop@slave1$ $HBASE_HOME/bin/hbase-daemon.sh stop regionserver
但是,这种方法有一个缺点,即在停止过程中,部署在停止区域服务器上的区域将会脱机一段时间。 在生产中,特别是对于处理在线请求的集群,预计会优雅地停止区域服务器,以最大限度地减少区域的离线时间。
在本食谱中,我们将描述 HBase 如何支持其优雅的节点退役特性。
做好准备
启动您的 HBase 集群,并以启动集群的用户(在我们的演示中是 hadoop
用户)的身份登录到主节点。
怎么做……
正常停用区域服务器的说明如下:
-
通过调用以下命令优雅地停止区域服务器:
hadoop@master1$ $HBASE_HOME/bin/graceful_stop.sh ip-10-160-226- 84.us-west-1.compute.internal Disabling balancer! HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 balance_switch false true 0 row(s) in 1.3370 seconds Unloading ip-10-160-226-84.us-west-1.compute.internal region(s) 12/01/31 23:32:16 INFO region_mover: Moving 2 region(s) from ip- 10-160-226-84.us-west-1.compute.internal,60020,1328020203720 during this cycle 12/01/31 23:32:16 INFO region_mover: Moving region 5740a39d9eaa59c4175487c14e0a272a (0 of 2) to server=ip-10-161-83- 13.us-west-1.compute.internal,60020,1328013827479 12/01/31 23:32:17 INFO region_mover: Moving region f6a1084fc7534c15696f4baa4abf61ce (1 of 2) to server=ip-10-161-83- 13.us-west-1.compute.internal,60020,1328013827479 12/01/31 23:32:18 INFO region_mover: Wrote list of moved regions to /tmp/ip-10-160-226-84.us-west-1.compute.internal Unloaded ip-10-160-226-84.us-west-1.compute.internal region(s) ip-10-160-226-84.us-west-1.compute.internal: stopping regionserver...
-
由于
graceful_stop.sh
脚本将在实际停止区域服务器守护进程之前关闭 HBase 的负载平衡,因此如果需要,请再次显式启用它:hadoop@master1$ echo 'balance_switch true' | $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 balance_switch true false 0 row(s) in 1.4450 seconds
它是如何工作的.
停止区域服务器是以区域服务器为中心的操作。 它关闭区域服务器上部署的区域,并自行停止。 主服务器只能通过删除 ZooKeeper 中的区域服务器的 Znode 来获知区域服务器的消失,区域服务器调用该 Z 节点是它退出的最后一件事。 在关闭期间以及在主服务器得知区域服务器被移除之前,会有一个很大的不可用窗口。 因此,为了最小化此窗口,我们使用了一种优雅的关闭方式。 它一次(优雅地)将区域从区域服务器上移出一个。
graceful_stop.sh
脚本将区域移出区域服务器,然后停止它。 由于区域首先移动到其他区域服务器,避免了停止过程中的区域离线时间。
在移动部署在特定区域服务器上的区域之前,脚本将首先关闭集群的负载均衡器。 这一点很重要,因为平衡器可能会在 graceful_stop.sh
脚本移动区域的同时平衡集群中的区域。 我们必须通过关掉平衡器来避免这种情况。
从步骤 1 的输出中可以看到,在关闭负载均衡器后,脚本将两个地域从指定的服务器移动到其他地域服务器,最后停止了地域服务器。
由于 graceful_stop.sh
脚本关闭了负载均衡器,因此您可能需要在节点停用后再次打开它。 这就是我们在步骤 2 中所做的。
确保将 HBase web UI 上显示的准确主机名传递给 graceful_stop.sh
,否则它实际上不会将区域优雅地移动到其他区域服务器。
graceful_stop.sh
脚本的全部用法如下所示:
./graceful_stop.sh
Usage: graceful_stop.sh [--config <conf-dir>] [--restart] [-- reload] [--thrift] [--rest] <hostname>
thrift If we should stop/start thrift before/after the hbase stop/start
rest If we should stop/start rest before/after the hbase stop/start
restart If we should restart after graceful stop
reload Move offloaded regions back on to the stopped server
debug Display extra debug logging
hostname Hostname of server we are to stop
还有更多...
您可能已经注意到, graceful_stop.sh
脚本有 restart
和 reload
选项。 这些选项用于滚动重启 HBase 集群。 稍后我们将在滚动重启配方中对其进行描述。
另请参阅
在本章中:
- 滚动重新启动
将节点添加到群集
HBase 最重要的特性之一是它具有极强的可伸缩性。 HBase 通过将节点添加到群集进行线性横向扩展。 您可以轻松地从一个小群集开始,并在您的服务和数据增长时向外扩展。 向 HBase 集群添加区域服务器将是管理员的一项重要维护任务。
一个 HBase 群集只能有一个活动的主节点。 但是,我们可以向集群添加一个备份主节点,以使 HBase 主节点高度可用(HA)。
在本食谱中,我们将介绍如何向 HBase 集群添加备份主节点。 之后,我们还将介绍如何将区域服务器添加到集群。
做好准备
首先在新的主服务器或区域服务器上下载并安装 HBase。 确保该节点上的 HBase 配置与群集中的其他节点同步。
地域服务器通常运行在 Hadoop 的同一 DataNode/TaskTracker 上。 您可能还想在该节点上安装 Hadoop 并启动 DataNode 和 TaskTracker。
我们假设您已经在该新节点上准备好了所有 Hadoop/HBase 目录、配置和操作系统/用户设置。 有关这些初始设置的详细信息,请参阅第 1 章,设置 HBase 群集。
怎么做……
将备份主节点添加到群集的说明如下:
-
在备份主节点上启动 HBase 主守护进程:
hadoop@master2$ $HBASE_HOME/bin/hbase-daemon.sh start master
-
从主机日志中,您会发现新启动的主机正在等待成为下一个活动主机:
org.apache.hadoop.hbase.master.ActiveMasterManager: Another master is the active master, ip-10-176-201-128.us-west- 1.compute.internal,60000,1328878644330; waiting to become the next active master
-
-
将新区域服务器的主机名添加到
$HBASE_HOME/conf
目录下的regionservers
文件中。 例如,要将"slave4"
添加到集群,我们调用以下命令:hadoop@master1$ echo "slave4" >> $HBASE_HOME/conf/regionservers
-
在群集中同步修改后的
regionservers
文件。 -
登录到新服务器并在那里启动区域服务器守护进程:
hadoop@slave4$ $HBASE_HOME/bin/hbase-daemon.sh start regionserver
-
也可以通过 HBase Shell 手动触发负载均衡,将部分地域迁移到新的地域服务器上。 您还可以等待平衡器的下一次运行,默认情况下,平衡器每五分钟运行一次。
hbase> balance_switch true hbase> balancer
它是如何工作的.
在步骤 1 中,我们只需在备份主节点上启动 HBase 主守护进程。 HBase 使用动物园管理员来协调主人选举。 所有主节点都会竞争在 ZooKeeper 中创建 /hbase/master
Z 节点。 赢得选举(成功创建 Znode)的节点将成为群集的活动主节点。 因为现在已经存在一个主节点,所以新的主节点进入空闲模式,并且如果当前的主节点关闭,则等待成为下一个活动的主节点。
要添加区域服务器,首先在步骤 2 中将其主机名添加到 regionservers
文件中。下次启动群集时,区域服务器守护进程将在该节点上自动启动。
由于我们没有重启集群,因此我们在步骤 4 中手动启动了新节点上的地域服务器守护进程。地域服务器将查询 ZooKeeper 来查找并加入集群。
步骤 5 是可选的。 由于新区域服务器上部署的区域较少,因此我们可以显式触发负载均衡,将一些区域移到其中。 默认情况下,平衡器每五分钟运行一次,因此我们也可以等待平衡器的下一次运行。
还有更多...
我们还可以通过以下步骤在群集启动时启动备份主机:
-
将备份主节点添加到
conf/backup-masters
文件:hadoop@master1$ echo "master2" >> $HBASE_HOME/conf/backup-masters
-
在群集中同步
backup-masters
文件。 -
正常启动 HBase,您会发现备份主节点上会启动一个备份 HBase 主节点:
hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh starting master, logging to /usr/local/hbase/logs/hbase-hadoop- master-master1.out slave2: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave2.out slave1: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave1.out slave3: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave3.out master2: starting master, logging to /usr/local/hbase/logs/hbase- hadoop-master-master2.out
-
备份主机上的主日志表明它是备份主机:
hadoop@master2$ tail /usr/local/hbase/logs/hbase-hadoop-master-master2.log 2012-02-10 23:12:07,016 INFO org.apache.hadoop.hbase.master.ActiveMasterManager: Another master is the active master, ip-10-176-201-128.us-west- 1.compute.internal,60000,1328883123244; waiting to become the next active master
-
滚动重启
当升级到新的 HBase 版本时,或者当您想要应用一些配置更改时,您可能想要调用滚动重启。 正如优雅节点退役配方中所述,滚动重启可以最大限度地减少停机时间,因为我们一次只离线一个区域,而不是整个集群。 滚动重新启动使区域分布与重新启动前相同。 这对于保持数据的局部性很重要。
备注
新的 HBase 版本并不总是向后兼容。 您可以调用滚动重启来升级次要版本(例如,从 0.92.1 升级到 0.92.2),但不能跨主要版本升级(例如,从 0.92.x 升级到 0.94.x),因为这些版本之间的协议已更改。 这将在 HBase 0.96 中发生变化,届时您将能够让旧客户端与新服务器对话,反之亦然。
有关从一个版本升级到另一个版本的详细信息,请查看以下链接:
http://hbase.apache.org/book.html#upgrading
滚动重新启动包含重新启动主服务器、正常重新启动区域服务器以及在重新启动后检查数据一致性的步骤。 我们将在本食谱中描述这些步骤。
做好准备
启动您的 HBase 集群。 以启动群集的用户身份登录到主节点。
怎么做……
滚动重启 HBase 集群的说明如下:
-
运行
hbck
以确保群集一致:hadoop@master1$ $HBASE_HOME/bin/hbase hbck 0 inconsistencies detected. Status: OK
-
重新启动主后台进程:
hadoop@master1$ $HBASE_HOME/bin/hbase-daemon.sh stop master hadoop@master1$ $HBASE_HOME/bin/hbase-daemon.sh start master
-
禁用负载均衡器:
hadoop@master1$ echo 'balance_switch false' | $HBASE_HOME/bin/hbase shell balance_switch false true 0 row(s) in 1.1680 seconds
-
创建
rolling-restart-rs.sh
脚本,如下图所示:$ vi rolling-restart-rs.sh #!/bin/bash HBASE_HOME=/usr/local/hbase/current zparent=`$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.HBaseConfTool zookeeper.znode.parent` if [ "$zparent" == "null" ]; then zparent="/hbase" fi online_regionservers=`$HBASE_HOME/bin/hbase zkcli ls $zparent/rs 2>&1 | tail -1 | sed "s/\[//" | sed "s/\]//"` for rs in $online_regionservers do rs_parts=(${rs//,/ }) hostname=${rs_parts[0]} echo "Gracefully restarting: $hostname" $HBASE_HOME/bin/graceful_stop.sh --restart --reload --debug $hostname sleep 1 done
-
使脚本可执行,然后执行它以正常重新启动集群中的每个区域服务器守护进程。 您将获得以下输出:
hadoop@master1$ chmod +x rolling-restart-rs.sh hadoop@master1$ ./rolling-restart-rs.sh Gracefully restarting: ip-10-160-41-210.us-west-1.compute.internal Disabling balancer! HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 balance_switch false false 0 row(s) in 1.2060 seconds Unloading ip-10-160-41-210.us-west-1.compute.internal region(s) Unloaded ip-10-160-41-210.us-west-1.compute.internal region(s) ip-10-160-41-210.us-west-1.compute.internal: stopping regionserver... ip-10-160-41-210.us-west-1.compute.internal: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop- regionserver-slave1.out Reloading ip-10-160-41-210.us-west-1.compute.internal region(s) Reloaded ip-10-160-41-210.us-west-1.compute.internal region(s) Gracefully restarting: ip-10-166-109-91.us-west-1.compute.internal ... Gracefully restarting: ip-10-176-79-57.us-west-1.compute.internal ... Reloaded ip-10-176-79-57.us-west-1.compute.internal region(s)
-
再次启用负载均衡器:
hadoop@master1$ echo 'balance_switch true' | $HBASE_HOME/bin/hbase shell balance_switch true false 0 row(s) in 1.1730 seconds
-
再次运行
hbck
以确保群集一致:hadoop@master1$ $HBASE_HOME/bin/hbase hbck
它是如何工作的.
在步骤 1 中,我们运行了 hbck
命令,以确保集群在重启之前保持一致。 之后,我们在步骤 2 中重新启动了主守护进程。
在步骤 3 中,在重启地域服务器之前,我们关闭了均衡器,以避免在重启过程中出现均衡的情况。
我们在步骤 4 中创建的 rolling-restart-rs.sh
脚本首先通过从 hbase
命令调用 org.apache.hadoop.hbase.util.HBaseConfTool
类来获取 zooKeeper(Zparent)中的根 Z 节点。 HBaseConfTool
是用于获取运行时 HBase 配置的 Java 类。 向其传递 zookeeper.znode.parent
将返回 zparent
设置(如果已设置)。 如果未设置 zparent
,则使用默认值 /hbase
。
每个在线区域服务器在 ZooKeeper 的 $zparent/rs
下都有自己的 Znode。 我们使用它通过调用 hbase zkcli ls
命令来获取在线区域服务器的列表。 返回值是一个包含空格分隔的在线区域服务器的字符串,例如 [region_server1,port,startcode region_server2,port,startcode region_server3,port,startcode]
。 我们没有从 conf/regionservers
文件中获取区域服务器列表。 这是因为,在某些环境中,例如在 Amazon EC2 中使用自己的 DNS 服务的群集,文件中的主机名可能与 HBase 提供的主机名不同。 graceful_stop.sh
脚本只接受 HBase 中使用的主机名,该主机名显示在 HBase web 用户界面上。
对于列表中的每个区域服务器,我们调用传递 --restart --reload --debug
选项的 graceful_stop.sh
命令和服务器的主机名,以正常地重新启动该节点上的区域服务器。
从步骤 5 的输出中可以看到,对于每个在线区域服务器, rolling-restart-rs.sh
脚本一次将一个区域移出区域服务器,重新启动它们,然后将移动的区域重新加载回区域服务器。
在所有区域服务器正常重启之后,在步骤 6 中重新启动之前,我们再次将负载均衡器启用到其状态。
最后,在步骤 7 中,我们再次执行 hbck
以确保集群一致运行。
还有更多...
请注意,在 $HBASE_HOME/bin
目录中有一个 rolling-restart.sh
脚本。 我们没有使用此脚本,因为我们希望优雅地重新启动我们的区域服务器,而脚本中没有实现这一点。
备注
有一个补丁可以使 rolling-restart.sh
以与本食谱前面描述的方式相同的方式工作。 该修补程序计划用于 0.96 版。 您可以在https://issues.apache.org/jira/browse/HBASE-5314查看它以了解详细信息。
管理 HBase 进程的简单脚本
当集群中的节点不断增长时,您可能希望找到工具来显示和管理集群中运行的与 HBase 相关的进程。 由于 hadoop
用户配置为无需密码即可从主节点 SSH 到集群中的每个从节点,因此我们很容易编写一个简单的脚本来完成此任务,只需对每个节点进行 SSH 登录,并显示/管理该节点上正在运行的 HBase 进程。
由于 Hadoop/HBase 进程在Java 虚拟机(JVM)中运行,我们的任务是管理集群中的这些 Java 进程。
在本食谱中,我们将创建一个简单的脚本来显示 HBase 集群中 hadoop
用户拥有的所有正在运行的 Java 进程。
做好准备
启动您的 HBase 集群。 以启动群集的用户身份登录到主节点。
我们假设您以同一用户(这里的 hadoop
用户)身份运行 HDFS 和 HBase。
怎么做……
创建管理 HBase 进程的简单脚本的说明如下:
-
创建
cluster-jps.sh
脚本,如下图所示:$ vi cluster-jps.sh #!/bin/bash # Show all running Java processes on region servers. Must run on master using HBase owner user. JAVA_HOME=/usr/local/jdk1.6 HBASE_HOME=/usr/local/hbase/current IFS=$'\n' printf "+------------------------------+----------+--------------------+\n" printf "|%-30s|%-10s|%-20s|\n" " HOST" " PID" " PROCESS" printf "+------------------------------+----------+--------------------+\n" process_count=0 rs_count=0 for rs in `cat $HBASE_HOME/conf/regionservers` do i=1 for process in `ssh $rs "$JAVA_HOME/bin/jps" | grep -v Jps` do process_parts=(${process/ /$'\n'}) pid=${process_parts[0]} pname=${process_parts[1]} if [ $i -eq 1 ]; then host="$rs" else host=" " fi printf "|%-30s|%-10s|%-20s|\n" " $host" " $pid" " $pname" i=`expr $i + 1` process_count=`expr $process_count + 1` done rs_count=`expr $rs_count + 1` printf "+------------------------------+----------+--------------------+\n" done echo -e "$process_count running Java processes on $rs_count region servers.\n"
-
以在主节点上启动 Hadoop/HBase 的用户身份运行脚本:
hadoop@master1$ chmod +x cluster-jps.sh hadoop@master1$ ./cluster-jps.sh
-
You will get an output as shown in the following screenshot:
-
要显示主节点上的主进程,请调用以下命令:
hadoop@master1$ $JAVA_HOME/bin/jps 3005 Jps 1680 QuorumPeerMain 1781 HMaster 1538 NameNode
它是如何工作的.
在此脚本中,我们将从 conf/regionservers
文件中获取区域服务器列表。 然后,对于每个区域服务器,SSH 登录到该服务器并在其上运行 jps
命令。 Jps
是随 JDK 安装一起提供的命令,用于显示用户拥有的正在运行的 Java 进程。 我们需要的最基本信息,进程 ID(PID)和每个 Java 进程的名称都包含在 jps
命令的输出中。
脚本的其余部分只收集每个节点的 jps
命令输出,并以友好的格式显示结果。
正如我们在步骤 4 中所做的那样,我们还可以使用 jps
命令来显示在主节点上运行的 Java 进程。 在此脚本中,我们跳过了显示主服务器的 Java 进程的逻辑。
如第 5 章,监视和诊断的设置 Nagios 以监视 HBase 进程配方中所述,您需要确保仔细监视这些 Java 进程。
简化部署的简单脚本
有很多方法可以将您的 HBase 部署到集群。 因为 Hadoop 和 HBase 都是用 Java 编写的,所以大多数部署都是通过简单地将所有文件复制到集群中的节点来完成的。
最简单的方法是使用基于脚本的方法在整个集群中同步 HBase 安装和配置。 与其他现代部署管理工具相比,它可能不那么酷,但对于小型甚至中型集群来说,它工作得很好。
在本食谱中,我们将创建一个简单的脚本来将 HBase 安装从其主节点同步到集群中的所有区域服务器。 此方法也可用于部署 Hadoop。
做好准备
以启动群集的用户身份登录到主节点。 我们假设您已经为用户设置了从主节点到区域服务器的非密码 SSH。
怎么做……
创建简单脚本以简化 HBase 部署的说明如下:
-
创建
cluster-deploy.sh
脚本,如下图所示:$ vi cluster-deploy.sh #!/bin/bash # Sync HBASE_HOME across the cluster. Must run on master using HBase owner user. HBASE_HOME=/usr/local/hbase/current for rs in `cat $HBASE_HOME/conf/regionservers` do echo "Deploying HBase to $rs:" rsync -avz --delete --exclude=logs $HBASE_HOME/ $rs:$HBASE_HOME/ echo sleep 1 done echo "Done"
-
以在主节点上启动 Hadoop/HBase 的用户身份运行脚本:
hadoop@master1$ chmod +x cluster-deploy.sh hadoop@master1$ ./cluster-deploy.sh Deploying HBase to slave1: sending incremental file list ./ conf/ conf/hbase-site.xml sent 40023 bytes received 259 bytes 80564.00 bytes/sec total size is 55576486 speedup is 1379.69 Deploying HBase to slave2: ... Deploying HBase to slave3: ... Done
它是如何工作的.
该脚本使用的事实是, conf/regionservers
文件中列出了所有区域服务器。 它从文件中读取每个区域服务器的主机名,然后调用 rsync
命令将 HBase 安装目录从主节点同步到该区域服务器。 我们将 --delete
选项添加到 rsync
命令,因为我们希望从区域服务器中删除不必要的文件。
要使用此脚本,请在主节点上进行一些 HBase 配置更改,然后使用 HBase 所有者用户运行该脚本。
还有更多...
当群集变得更大时,您可能希望尽可能自动执行部署,例如新节点的操作系统和用户设置、获取和部署软件包以及从中心版本管理的配置中心设置文件。
对于这些类型的部署任务,您可能希望尝试使用 Pupppt 或 Chef。 互联网上有一些 HBase 的木偶和厨师配置示例,其 URL 如下:
- 木偶配方:http://hstack.org/hstack-automated-deployment-using-puppet/
- 厨师食谱:https://github.com/ueshin/chef-cookbooks
您可能想尝试的另一个工具是 Apache bigtop,它可以在以下位置找到:
http://incubator.apache.org/bigtop/
Bigtop 是一个开发 Hadoop 生态系统打包和测试的项目。 它还没有准备好进入黄金时间,但是对于轻松构建和部署 Hadoop 和 HBase 来说,它是一个很棒的工具。
针对 Hadoop 和 HBase 的 Kerberos 身份验证
最近发布的 Hadoop 1.0 和 HBase 0.92 增加了安全支持。 启用安全保护后,只有经过身份验证的用户才能访问 Hadoop 和 HBase 群集。 身份验证由受信任管理员管理的单独身份验证服务提供。 这使得 HBase 成为存储金融数据等敏感大数据的一个相当不错的选择。
Hadoop 依靠 Kerberos 身份验证服务提供安全支持。 安全的 HBase 必须在具有安全支持的 HDFS 上运行,因此 HBase 还依赖 Kerberos 为其提供安全支持。
以下是维基百科对 Kerberos 的描述:
Kerberos 是一种计算机网络认证协议,它基于“票证”工作,以允许在非安全网络上通信的节点以安全的方式相互证明其身份。
使用最广泛的 Kerberos 实现是 MIT Kerberos。 在本食谱中,我们将介绍如何安装和设置 MIT Kerberos。 安装包括设置 Kerberos 管理服务器和密钥分发中心(KDC)。
做好准备
安装 MIT Kerberos 需要 Linux 服务器。 确保您在该服务器上具有 root 权限。 在这个配方中,我们假设服务器的主机名是 master2
。
使用以下命令提前在服务器上创建 Kerberos 日志目录:
$ sudo mkdir /var/log/kerberos
怎么做……
安装 MIT Kerberos 的说明如下:
-
Install the Kerberos admin server and KDC:
hac@master2$ sudo apt-get install krb5-{admin-server,kdc}
- 在安装过程中,系统将要求您输入默认域名。 Kerberos 领域的标准名称是您的大写域名。 当我们在本书中使用
hbase-admin-cookbook.com
作为我们的域时,我们将选择HBASE-ADMIN-COOKBOOK.COM
作为域名,如以下屏幕截图所示:
- 安装过程还会要求您输入 Kerberos 服务器和管理服务器。 在此处输入您的服务器的完全限定域名。 在我们的演示中,它是
master2.hbase-admin-cookbook.com.
- 在安装过程中,系统将要求您输入默认域名。 Kerberos 领域的标准名称是您的大写域名。 当我们在本书中使用
-
Create a realm using the following command:
hac@master2$ sudo krb5_newrealm
备注
该命令将要求提供主密码。 输入您的密码,不要忘记。
-
通过编辑
krb5.conf
文件配置 Kerberos,如下图所示:hac@master2$ sudo vi /etc/krb5.conf [realms] HBASE-ADMIN-COOKBOOK.COM = { kdc = master2.hbase-admin-cookbook.com admin_server = master2.hbase-admin-cookbook.com default_domain = hbase-admin-cookbook.com } [domain_realm] .hbase-admin-cookbook.com = HBASE-ADMIN-COOKBOOK.COM hbase-admin-cookbook.com = HBASE-ADMIN-COOKBOOK.COM [logging] kdc = FILE:/var/log/kerberos/krb5kdc.log admin_server = FILE:/var/log/kerberos/kadmin.log default = FILE:/var/log/kerberos/krb5lib.log
-
通过取消注释
kadm5.acl
文件中的以下行,授予管理员用户完全权限:hac@master2$ sudo vi /etc/krb5kdc/kadm5.acl */admin *
-
重新启动 Kerberos 管理服务器和 KDC 以应用更改:
hac@master2$ sudo invoke-rc.d krb5-admin-server restart hac@master2$ sudo invoke-rc.d krb5-kdc restart
-
在
kadmin.local
控制台中测试安装和配置:hac@master2$ sudo kadmin.local Authenticating as principal root/admin@HBASE-ADMIN-COOKBOOK.COM with password. kadmin.local: listprincs K/M@HBASE-ADMIN-COOKBOOK.COM kadmin/admin@HBASE-ADMIN-COOKBOOK.COM kadmin/changepw@HBASE-ADMIN-COOKBOOK.COM kadmin/history@HBASE-ADMIN-COOKBOOK.COM kadmin/ip-10-174-14-251.us-west-1.compute.internal@HBASE-ADMIN- COOKBOOK.COM krbtgt/HBASE-ADMIN-COOKBOOK.COM@HBASE-ADMIN-COOKBOOK.COM
-
创建具有管理权限的主体:
kadmin.local: addprinc root/admin WARNING: no policy specified for root/admin@HBASE-ADMIN- COOKBOOK.COM; defaulting to no policy Enter password for principal "root/admin@HBASE-ADMIN- COOKBOOK.COM": Re-enter password for principal "root/admin@HBASE-ADMIN- COOKBOOK.COM": Principal "root/admin@HBASE-ADMIN-COOKBOOK.COM" created.
-
将测试用户帐户(“主体”)添加到 Kerberos:
kadmin.local: addprinc hac
-
退出
kadmin.local
控制台,并通过获取 Kerberos 票证测试测试用户的身份验证:kadmin.local: quit hac@master2$ klist -5 klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_1002) hac@master2$ kinit Password for hac@HBASE-ADMIN-COOKBOOK.COM: hac@master2$ klist -5 Ticket cache: FILE:/tmp/krb5cc_1002 Default principal: hac@HBASE-ADMIN-COOKBOOK.COM Valid starting Expires Service principal 02/05/12 11:48:49 02/05/12 21:48:49 krbtgt/HBASE-ADMIN- COOKBOOK.COM@HBASE-ADMIN-COOKBOOK.COM renew until 02/06/12 11:48:46
-
使用以下命令终止缓存的票证:
```scala
hac@master2$ kdestroy
hac@master2$ klist -5
klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_1002)
```
- 使用以下命令删除测试用户帐户:
```scala
hac@master2$ sudo kadmin.local
kadmin.local: delprinc hac
```
它是如何工作的.
我们在同一台 master2
服务器上安装了 Kerberos 管理服务器和 KDC。 在安装期间,我们将 HBASE-ADMIN-COOKBOOK.COM
设置为默认领域。 该领域实际上是在步骤 2 中使用 krb5_newrealm
命令创建的。
在步骤 3 中,我们在 /etc/krb5.conf
文件中配置了 Kerberos 的域、域和日志设置。 在步骤 4 中,我们向管理员用户授予了完全权限。之后,我们重新启动 Kerberos 以应用更改。
为了测试安装和配置,我们在步骤 6 进入了 Kerberos 的 kadmin.local
控制台。 kadmin.local
是操作 Kerberos 的提示控制台。 只有 Kerberos 管理服务器上的 root
用户可以进入 kadmin.local
控制台并在那里执行 Kerberos 管理命令。
在步骤 6 中,我们还运行了 listprincs
命令来显示 Kerberos 的初始主体。Kerberos 中的唯一标识(用户、主机等)称为主体。 主体的格式为 primary/instance@REALM
。
在步骤 7 中,我们向 Kerberos 添加了一个管理员用户( root
用户)。 通过输入 Kerberos 密码(而不是操作系统密码),来自 Kerberos 客户端的 root
用户将能够通过 kadmin
控制台执行 Kerberos 的管理命令。 kadmin
控制台类似于 kadmin.local
,但是可以使用管理员用户从其他 Kerberos 客户端进入。 当您需要授予 admin 用户,但不希望他们拥有对 Kerberos 管理服务器的 root 访问权限时,它很有用。
我们还在步骤 8 中为 hac
用户添加了一个测试主体,并从步骤 9 到步骤 11 测试了它的身份验证。
Kerberos 用户需要票证才能访问 Kerberos 经过身份验证的服务。 票证由 KDC 发行票证授予票证(TGT)。 要获得 TGT,用户需要提供他们的 Kerberos 密码来证明他们的身份。 Kerberos TGT 可以缓存,并将在配置的时间之后过期。
在步骤 9 中,我们使用 klist
命令显示了 hac
用户缓存的 TGT。 结果是空的,这是我们预期的。 然后,我们使用 kinit
命令请求 TGT。 之后, klist
命令显示缓存的 TGT 及其到期日期。 在步骤 10 中使用 kdestroy
命令删除了缓存的 TGT。
还有更多...
在本食谱中,我们进行了基本的 Kerberos 设置。 我们将配置 HDFS 和 HBase 以使用此 Kerberos 安装来提供以下两种方法的安全支持。 Kerberos 的细节超出了本书的范围。 有关详细信息,请参阅以下 URL:
- kerberos:http://web.mit.edu/Kerberos/的网络身份验证协议
另请参阅
在本章中:
- 使用 Kerberos 配置 HDFS 安全性
- HBase 安全配置
使用 Kerberos 配置 HDFS 安全性
较新版本的 Hadoop(0.20.203 及更高版本)支持可选的客户端 Kerberos 身份验证。 此安全支持包括安全 HDFS 和安全 MapReduce 配置。
Hadoop 安全的动机不是为了防御黑客,因为所有大型 Hadoop 集群都在防火墙后面,只允许员工访问它们。 其目的只是允许将敏感数据(如财务数据)存储在共享群集上。
Hadoop 的以前版本已经在 HDFS 中拥有文件所有权和权限;限制是它们没有验证用户身份的机制。 有了这种 Kerberos 安全支持,Kerberos 将验证用户身份,并且只允许经过身份验证的用户访问 HDFS 群集。
由于安全的 HBase 访问预计运行在安全的 HDFS 集群之上,因此设置 HDFS 安全是配置 HBase 安全的先决条件。 在本食谱中,我们将重点介绍如何使用 Kerberos 配置 HDFS 安全性。
做好准备
我们假设您有一个正在工作的Kerberos 密钥分发中心(KDC),并设置了领域。 有关安装和配置 Kerberos 的更多信息,请参阅本章中的用于 Hadoop 和 HBase 的Kerberos 身份验证食谱。
我们假设每个节点上的 root
用户在 Kerberos 中具有管理权限。 在此假设下,我们将使用 kadmin
控制台执行 Kerberos 管理命令。 如果这一假设不成立,您将需要通过 Kerberos 管理服务器上的 kadmin.local
控制台操作 Kerberos。
在群集的每个节点上,使用以下命令创建一个目录来存储 Kerberos 密钥表文件:
$sudo mkdir /etc/hadoop
在执行本食谱中讨论的步骤之前,请关闭您的集群。
怎么做……
要使用 Kerberos 配置 HDFS 安全,请在群集的每个节点上执行以下步骤:
-
安装 Kerberos 客户端软件:
$ sudo apt-get install krb5-{config,user} libpam-krb5
-
使用以下命令进入
kadmin
控制台:$ sudo kadmin
-
将
hadoop
用户主体添加到 Kerberos。 将主体的主机名部分(ip-10-168-46-11.us-west-1.compute.internal
)替换为主机的完全限定域名(FQDN):kadmin: addprinc -randkey hadoop/ip-10-168-46-11.us-west- 1.compute.internal
-
将主机主体添加到 Kerberos。 将主体的主机名部分替换为主机的 FQDN:
kadmin: addprinc -randkey host/ip-10-168-46-11.us-west- 1.compute.internal
-
使用以下命令创建密钥表文件:
kadmin: ktadd -k /etc/hadoop/hadoop.keytab -norandkey hadoop/ip- 10-168-46-11.us-west-1.compute.internal host/ip-10-168-46-11.us- west-1.compute.internal
-
退出
kadmin
控制台,确认密钥表文件是否创建正确:kadmin: quit $ klist -e -k -t /etc/hadoop/hadoop.keytab Keytab name: WRFILE:/etc/hadoop/hadoop.keytab KVNO Timestamp Principal ---- ----------------- ------------------------------------------- ------------- 5 02/05/12 16:03:34 hadoop/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (AES-256 CTS mode with 96-bit SHA-1 HMAC) 5 02/05/12 16:03:34 hadoop/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (ArcFour with HMAC/md5) 5 02/05/12 16:03:34 hadoop/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (Triple DES cbc mode with HMAC/sha1) 5 02/05/12 16:03:34 hadoop/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (DES cbc mode with CRC-32) 5 02/05/12 16:03:34 host/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (AES-256 CTS mode with 96-bit SHA-1 HMAC) 5 02/05/12 16:03:34 host/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (ArcFour with HMAC/md5) 5 02/05/12 16:03:34 host/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (Triple DES cbc mode with HMAC/sha1) 5 02/05/12 16:03:34 host/ip-10-168-46-11.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM (DES cbc mode with CRC-32)
-
确保密钥表文件仅可由
hadoop
用户读取:$ sudo chown hadoop:hadoop /etc/hadoop/hadoop.keytab $ sudo chmod 400 /etc/hadoop/hadoop.keytab
-
Download the "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6" from the following URL:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
按照下载文件中的
README
文件安装。 -
通过将以下配置添加到
core-site.xml
文件来启用 Hadoop 安全性:$ vi $HADOOP_HOME/conf/core-site.xml <property> <name>hadoop.security.authentication</name> <value>kerberos</value> </property> <property> <name>hadoop.security.authorization</name> <value>true</value> </property>
-
通过将以下配置添加到
hdfs-site.xml
文件来启用 HDFS 安全性:
```scala
$ vi $HADOOP_HOME/conf/hdfs-site.xml<property>
<name>dfs.block.access.token.enable</name>
<value>true</value>
</property>
```
- 将 NameNode 安全配置添加到
hdfs-site.xml
文件:
```scala
$ vi $HADOOP_HOME/conf/hdfs-site.xml
<property>
<name>dfs.https.address</name>
<value>${your_namenode_host}:50470</value>
</property>
<property>
<name>dfs.https.port</name>
<value>50470</value>
</property>
<property>
<name>dfs.namenode.keytab.file</name>
<value>/etc/hadoop/hadoop.keytab</value>
</property>
<property>
<name>dfs.namenode.kerberos.principal</name>
<value>hadoop/_HOST@HBASE-ADMIN-COOKBOOK.COM</value>
</property>
<property>
<name>dfs.namenode.kerberos.https.principal</name>
<value>host/_HOST@HBASE-ADMIN-COOKBOOK.COM</value>
</property>
```
- 将 DataNode 安全配置添加到
hdfs-site.xml
文件:
```scala
$ vi $HADOOP_HOME/conf/hdfs-site.xml
<property>
<name>dfs.datanode.data.dir.perm</name>
<value>700</value>
</property>
<!-- secure setup requires privileged ports -->
<property>
<name>dfs.datanode.address</name>
<value>0.0.0.0:1004</value>
</property>
<property>
<name>dfs.datanode.http.address</name>
<value>0.0.0.0:1006</value>
</property>
<property>
<name>dfs.datanode.keytab.file</name>
<value>/etc/hadoop/hadoop.keytab</value>
</property>
<property>
<name>dfs.datanode.kerberos.principal</name>
<value>hadoop/_HOST@HBASE-ADMIN-COOKBOOK.COM</value>
</property>
<property>
<name>dfs.datanode.kerberos.https.principal</name>
<value>host/_HOST@HBASE-ADMIN-COOKBOOK.COM</value>
</property>
```
- 在主节点上启动 NameNode 守护进程:
```scala
hadoop@master1$ $HADOOP_HOME/bin/hadoop-daemon.sh start namenode
```
* 您将在 NameNode 日志文件中找到类似以下内容的日志:
```scala
2012-02-05 20:26:48,642 INFO org.apache.hadoop.security.UserGroupInformation: Login successful for user hadoop/ip-10-168-46-11.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM
2012-02-05 20:37:19,397 INFO org.apache.hadoop.security.UserGroupInformation: Login successful for user host/ip-10-168-46-11.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM
2012-02-05 20:37:19,694 INFO org.apache.hadoop.http.HttpServer: Added global filtersafety (class=org.apache.hadoop.http.HttpServer$QuotingInputFilter)
2012-02-05 20:37:19,705 INFO org.apache.hadoop.http.HttpServer: dfs.webhdfs.enabled = false
2012-02-05 20:37:19,714 INFO org.apache.hadoop.http.HttpServer: Adding Kerberos filter to getDelegationToken
2012-02-05 20:37:19,714 INFO org.apache.hadoop.http.HttpServer: Adding Kerberos filter to renewDelegationToken
2012-02-05 20:37:19,716 INFO org.apache.hadoop.http.HttpServer: Adding Kerberos filter to cancelDelegationToken
2012-02-05 20:37:19,717 INFO org.apache.hadoop.http.HttpServer: Adding Kerberos filter to fsck
2012-02-05 20:37:19,718 INFO org.apache.hadoop.http.HttpServer: Adding Kerberos filter to getimage
```
* 以下步骤仅适用于 DataNode 节点:
- 将数据存储目录权限更改为仅由其所有者读取:
```scala
$ chmod -R 700 /usr/local/hadoop/var/dfs/data
```
- 将日志目录所有者更改为
hadoop
用户:
```scala
$ chown -R hadoop:hadoop $HADOOP_HOME/logs
```
- 通过将以下命令添加到
hadoop-env.sh
文件来配置 Hadoop 安全数据节点用户:
```scala
$ vi conf/hadoop-env.sh
HADOOP_SECURE_DN_USER=hadoop
```
- For 32-bit systems only, build
jsvc
from source, and then replace thecommons-daemon
JAR file in the Hadooplib
directory, as described in the following steps:
I.从 Apache Commons Daemon 下载 URL([http://commons.apache.org/daemon/download_daemon.cgi](http://commons.apache.org/daemon/download_daemon.cgi))下载 `jsvc`源文件(`commons-daemon-1.x.y-native-src.tar.gz`),然后按照 tarball 中的 `README`文件构建 `jsvc`。 然后,将构建的 `jsvc`二进制文件复制到 `$HADOOP_HOME/libexec/jsvc.i386`文件。
二、。 另外,使用相同版本的 `jsvc`下载 Commons Daemon JAR 文件(`commons-daemon-1.x.y-bin.tar.gz`)。 将其解压缩并用下载的 JAR 文件替换 `$HADOOP_HOME/lib/commons-daemon-1.0.1.jar`。
- 使用
root
用户
```scala
$ sudo $HADOOP_HOME/bin/hadoop-daemon.sh start datanode
```
启动 DataNode
* 如果一切配置良好,应该可以找到日志,如下图所示:
```scala
2012-02-05 21:46:02,925 INFO org.apache.hadoop.security.UserGroupInformation: Login successful for user hadoop/ip-10-168-106-166.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM using keytab file /etc/hadoop/hadoop.keytab
```
* 启动群集中的所有 DataNode 后,使用以下说明测试我们的安全 HDFS:
- Test the secure HDFS from its admin web page. Click on the Browse the filesystem link; an error will show on the screen, as seen in the following screenshot:

- 从您的客户端节点上的命令行测试安全 HDFS:
```scala
hac@client1$ $HADOOP_HOME/bin/hadoop fs -ls /
12/02/05 23:00:50 ERROR security.UserGroupInformation: PriviledgedActionException as:hac cause:javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
12/02/05 23:00:50 WARN ipc.Client: Exception encountered while connecting to the server : javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
12/02/05 23:00:50 ERROR security.UserGroupInformation: PriviledgedActionException as:hac cause:java.io.IOException: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
Bad connection to FS. command aborted. exception: Call to ip-10- 168-46-11.us-west-1.compute.internal/10.168.46.11:8020 failed on local exception: java.io.IOException: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
```
- 验证用户是否可以从 kadmin 控制台访问安全 HDFS:
```scala
hac@client1$ sudo kadmin
kadmin: addprinc hac/ip-10-160-34-143.us-west-1.compute.internal
kadmin: quit
```
- 使用以下命令获取用户的 Kerberos 票证:
```scala
hac@client1$ kinit hac/ip-10-160-34-143.us-west-1.compute.internal
Password for hac/ip-10-160-34-143.us-west- 1.compute.internal@HBASE-ADMIN-COOKBOOK.COM:
```
- 从您的客户端节点上的命令行再次测试身份验证:
```scala
hac@client1$ $HADOOP_HOME/bin/hadoop fs -ls /
Found 6 items
drwxr-xr-x - hadoop supergroup 0 2012-01-08 08:09 /backup
drwxr-xr-x - hadoop supergroup 0 2012-02-04 21:29 /hbase
drwxr-xr-x - hadoop supergroup 0 2011-11-24 17:13 /hbase-b
drwxrwxrwx - hadoop supergroup 0 2012-01-08 22:47 /tmp
drwxr-xr-x - hadoop supergroup 0 2012-01-03 22:12 /user
drwxr-xr-x - hadoop supergroup 0 2012-01-25 11:08 /usr
```
它是如何工作的.
首先,我们在每个 HDFS 节点上安装了 Kerberos 客户端软件。 之后,我们进入 kadmin
控制台,为该节点的 hadoop
用户和节点本身添加 Kerberos 主体。 由于我们将只使用密钥表文件中的这些主体,因此我们将 -randkey
选项添加到 addprinc
命令中。
由于我们将在本配方中仅设置 HDFS 安全性,因此我们在这里使用的是 hadoop
用户。 如果您还将启用安全 MapReduce,我们建议您对 HDFS 和 MapReduce 使用不同的用户,例如分别为 hdfs
和 mapreduce
用户。
这里的另一点是为每个节点上的 hadoop
用户添加不同的主体。 尽管可以在 Kerberos 中为所有节点上的 hadoop
用户添加单个主体,但不建议这样做。 因为在这种情况下,由于群集将向同一主体(hadoop@YOUR_REALM.COM
)发送许多身份验证请求,因此 Kerberos 可能会将其视为攻击而无法进行身份验证。
在步骤 5 中,我们为前面步骤中添加的主体创建了密钥表文件(/etc/hadoop/hadoop.keytab
)。 密钥表是一个包含 Kerberos 主体和加密密钥对的文件。 使用此文件,Hadoop 可以在不提示输入密码的情况下登录到 Kerberos。
在步骤 6 中确认密钥表文件之后,我们在步骤 7 中使其只对 hadoop
用户可读。
默认情况下,票证使用 AES-256 加密进行加密。 这需要在所有 HDFS 节点和客户端上安装“Java加密扩展(JCE)无限强度管辖策略文件 6”。 这就是我们在步骤 8 中所做的。
在步骤 9 和 10 中启用 Hadoop 和 HDFS 安全后,我们在步骤 11 中在 NameNode 上配置了 Kerberos 安全。我们在此步骤中设置密钥表文件路径和主体。 在运行时, _HOST
设置值将替换为节点的主机名。
在步骤 12 中添加了 DataNode 安全设置。对于 dfs.datanode.address
和 dfs.datanode.http.address
设置,请注意安全设置需要特权端口(低于 1024 的端口)。
正如您从步骤 13 的输出中看到的,如果一切配置良好, hadoop
用户和主节点将在 NameNode 启动过程中由 Kerberos 进行身份验证。
对于 DataNode 节点,我们首先在步骤 14 中将数据存储目录更改为只对 hadoop
用户可读。
安全数据节点必须由 root
用户启动并由 hadoop
用户运行。 这是在步骤 16 中通过将 HADOOP_SECURE_DN_USER=hadoop
设置添加到 hadoop-env.sh
文件来配置的。
步骤 17 仅对 32 位系统是必需的。 这很简单,因为当前 Hadoop 发行版的 tarball 中没有包含 32 位 jsvc
二进制文件。
在集群中的所有 DataNode 都由 root
用户启动后,我们现在可以从其 Web UI 和命令行界面测试受保护的 HDFS。
正如您在步骤 19 和 20 中看到的那样,未经身份验证的用户无法从 Web UI 或命令行访问受保护的 HDFS。 这是我们所期待的。 身份(包括 HDFS 本身的组件和客户端)必须由 Kerberos 进行身份验证,然后才能与领域中的其他身份通信。
在步骤 21 中,我们为 HDFS 客户机节点上的 hac
用户添加了一个测试主体。 在步骤 22 中获得 Kerberos 票证(经过身份验证)后,用户能够成功访问受保护的 HDFS。
还有更多...
我们刚刚讨论了最基本的 HDFS 安全配置。 由于本书范围有限,我们在此省略了一些步骤,包括:
- Second DaryNameNode 安全性
- MapReduce 安全性
- 启用 WebHDFS 时的 WebHDFS 安全性
- 用户帐户和组
- HDFS 和本地文件系统上的目录所有权
- 向客户端验证数据节点的身份(HDFS-1150)
HBase 安全配置
随着 HBase 变得越来越流行,不同的用户和群体可能会在一个共享的 HBase 集群中存储更多的数据。 您可能不希望所有用户都拥有对每个 HBase 表的完全权限。 这会给您的数据增加风险,例如,安全风险或错过的数据操作。
较新的 HBase 版本(0.92 及更高版本)具有基于 Kerberos 的安全支持。 这样,Kerberos 将验证用户身份,并且只允许经过身份验证的用户访问安全的 HBase 集群中的数据。
在本食谱中,我们将介绍如何配置对 HBase 的安全客户端访问。
做好准备
确保您使用的是启用安全功能的 HBase 版本。 如果你从 HBase 官方网站下载,文件名应该类似于 hbase-0.92.1-security.tar.gz
。
我们假设您有一个正在工作的Kerberos 密钥分发中心(KDC),并设置了领域。 有关安装和配置 Kerberos 的更多信息,请参阅本章中的用于 Hadoop 和 HBase 的Kerberos 身份验证食谱。
由于安全的 HBase 访问预计将在受保护的 HDFS 集群上运行,因此您还需要提前设置 HDFS 安全性。 有关详细信息,请参阅本章中的使用 Kerberos配置 HDFS 安全性食谱。
因为我们在本书中使用了相同的用户( hadoop
用户)来运行 HDFS 和 HBase,所以我们能够共享密钥表文件。 如果您对 HDFS 和 HBase 使用不同的用户,则生成 HBase 所有者自己的密钥表文件。
怎么做……
配置 HBase 安全性的说明如下:
-
在集群中的每个节点上,将以下最基本的配置添加到
hbase-site.xml
文件:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.regionserver.kerberos.principal</name> <value>hadoop/_HOST@HBASE-ADMIN-COOKBOOK.COM</value> </property> <property> <name>hbase.regionserver.keytab.file</name> <value>/etc/hadoop/hadoop.keytab</value> </property> <property> <name>hbase.master.kerberos.principal</name> <value>hadoop/_HOST@HBASE-ADMIN-COOKBOOK.COM</value></property> <property> <name>hbase.master.keytab.file</name> <value>/etc/hadoop/hadoop.keytab</value> </property>
-
在群集中的每个服务器节点上,将以下配置添加到
hbase-site.xml
文件:$ vi $HBASE_HOME/conf/hbase-site.xml <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>
-
在每个 HBase 客户端节点上,将以下配置添加到
hbase-site.xml
文件:$ vi $HBASE_HOME/conf/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>
-
在主节点上启动主守护进程:
hadoop@master1$ $HBASE_HOME/bin/hbase-daemon.sh start master
2012-02-06 00:06:57,524 INFO org.apache.hadoop.security.UserGroupInformation: Login successful for user hadoop/ip-10-168-46-11.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM
- 如果一切配置正常,您将在主节点上的 HBase 日志文件中找到日志,如下所示:
-
Start the region server daemon on every region server node:
hadoop@master1$ $HBASE_HOME/bin/hbase-daemons.sh start regionserver
您应该可以在主节点上的 HBase 日志文件中找到日志,如下所示:
2012-02-06 00:13:10,206 INFO SecurityLogger.org.apache.hadoop.security.authorize.ServiceAuthorizationManager: Authorization successfull for hadoop/ip-10-168-62-63.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM for protocol=interface org.apache.hadoop.hbase.ipc.HMasterRegionInterface
并登录地域服务器,如下所示:
2012-02-06 00:14:29,276 INFO SecurityLogger.org.apache.hadoop.security.authorize.ServiceAuthorizationManager: Authorization successfull for hadoop/ip-10-168-46-11.us-west-1.compute.internal@HBASE-ADMIN-COOKBOOK.COM for protocol=interface org.apache.hadoop.hbase.ipc.HMasterRegionInterface
-
清除 Kerberos 缓存并测试客户端节点上的安全 HBase 配置。
hac@client1$ kdestory hac@client1$ $HBASE_HOME/bin/hbase shell hbase> count 'hly_temp' 12/02/06 00:21:37 ERROR security.UserGroupInformation: PriviledgedActionException as:hac cause:javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
-
获取 Kerberos 票证并再次测试:
hac@client1$ kinit hac/ip-10-160-34-143.us-west-1.compute.internal hac@client1$ $HBASE_HOME/bin/hbase shell hbase> count 'hly_temp' ... Current count: 94000, row: USW000948470714 Current count: 95000, row: USW000949080410 95630 row(s) in 22.1700 seconds
它是如何工作的.
在步骤 1 中,我们在 hbase-site.xlm
文件中为 HBase 主服务器和区域服务器配置了 Kerberos 主体及其密钥表文件。 它是服务器节点和客户端节点的通用配置。 与 Hadoop 安全配置类似, _HOST
设置值将在运行时替换为节点的主机名。
步骤 2 仅适用于服务器节点。 我们已经在配置中启用了 HBase 身份验证和授权。 我们已经将 HBase 设置为使用 Kerberos 身份验证和基于 HBase 协处理器的授权。 HBase 协处理器是 0.92 版本中的一个新特性,基于 Google 的 Bigtable 协处理器。 它是在区域服务器中的每个区域运行的任意代码。 HBase 协处理器的一个示例用法是实现对 HBase 表的访问控制。
在步骤 3 的客户端,我们只需要配置 Kerberos 的身份验证。
从步骤 4 和 5 的日志中可以看到,HBase 主服务器和区域服务器将在启动过程中由 Kerberos 进行身份验证。
如步骤 6 和 7 所示,未经身份验证的用户无法访问受保护的 HBase。 这是我们所期待的。 身份(包括服务器端组件和客户端)必须由 Kerberos 进行身份验证,然后才能与领域中的其他身份通信。
还有更多...
我们在本食谱中设置了对 HBase 的安全客户端访问。 除此之外,您可能还希望配置 HBase 表的访问控制。 使用前面的设置,实施以下配置以启用访问控制:
- 配置 ZooKeeper 安全性
- 启用 AccessController 协处理器
- 在 HBase Shell 中编辑用户权限
由于本书范围有限,我们省略了这些配置。 有关详细说明,请参阅以下 URL:
https://issues.apache.org/jira/browse/HBASE-4990
七、故障排除
在本章中,我们将介绍:
- 故障排除工具
- 处理 XceiverCount 错误
- 处理“打开的文件太多”错误
- 处理“无法创建新的本机线程”错误
- 处理“HBase 忽略 HDFS 客户端配置”问题
- 处理 ZooKeeper 客户端连接错误
- 处理 ZooKeeper 会话过期错误
- 处理 EC2 上的 HBase 启动错误
简介
每个人都希望自己的 HBase 集群能够平稳稳定地运行,但有时集群并不能像预期的那样工作,特别是在集群配置不好的情况下。 本章介绍对以意外状态运行的群集进行故障排除时可以执行的操作。
在开始对群集进行故障排除之前,最好熟悉帮助我们恢复群集的工具。 有用的工具与深入了解 HBase 和您正在操作的集群一样重要。 在第一个食谱中,我们将介绍几个推荐的工具及其示例用法。
问题通常发生在缺少基本设置的群集上。 如果您的集群遇到问题,您应该做的第一件事是分析主日志文件,因为主日志文件充当集群的协调器服务。 希望一旦在日志文件中找到警告或错误级别日志,您就能够确定错误的根本原因。 区域服务器日志文件是您需要检查的另一个来源。 区域服务器日志文件通常包含与加载相关的错误日志,因为区域服务器处理集群的实际数据存储和访问。
HBase 运行在 HDFS 之上,它依赖 ZooKeeper 作为其协调服务。 有时,还需要调查 HDFS、MapReduce 和 ZooKeeper 日志。 默认情况下,所有这些日志文件都存储在安装文件夹的 logs
目录下。 当然,它可以在 log4j
属性文件中配置。
如果发现错误消息,请搜索http://search-hadoop.com/上的在线资源;很可能以前已经报告和讨论过此问题。 有一个很棒的 HBase 社区,你可以随时寻求帮助。 不过,在提问之前,不要忘记先订阅-http://hbase.apache.org/mail-lists.html。
在本章中,我们将回顾几个最棘手的问题。 我们将介绍这些问题的错误消息、发生原因以及如何使用故障排除工具修复这些问题。
故障排除工具
为了对 HBase 集群进行故障排除,除了扎实地了解您正在操作的集群之外,您使用的工具也很重要。 我们推荐以下故障排除工具:
ps:
这可用于查找使用最多内存和 CPU 的顶级进程- ClusterSSH 工具:此工具用于同时控制多个 SSH 会话
jps:
此工具显示当前用户的 Java 进程jmap:
此工具打印 Java 堆摘要jstat:
这是 Java 虚拟机统计监控工具hbase hbck:
此工具用于检查和修复区域一致性和表完整性hadoop fsck:
此工具用于检查 HDFS 一致性
在本食谱中,我们将描述这些工具的示例用法。
做好准备
启动您的 HBase 集群。
怎么做……
以下是我们将介绍的故障排除工具:
-
ps:
此工具用于查找占用大量内存的顶级进程。 以下命令按内存使用情况降序对进程进行排序:$ ps auxk -rss | less USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND hadoop 1346 1.2 6.6 1222680 115096 ? Sl 11:01 0:10 /usr/local/jdk1.6/bin/java -XX:OnOutOfMemoryError=kill -9 %p -Xmx1000m -ea -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -Dhbase.log.dir=/usr/local/hbase/logs ... org.apache.hadoop.hbase.regionserver.HRegionServer start
-
ClusterSSH tool: This tool is used to control multiple SSH sessions, simultaneously. In order to install ClusterSSH (cssh) on a Ubuntu desktop, run the following command:
$ sudo apt-get install clusterssh
还有一个针对 MacOSX 的 ClusterSSH 工具,名为
csshx
。 您可以从http://code.google.com/p/csshx/下载 csshx。 启动 ClusterSSH 工具以监视小型 HBase 集群。 例如,在 Mac OS X 上,运行以下命令以在 HBase 群集的每个节点上启动多个 SSH 会话:$ csshX --login hac master1 slave1 slave2 slave3
还将创建一个主窗口(以下屏幕截图中的第二个)。 在红色主窗口中,键入以下命令以在每个节点上运行
top
命令:$ top
您将看到与以下屏幕截图类似的屏幕:
-
jps:
此工具显示当前用户的 Java 进程。 要显示hadoop
用户的从节点上的 Java 进程,请运行以下命令:hadoop@slave1$ $JAVA_HOME/bin/jps 1254 DataNode 2445 Child 1970 TaskTracker 2481 Jps 1812 HRegionServer
-
jmap:
此工具打印 Java 堆摘要。 要打印前面提到的 HRegionServer 进程的 Java 堆摘要,请使用以下命令:hadoop@slave1$ $JAVA_HOME/bin/jmap -heap 1812 Attaching to process ID 1812, please wait... Debugger attached successfully. Client compiler detected. JVM version is 20.4-b02 using thread-local object allocation. Concurrent Mark-Sweep GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 1048576000 (1000.0MB) NewSize = 16777216 (16.0MB) MaxNewSize = 16777216 (16.0MB) OldSize = 50331648 (48.0MB) NewRatio = 7 SurvivorRatio = 8 PermSize = 12582912 (12.0MB) MaxPermSize = 67108864 (64.0MB) Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 15138816 (14.4375MB) used = 6533712 (6.2310333251953125MB) free = 8605104 (8.206466674804688MB) 43.1586723823052% used Eden Space: capacity = 13500416 (12.875MB) used = 6514736 (6.2129364013671875MB) free = 6985680 (6.6620635986328125MB) 48.25581670964806% used From Space: capacity = 1638400 (1.5625MB) used = 18976 (0.018096923828125MB) free = 1619424 (1.544403076171875MB) 1.158203125% used To Space: capacity = 1638400 (1.5625MB) used = 0 (0.0MB) free = 1638400 (1.5625MB) 0.0% used concurrent mark-sweep generation: capacity = 274624512 (261.90234375MB) used = 68805464 (65.61800384521484MB) free = 205819048 (196.28433990478516MB) 25.05437824865393% used Perm Generation: capacity = 26222592 (25.0078125MB) used = 15656912 (14.931594848632812MB) free = 10565680 (10.076217651367188MB) 59.70772073180256% used
-
jstat:
This is the Java Virtual Machine statistics monitoring tool. Run the following command to show the summary of the garbage collection statistics of a HRegionServer process:hadoop@slave1$ jstat -gcutil 1812 1000
输出如以下屏幕截图所示:
它是如何工作的.
ps
命令有一个 k
选项,我们可以使用它来指定排序顺序。 我们将 -rss
格式说明符传递给此选项。 这会导致 ps
命令按照进程的驻留集大小按降序对进程进行排序。 我们使用此方法查找使用内存最多的顶级进程。
群集 SSH 工具便于管理小型群集。 您可以使用它在多个服务器上同时调用相同的命令。 与前面一样,我们打开一个到集群中的主节点和每个从节点的会话。 然后,我们通过主窗口在每台服务器上调用 top
命令。 它充当群集的简单监控系统。
jps
是用于管理 Java 进程的小型动手工具。 它显示了当前用户的 Java 进程。
步骤 4 显示了在运行 DataNode、RegionServer 和 TaskTracker 守护进程的从节点上执行 jps
命令的输出。 Hadoop 和 HBase 都是用 Java 编写的;这使得 jps
对我们来说是一个有用的工具。
jmap
是打印 Java 堆摘要的工具。 如步骤 5 所示,我们使用 jmap -heap <PID>
命令显示 HRegionServer 守护进程的堆摘要。 jmap
将打印 HRegionServer 守护进程的堆配置和使用情况。
jstat
是一个显示Java 虚拟机(JVM)统计监控数据的工具。 在本例中, jstat
连接到 HRegionServer(PID 1812)的 JVM,每隔 1000 毫秒采集样本,并显示由 -gcutil
选项指定的输出。 输出显示年轻一代收集不断发生。 例如,在第 4 和第 5 个样本之间,收集花费了 0.216 秒(YGCT列中的8.134到7.918),并将对象从伊甸园空间(E)提升到旧空间(O),导致旧空间利用率从55.99%增加到58.73%。 在收集之前,幸存者空间(S0)被100%利用,但在此收集之后仅被25.85%利用。
另请参阅
处理 XceiverCount 错误
在本食谱中,我们将介绍如何对 DataNode 日志中显示的以下 XceiverCount 错误进行故障排除:
2012-02-18 17:08:10,695 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: DatanodeRegistration(10.166.111.191:50010, storageID=DS-2072496811-10.168.130.82-50010-1321345166369, infoPort=50075, ipcPort=50020):DataXceiver
java.io.IOException: xceiverCount 257 exceeds the limit of concurrent xcievers 256
at org.apache.hadoop.hdfs.server.datanode.DataXceiver.run (DataXceiver.java:92)
at java.lang.Thread.run(Thread.java:662)
做好准备
登录到您的主节点。
怎么做……
以下是修复 XceiverCount 错误的步骤:
-
将以下代码片段添加到 HDFS 设置文件(
hdfs-site.xml
):hadoop@master1$ vi $HADOOP_HOME/conf/hdfs-site.xml <property> <name>dfs.datanode.max.xcievers</name> <value>4096</value> </property>
-
在群集中同步
hdfs-site.xml
文件:hadoop@master1$ for slave in `cat $HADOOP_HOME/conf/slaves` do rsync -avz $HADOOP_HOME/conf/ $slave:$HADOOP_HOME/conf/ done
-
从主节点重新启动 HDFS 和 HBase:
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh hadoop@master1$ $HADOOP_HOME/bin/stop-dfs.sh hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
它是如何工作的.
dfs.datanode.max.xcievers
(是的,这是拼写错误)设置定义了 HDFS DataNode 在任何给定时间将服务的文件数的上限。 其默认值为 256
,太小,无法在 HDFS 上运行 HBase。 如果您的 DataNode 达到此上限,您将在 DataNode 日志中看到一个错误日志,通知您已超过 Xciever 计数。
我们建议在现代机器上将其设置为更高的值,例如 4096。 更改此设置后,您需要在整个群集中同步修改后的 hdfs-site.xml
文件,然后重新启动 HDFS 以应用更改。
处理“打开的文件太多”错误
在本食谱中,我们将介绍如何对以下 DataNode 日志中显示的错误进行故障排除:
2012-02-18 17:43:18,009 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: DatanodeRegistration(10.166.111.191:50010, storageID=DS-2072496811-10.168.130.82-50010-1321345166369, infoPort=50075, ipcPort=50020):DataXceiver
java.io.FileNotFoundException: /usr/local/hadoop/var/dfs/data/current/subdir6/blk_-8839555124496884481 (Too many open files)
at java.io.RandomAccessFile.open(Native Method)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:216)
at org.apache.hadoop.hdfs.server.datanode.FSDataset.getBlockInputStream(FSDataset.java:1068)
做好准备
要解决此问题,您需要在群集的每个节点上拥有 root 权限。 我们假设您使用 hadoop
用户启动 HDFS 集群。
怎么做……
要修复“打开的文件太多”错误,请在群集的每个节点上执行以下步骤:
-
通过向
/etc/security/limits.conf
文件添加以下属性来增加hadoop
用户的打开文件号:$ vi /etc/security/limits.conf hadoop soft nofile 65535 hadoop hard nofile 65535
-
将以下行添加到
/etc/pam.d/login
文件:$ vi /etc/pam.d/login session required pam_limits.so
-
注销,然后以
hadoop
用户身份重新登录。 -
通过运行以下命令确认已提高打开文件限制:
$ ulimit -n 65535
-
从主节点重新启动 HDFS 和 HBase:
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh hadoop@master1$ $HADOOP_HOME/bin/stop-dfs.sh hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
它是如何工作的.
HBase 是一个运行在 Hadoop 上的数据库,就像其他数据库一样,它可以同时打开许多文件。 另一方面,Linux 限制了进程可以打开的文件描述符的数量。 默认限制为 1024,对于 HBase 来说太小了。 如果 hadoop
用户的打开文件数超过这个上限,您将在 DataNode 日志中看到一个错误,指示打开的文件太多。
要平稳运行 HBase,您需要为运行 HDFS 和 HBase 的用户增加打开文件描述符的最大数量;在我们的示例中,即 hadoop
用户。
在步骤 1 中,我们在 /etc/security/limits.conf
文件中设置了 hadoop
用户的打开文件限制,然后在步骤 2 中修改了 /etc/pam.d/login
文件,以便更改在用户下次登录系统时生效。 软限制(软 nofile)
)是操作系统内核对打开的文件描述符的数量强制执行的值。 硬限制(HARD nofile)
)用作软限制的上限。 ulimit
是一个为当前用户设置和显示系统范围内资源使用限制的程序。 正如您在步骤 4 中看到的, ulimit -n
命令显示了 hadoop
用户的打开文件限制。
不要忘记重新启动 HDFS 群集以应用更改。
还有更多...
您可能还想知道 hadoop
用户当前打开的文件号。 要获取此信息,请使用 lsof
命令:
$ lsof -uhadoop | wc -l
另请参阅
- 更改内核设置配方,在第 1 章,设置 HBase 群集
- 在本章中,处理“无法创建新的本机线程”错误
处理“无法创建新的本机线程”错误
在本指南中,我们将介绍如何对以下 RegionServer 日志中显示的错误进行故障排除:
2012-02-18 18:46:04,907 WARN org.apache.hadoop.hdfs.DFSClient: DataStreamer Exception: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:640)
at org.apache.hadoop.hdfs.DFSClient$DFSOutputStream$DataStreamer.run(DFSClient.java:2830)
做好准备
要修复此错误,您将需要群集的每个节点上的 root 权限。 我们假设您使用 hadoop
用户启动 HDFS 和 HBase 集群。
怎么做……
要修复“无法创建新的本机线程”错误,请在群集的每个节点上执行以下步骤:
-
通过向
/etc/security/limits.conf
文件添加以下属性来增加hadoop
用户的最大进程数:$ vi /etc/security/limits.conf hadoop soft nproc 32000 hadoop hard nproc 32000
-
将以下行添加到
/etc/pam.d/login
文件:$ vi /etc/pam.d/login session required pam_limits.so
-
注销,然后以
hadoop
用户身份重新登录。 -
通过运行以下命令,确保已增加打开进程限制:
$ ulimit -u 32000
-
从主节点重新启动 HDFS 和 HBase:
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh hadoop@master1$ $HADOOP_HOME/bin/stop-dfs.sh hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
它是如何工作的.
Linux 限制了用户可以同时执行的进程数量。 在高负载 HBase 集群中,较低的 nproc
设置可能表现为 OutOfMemoryError
异常,如前面所示。 如果 hadoop
用户的运行进程数超过了 nproc
限制,您将在 RegionServer 日志中看到“无法创建新的本机线程”的错误。
为了顺利运行 HBase,您需要提高运行 HDFS 和 HBase 的用户的 nproc
限制。
我们在步骤 1 中在 /etc/security/limits.conf
文件中设置了 hadoop
用户的 nproc
限制,然后在步骤 2 中修改了 /etc/pam.d/login
文件,以便更改在用户下次登录系统时生效。 软限制(软 nproc)
是操作系统内核对进程数强制执行的值。 硬限制(HARD nproc)
)用作软限制的上限。
注销后,我们再次登录并运行 ulimit -u
程序,以显示当前用户的进程数限制。
最后,我们重新启动 HDFS 和 HBase 以应用更改。
还有更多...
要查看 hadoop
用户的当前线程数,请键入以下命令:
$ ps -o pid,comm,user,thcount -u hadoop
PID COMMAND USER THCNT
1349 su hadoop 1
1350 bash hadoop 1
1429 java hadoop 32
1580 java hadoop 14
1690 java hadoop 48
1819 ps hadoop 1
输出的 THCNT
列是 hadoop
用户的每个进程的线程号。
另请参阅
- 更改内核设置,第 1 章,设置 HBase 群集
- 在本章中,处理“打开的文件太多”错误
处理“HBase 忽略 HDFS 客户端配置”问题
您可能已经注意到,HBase 会忽略您的 HDFS 客户端配置,例如 dfs.replication
设置。 在以下示例中,我们为 HDFS 客户端设置了复制因子 2
:
$ grep -A 1 "dfs.replication" $HADOOP_HOME/conf/hdfs-site.xml
<name>dfs.replication</name>
<value>2</value>
但是,HDFS 上的 HBase 文件显示的因子为 3
,这是 HDFS 的默认复制因子:
这与我们预期的不同-复制因子预期为 2,但实际值为 3。
在本食谱中,我们将描述为什么会发生这种情况,以及如何修复它。
做好准备
以启动 HDFS 和 HBase 的用户身份登录到您的主节点。 我们假设您使用的是 HDFS 和 HBase 的 hadoop
用户。
怎么做……
以下是将 HDFS 客户端配置应用到 HBase 的步骤:
-
在 HBase 配置目录下添加 HDFS 设置文件(hdfs-site.xml)的符号链接:
$ hadoop@master1$ ln -s $HADOOP_HOME/conf/hdfs-site.xml $HBASE_HOME/conf/hdfs-site.xml
-
在群集中同步此更改:
hadoop@master1$ for slave in `cat $HBASE_HOME/conf/regionservers` do rsync -avz $HBASE_HOME/conf/ $slave:$HBASE_HOME/conf/ done
-
重新启动 HBase。 现在,新创建的 HBase 文件将具有 HDFS 客户端配置。 实际复制系数值将为 2,正如我们预期的那样:
hadoop@master1$ $HBASE_HOME/bin/stop-hbase.sh hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
它是如何工作的.
HDFS 客户端配置在 hdfs-site.xml
文件中设置。 要将 HDFS 客户端配置应用到 HBase,我们需要添加此文件中的设置,并将 hdfs-site.xml
文件添加到 HBase 的类路径中。 要做到这一点,最简单的方法是在 HBase 配置目录下创建 hdfs-site.xml
文件的符号链接,然后在整个集群中同步它。
重新启动 HBase 后,您将注意到将应用 HDFS 客户端配置。
处理 ZooKeeper 客户端连接错误
在本食谱中,我们将介绍如何对以下 RegionServer 日志中显示的 ZooKeeper 客户端连接错误进行故障排除:
2012-02-19 15:17:06,199 WARN org.apache.zookeeper.ClientCnxn: Session 0x0 for server ip-10-168-47-220.us-west-1.compute.internal /10.168.47.220:2181, unexpected error, closing socket connection and attempting reconnect
java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcher.read0(Native Method) at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:21) at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198) at sun.nio.ch.IOUtil.read(IOUtil.java:166) at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:243) at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:66)
做好准备
登录到您的动物园管理员仲裁节点。
怎么做……
以下是修复 ZooKeeper 客户端连接错误的步骤:
-
将以下内容添加到每个 ZooKeeper 仲裁节点上的 ZooKeeper 配置文件(
zoo.cfg
)中:$ vi $ZOOKEEPER_HOME/conf/zoo.cfg maxClientCnxns=60
-
重新启动 ZooKeeper 以应用更改:
$ $ZOOKEEPER_HOME/bin/zkServer.sh stop $ $ZOOKEEPER_HOME/bin/zkServer.sh start
它是如何工作的.
在 HBase 群集上运行 MapReduce 作业时,通常会出现此错误。 ZooKeeper 有一个 maxClientCnxns
设置,用于限制单个客户端可以与 ZooKeeper 集合中的单个成员建立的并发连接的数量。 每个区域服务器都是 ZooKeeper 客户端;如果区域服务器的并发连接数超过此最大客户端连接限制,它将无法创建到 ZooKeeper 集合的新连接。 这就是发生上述错误的原因。
要修复此错误,我们需要为 maxClientCnxns
设置设置一个更高的值,并重新启动 ZooKeeper 以应用更改。
还有更多...
从 ZooKeeper 3.4.0 开始, maxClientCnxns
的默认值已更改为 60。 它应该可以很好地适用于许多应用。
要查看从客户端到特定 ZooKeeper 仲裁节点的当前连接数,请运行以下命令:
$ echo "cons" | nc localhost 2181 | grep "your.client.ip.address" | wc l
将 localhost
替换为 ZooKeeper 仲裁节点的主机名,并将"your.client.ip.address"
替换为客户端的 IP 地址。
处理 ZooKeeper 会话过期错误
在本食谱中,我们将介绍如何对 RegionServer 日志中显示的以下 ZooKeeper 会话过期错误进行故障排除:
2012-02-19 16:49:15,405 WARN org.apache.hadoop.hbase.regionserver.HRegionServer: Failed deleting my ephemeral node
org.apache.zookeeper.KeeperException$SessionExpiredException: KeeperErrorCode = Session expired for /hbase/rs/ip-10-168-37-91.us-west-1.compute.internal,60020,1329635463251 at org.apache.zookeeper.KeeperException.create(KeeperException.java:127) at org.apache.zookeeper.KeeperException.create(KeeperException.java:51) at org.apache.zookeeper.ZooKeeper.delete(ZooKeeper.java:868) at org.apache.hadoop.hbase.zookeeper.RecoverableZooKeeper.delete(RecoverableZooKeeper.java:107)
这个问题非常重要,因为如果主服务器或区域服务器与 ZooKeeper 仲裁断开连接,它们将自动关闭。
做好准备
登录到发生此错误的服务器。
怎么做……
以下是修复 ZooKeeper 会话过期问题的步骤:
-
检查
hbase-env.sh
;确保为 HBase 守护进程提供足够的 RAM。 对于繁重的群集,默认的 1 GB 是不够的:$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_HEAPSIZE=8000
-
Run the
vmstat
command to show the virtual memory statistics:$ vmstat 1
- 检查名为si(换入)和so(换出)的交换列,并确保不交换。
-
Use the
jstat
command to show the Java Garbage Collection (GC) statistics:$ jstat -gcutil <java_process_pid> 1000
- 检查FGCT列,确保 RegionServer 不会遇到长时间的 GC 暂停。
-
使用
top
命令显示 CPU 统计信息。 确保 RegionServer 线程有足够的 CPU 资源。 MapReduce 可能会使用大量 CPU 资源,导致 RegionServer 饥饿并陷入长时间的 GC 暂停。 -
如果 MapReduce 占用太多 CPU 资源,请考虑减少区域服务器上的 MAP/Reduce 插槽数量。 调整下列值:
$ vi $HADOOP_HOME/conf/mapred-site.xml <property> <name>mapred.tasktracker.map.tasks.maximum</name> <value>2</value> </property> <property> <name>mapred.tasktracker.reduce.tasks.maximum</name> <value>1</value> </property>
-
考虑通过编辑
hbase-site.xml
文件$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>zookeeper.session.timeout</name> <value>120000</value> </property>
来增加 ZooKeeper 会话超时
-
增加每个 ZooKeeper 仲裁节点上的 ZooKeeper 最大会话超时。 修改
zoo.cfg
文件以增加maxSessionTimeout
值:$ vi $ZOOKEEPER_HOME/conf/zoo.cfg maxSessionTimeout= 120000
-
如果您更改了
zoo.cfg
文件$ $ZOOKEEPER_HOME/bin/zkServer.sh stop $ $ZOOKEEPER_HOME/bin/zkServer.sh start
,请在每个 ZooKeeper 仲裁节点上重新启动 ZooKeeper
-
跨群集中同步您修改的文件,然后重新启动 Hadoop/HBase 群集。
它是如何工作的.
此错误通常发生在过载的 HBase 群集上。 HBase Master 和 RegionServer 守护进程充当 ZooKeeper 客户端;如果客户端无法在配置的时间内与 ZooKeeper 仲裁进行通信,则连接将超时,并会出现此错误。
ZooKeeper 连接超时的两个最可能原因如下:
- 长时间的 JVM GC 暂停
- 配置的超时时间太短
从第 1 步到第 5 步,我们尝试找出是不是长 GC 导致了错误。 HBase 需要足够的 RAM 才能平稳运行。 对于繁重的群集,1 GB 的默认大小是不够的。 因此,将 hbase-env.sh
文件中的 HBASE_HEAPSIZE
值更改为更高的值(例如,8 GB),但小于 16 GB。 我们建议将 HBase 堆大小设置为小于 16 GB 的原因是,如果我们在这里使用非常大的堆大小,GC 将需要非常长的时间才能完成,这是我们必须避免的。
确保您的 RegionServer 不交换。 vmstat
命令可用于查看是否发生了交换。 在步骤 2 中,我们使用 vmstat
命令以 1 秒为间隔显示虚拟内存统计信息。 您应该更改 vm.swappiness
内核设置,以避免交换。 我们将在第 8 章,基本性能调优中的将 vm.swappness 设置为 0 以避免交换配方中进行描述。
jstat
是一款 Java 虚拟机统计监控工具。 我们在步骤 3 中使用 -gcutil
选项,以 1 秒为间隔显示特定 Java 进程的 Java GC 统计数据。 输出的FGCT列是总的完整 GC 时间;检查该列以查看是否发生了长时间的 GC 暂停。
GC 暂停时间长的另一个原因是 RegionServer 进程可能没有足够的 CPU 资源。 在 HBase 群集上运行繁重的 MapReduce 作业时尤其如此。 在这种情况下,如果您正在运行繁重的 MapReduce 作业,请使用 MapReduce 配置 mapred.tasktracker.map.tasks.maximum
和 mapred.tasktracker.reduce.tasks.maximum
来控制在 TaskTracker 上同时生成的贴图/缩减的数量。
如果希望增加 ZooKeeper 会话超时,请在 hbase-site.xml
文件中设置 zookeeper.session.timeout
,在 ZooKeeper 配置文件(zoo.cfg
)中设置 maxSessionTimeout
。 maxSessionTimeout
选项是 ZooKeeper 服务器端配置。 它是客户端会话超时的上限,因此它的值必须大于 HBase zookeeper.session.timeout
值。
备注
请注意,设置更高的超时意味着群集将至少花费同样多的时间来故障转移出现故障的 RegionServer。 您需要考虑您的系统是否可以接受。
另请参阅
- 故障排除工具,在本章中
- 将 vm.swappity 设置为 0 以避免交换配方,请参见第 8 章,基本性能调整
处理 EC2 上的 HBase 启动错误
在本食谱中,我们将介绍如何对以下主日志中显示的 HBase 启动错误进行故障排除:
2011-12-10 14:04:57,422 ERROR org.apache.hadoop.hbase.HServerAddress: Could not resolve the DNS name of ip-10-166-219-206.us-west-1.compute.internal
2011-12-10 14:04:57,423 FATAL org.apache.hadoop.hbase.master.HMaster: Unhandled exception. Starting shutdown.
java.lang.IllegalArgumentException: hostname can't be null
at java.net.InetSocketAddress.<init>(InetSocketAddress.java:121)
at org.apache.hadoop.hbase.HServerAddress.getResolvedAddress(HServerAddress.java:108)
at org.apache.hadoop.hbase.HServerAddress.<init>(HServerAddress.java:64)
此错误通常发生在停止并重新启动 EC2 实例之后。 原因是 HBase 将区域位置存储在它的“系统”-根-和 META 表中。 位置信息中包含内部 EC2 DNS 名称。 停止 EC2 实例将更改此 DNS 名称。 由于 DNS 名称更改,HBase 将无法将其系统表中的旧 DNS 名称解析为新名称,从而导致上述错误消息。
在本指南中,我们将介绍如何解决此问题,以便您可以随意停止/启动用于 HBase 的 EC2 实例(如果您的 HDFS 将数据保存在 EC2 实例存储上,则在重新启动 EC2 实例之前,您需要将数据保存在其他存储上,如 Amazon S3)。
做好准备
确保您已经为 HBase 设置了 Amazon EC2 环境。 如果您还没有准备好,请参考第 1 章,设置 HBase 集群中的在 Amazon EC2 上做好准备食谱,了解如何在 EC2 上做好准备以便在 EC2 上运行 HBase。
您需要在群集的每个节点上拥有 root 权限的帐户,才能运行我们稍后创建的脚本。
怎么做……
以下是修复 EC2 上的 HBase 启动错误的步骤:
-
创建一个
ec2-running-hosts.sh
脚本,如以下代码片段所示:$ vi ec2-running-hosts.sh #!/bin/bash ec2-describe-instances > /tmp/all-instances ip_type=$1 if [ "$ip_type" == "private" ]; then addresses=`grep ^INSTANCE /tmp/all-instances | cut -f18` elif [ "$ip_type" == "public" ]; then addresses=`grep ^INSTANCE /tmp/all-instances | cut -f17` else echo "Usage: `basename $0` private|public" exit -1 fi for address in $addresses do instance_id=`grep $address /tmp/all-instances | cut -f2` dns=`grep $address /tmp/all-instances | cut -f5` host=`grep ^TAG /tmp/all-instances | grep $instance_id | cut -f5` echo -e "${address}\t${dns}\t${host}" done
-
创建一个
update-ec2-hosts.sh
脚本,如以下代码片断所示。 将脚本中的YOUR_PASSWORD
替换为您用来运行脚本的帐户的密码:$ vi update-ec2-hosts.sh #!/bin/bash if [ $# -lt 2 ]; then echo "Usage: `basename $0` host-file target-host" exit 1 fi host_file=$1 target_host=$2 bin=`dirname $0` bin=`cd $bin;pwd` echo "updating hosts file on $target_host using $host_file" # backup ssh -f $target_host "cp /etc/hosts /tmp/hosts.org" cp $bin/hosts.template /tmp/hosts.update cat $host_file >> /tmp/hosts.update scp /tmp/hosts.update $target_host:/tmp # chmod 700 this file as we write password here ssh -f -t -t -t $target_host "sudo -S cp /tmp/hosts.update /etc/hosts <<EOF YOUR_PASSWORD EOF " echo "[done] update hosts file on $target_host"
-
确保
update-ec2-hosts.sh
脚本只对您可读:$ chmod 700 update-ec2-hosts.sh
-
将
/etc/hosts
文件从 EC2 实例复制到与update-ec2-hosts.sh
脚本相同的目录中,并将其重命名为hosts.template:
$ cp /etc/hosts hosts.template
-
创建包含以下内容的
update-ec2-private-hosts.sh
脚本。 将NS_HOSTNAME
的值替换为您的名称服务器的主机名:$ vi update-ec2-private-hosts.sh #!/bin/bash bin=`dirname $0` bin=`cd $bin;pwd` NS_HOSTNAME="ns1" $bin/ec2-running-hosts.sh private | tee /tmp/ec2-running-host cp $bin/hosts.template /tmp/hosts.update cat /tmp/ec2-running-host >> /tmp/hosts.update while read line do host=`echo "$line" | cut -f3` if [ "$host" == "$NS_HOSTNAME" ]; then # do not update name server continue fi echo echo "Updating $host" bash $bin/update-ec2-hosts.sh "/tmp/ec2-running-host" "$host" sleep 1 done < /tmp/ec2-running-host
-
创建一个
update-ec2-hmaster-hosts.sh
脚本,如以下代码片断所示。 将HMASTER_HOSTNAME
和RS_HOSTNAMES
的值分别替换为 Master 和 RegionServer 节点的主机名:$ vi update-ec2-hmaster-hosts.sh #!/bin/bash bin=`dirname $0` bin=`cd $bin;pwd` IFS_BAK=$IFS IFS=" " HMASTER_HOSTNAME="master1" RS_HOSTNAMES="slave[123]" scp $HMASTER_HOSTNAME:/tmp/hosts.org /tmp scp $HMASTER_HOSTNAME:/etc/hosts /tmp rm -f /tmp/hosts.done while read line do echo "$line" | egrep -q "$RS_HOSTNAMES" if [ $? -ne 0 ]; then echo -e "${line}" >> /tmp/hosts.done else slave=`echo "$line" | cut -f3` original_private_dns=`grep "$slave" /tmp/hosts.org | cut -f2` echo -e "${line}\t${original_private_dns}" >> /tmp/hosts.done fi done < /tmp/hosts bash $bin/update-ec2-hosts.sh "/tmp/hosts.done" "$HMASTER_HOSTNAME"
-
将前面步骤中创建的脚本带到 EC2 上的名称服务器,并从那里运行
update-ec2-private-hosts.sh
文件:hac@ns1$ ./update-ec2-private-hosts.sh 10.176.202.34 ip-10-176-202-34.us-west-1.compute.internal master1 10.160.49.250 ip-10-160-49-250.us-west-1.compute.internal ns1 10.160.47.194 ip-10-160-47-194.us-west-1.compute.internal slave1 10.176.23.117 ip-10-176-23-117.us-west-1.compute.internal client1 10.160.39.197 ip-10-160-39-197.us-west-1.compute.internal slave2 10.168.41.175 ip-10-168-41-175.us-west-1.compute.internal slave3 Updating master1 updating hosts file on master1 using /tmp/ec2-running-host hosts.update 100% 657 0.6KB/s 00:00 [done] update hosts file on master1 [sudo] password for hac: Updating slave1 ...
- 脚本使用 EC2API 更新了每个节点上的
/etc/hosts
文件。
- 脚本使用 EC2API 更新了每个节点上的
-
如果
update-ec2-private-hosts.sh
已成功完成,则运行update-ec2-hmaster-hosts.sh
:hac@ns1$ ./update-ec2-hmaster-hosts.sh hosts.org 100% 1457 1.4KB/s 00:00 hosts 100% 657 0.6KB/s 00:00 updating hosts file on master1 using /tmp/hosts.done hosts.update 100% 1058 1.0KB/s 00:00 [done] update hosts file on master1
- 每个从节点的旧 DNS 条目将被附加到主节点上的/etc/hosts 文件中。
-
运行以下命令检查主机文件:
hac@master1$ grep "slave" /etc/hosts
以下屏幕截图显示了该命令的输出:
以 hadoop
用户身份登录到您的主节点,并在此节点上启动 HBase:
hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh
starting namenode, logging to /usr/local/hadoop/logs/hadoop-hadoop-namenode-master1.out
slave1: Warning: Permanently added the RSA host key for IP address '10.160.47.194' to the list of known hosts.
slave2: Warning: Permanently added the RSA host key for IP address '10.160.39.197' to the list of known hosts.
slave3: Warning: Permanently added the RSA host key for IP address '10.168.41.175' to the list of known hosts.
slave1: starting datanode, logging to /usr/local/hadoop/logs/hadoop-hadoop-datanode-slave1.out
slave3: starting datanode, logging to /usr/local/hadoop/logs/hadoop-hadoop-datanode-slave3.out
slave2: starting datanode, logging to /usr/local/hadoop/logs/hadoop-hadoop-datanode-slave2.out
hadoop@master1$ $ZOOKEEPER_HOME/bin/zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/current/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
hadoop@master1$ $HBASE_HOME/bin/start-hbase.sh
starting master, logging to /usr/local/hbase/logs/hbase-hadoop-master-master1.out
slave3: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave3.out
slave1: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave1.out
slave2: starting regionserver, logging to /usr/local/hbase/logs/hbase-hadoop-regionserver-slave2.out
它是如何工作的.
作为一个简单的解决方案,每次在启动 HBase 之前停止和重新启动实例时,我们都会更新集群中每个节点上的 /etc/hosts
文件,然后将每个从节点的旧 DNS 条目附加到主节点的 /etc/hosts
文件中。 这样,HBase Master 就能够将其系统表中的旧 DNS 条目解析为更新后的新 IP 地址,并使用系统表中的新 DNS 条目对其进行更新。
为了实现这个想法,我们在步骤 1 中创建了 ec2-running-hosts.sh
脚本,并调用 ec2-describe-instances
EC2API,以获取有关所有正在运行的 EC2 实例的信息,并对每个正在运行的实例的 IP 地址、DNS 名称和用户数据(主机名)进行 grep。 这些是我们需要更新到每个节点的 /etc/hosts
文件的新 DNS 条目。
我们在步骤 2 中创建的 update-ec2-hosts.sh
脚本是一个实用程序脚本,用于通过 SSH 连接到特定服务器并覆盖该服务器中的 /etc/hosts
文件,而无需人工交互。 该脚本还会在覆盖原始 /etc/hosts
文件之前对其进行备份。 我们在脚本中写入帐户密码,这样就不需要在每个节点上输入密码。 这就是为什么在步骤 3 中注意到脚本应该只对您自己可读的原因。
在步骤 5 中创建的 update-ec2-private-hosts.sh
脚本使我们在前面步骤中创建的两个脚本协同工作以获得新的 DNS 条目,将它们附加到 hosts.template
文件,然后使用 hosts.template
文件覆盖除 DNS 名称服务器之外的每个运行的 EC2 实例上的 /etc/hosts
文件。
在步骤 6 中创建的 update-ec2-hmaster-hosts.sh
脚本执行简单的工作,即将每个从节点的旧 DNS 名称映射到新的 /etc/hosts
文件,将新的 IP 地址附加到,以便 HBase Master 可以将存储在其系统表中的旧 DNS 条目解析为新的 IP 地址。 之后,脚本调用 update-ec2-hosts.sh
,使用新附加的文件覆盖主节点上的 /etc/hosts
文件。
在按顺序执行 update-ec2-private-hosts.sh
和 update-ec2-hmaster-hosts.sh
脚本之后,我们现在就可以正常启动 HDFS 和 HBase 了。
如您所见,如果您只想在需要在 HBase 上运行 HBase 时启动 HBase EC2 实例,那么这些脚本是简单但有用的。
还有更多...
您可能还希望将 EC2 实例的公共 DNS 名称更新为本地 PC 上的 /etc/hosts
文件,以便您可以从 Web 浏览器访问 HBase Web UI。 以下是执行此操作的步骤:
-
创建一个
update-ec2-public-hosts.sh
脚本,如以下代码片断所示,并将其放在与我们在上一节中创建的脚本相同的目录下:$ vi update-ec2-public-hosts.sh #!/bin/bash bin=`dirname $0` bin=`cd $bin;pwd` $bin/ec2-running-hosts.sh public | tee /tmp/ec2-running-host # backup cp /etc/hosts /tmp/hosts.org # update cp $bin/hosts.local /tmp/hosts.update cat /tmp/ec2-running-host >> /tmp/hosts.update sudo cp /tmp/hosts.update /etc/hosts echo "[done] update EC2 public hostname in hosts file"
-
将您的
/etc/hosts
文件复制到与update-ec2-public-hosts.sh
脚本相同的目录,并将其重命名为hosts.local:
$ cp /etc/hosts hosts.local
-
在本地 PC 上运行
update-ec2-public-hosts.sh
脚本。 脚本将要求您输入帐户密码,因为它需要 root 权限才能更新/etc/hosts
文件:184.72.22.179 ip-10-176-202-34.us-west-1.compute.internal master1 ... 184.169.243.211 ip-10-168-41-175.us-west-1.compute.internal slave3 Password: [done] update EC2 public hostname in hosts file
-
现在,打开您的 Web 浏览器并输入 URL
http://master1:60010/master.jsp
。 您将获得 HBase Web 用户界面。 将master1
替换为您的主节点的主机名。
备注
请注意,您需要一台 MacOSX 或 Linux PC 才能使用此脚本。
另请参阅
- 为 Amazon EC2 做好准备,在第 1 章,设置 HBase 群集
八、基本性能调整
在本章中,我们将介绍:
- 设置 Hadoop 以分散磁盘 I/O
- 使用网络拓扑脚本使 Hadoop 机架感知
- 使用
noatime
和nodiratime
挂载磁盘 - 将
vm.swappiness
设置为 0 以避免交换 - Java GC 和 HBase 堆设置
- 使用压缩
- 管理压缩
- 管理区域拆分
简介
性能是 HBase 集群行为最有趣的特征之一。 这对管理员来说是一项具有挑战性的操作,因为性能调优不仅需要深入了解 HBase,还需要深入了解 Hadoop、Java 虚拟机垃圾收集(JVM GC)以及操作系统的重要调优参数。
典型的 HBase 集群结构如下图所示:
集群中有几个组件-ZooKeeper 集群、HBase 主节点、区域服务器、Hadoop 分布式文件系统(HDFS)和 HBase 客户端。
ZooKeeper 集群充当整个 HBase 集群的协调服务,处理主服务器选择、根区域服务器查找、节点注册等。 主节点不执行繁重的任务。 它的工作包括区域分配和故障转移、日志拆分和负载均衡。 区域服务器保存实际区域;它们处理对托管区域的 I/O 请求,将内存中的数据存储(MemStore)刷新到 HDFS,以及拆分和压缩区域。 HDFS 是 HBase 存储其数据文件(StoreFile)和预写日志(WAL)的地方。 我们通常有一个 HBase 区域服务器与 HDFS DataNode 运行在同一台机器上,但这不是强制性的。
HBase 客户端提供访问 HBase 集群的 API。 要与集群通信,客户端需要找到持有特定行键范围的区域服务器;这称为区域查找。 HBase 有两个系统表来支持区域查找- -ROOT-
表和 .META
表。 桌子。
-ROOT-
表用于引用 .META
中的区域。 表,而 .META
。 该表包含对所有用户区域的引用。 首先,客户端查询 ZooKeeper 以找到 -ROOT-
表位置(部署它的区域服务器);然后查询 -ROOT-
表,然后查询 .META
。 表中,查找包含特定区域的区域服务器。 客户端还缓存区域位置,以避免查询 ZooKeeper、 -ROOT-
和 .META
。 每次都有桌子。
有了这些背景知识,我们将在本章描述如何调优 HBase 以获得更好的性能。
除了 HBase 本身,其他调优点包括 Hadoop 配置、JVM 垃圾收集设置和操作系统内核参数。 这些与调整 HBase 本身一样重要。 在本章中,我们还将介绍调整这些配置的方法。
设置 Hadoop 以分散磁盘 I/O
现代服务器通常具有多个磁盘设备以提供大存储容量。 这些磁盘通常按照出厂设置配置为 RAID 阵列。 这对许多情况都有好处,但对 Hadoop 不好。
Hadoop 从节点在其本地磁盘上存储 HDFS 数据块和 MapReduce 临时文件。 这些本地磁盘操作得益于使用多个独立磁盘来分散磁盘 I/O。
在本指南中,我们将介绍如何设置 Hadoop 以使用多个磁盘来分散其磁盘 I/O。
做好准备
我们假设每个 DataNode 节点都有多个磁盘。 这些磁盘采用JBOD(就是一堆磁盘)或 RAID0 配置。 假设磁盘挂载在 /mnt/d0, /mnt/d1
、...、 /mnt/dn
,启动 HDFS 的用户对每个挂载点都有写权限。
怎么做……
要将 Hadoop 设置为分散磁盘 I/O,请按照以下说明操作:
-
在每个 DataNode 节点上,在每个磁盘上为 HDFS 创建目录以存储其数据块:
hadoop$ mkdir -p /mnt/d0/dfs/data hadoop$ mkdir -p /mnt/d1/dfs/data ... hadoop$ mkdir -p /mnt/dn/dfs/data
-
将以下代码添加到 HDFS 配置文件(hdfs-site.xml):
hadoop@master1$ vi $HADOOP_HOME/conf/hdfs-site.xml <property> <name>dfs.data.dir</name> <value>/mnt/d0/dfs/data,/mnt/d1/dfs/data,...,/mnt/dn/dfs/data </value> </property>
-
在群集中同步修改后的
hdfs-site.xml
文件:hadoop@master1$ for slave in `cat $HADOOP_HOME/conf/slaves` do rsync -avz $HADOOP_HOME/conf/ $slave:$HADOOP_HOME/conf/ done
-
重新启动 HDFS:
hadoop@master1$ $HADOOP_HOME/bin/stop-dfs.sh hadoop@master1$ $HADOOP_HOME/bin/start-dfs.sh
它是如何工作的.
我们推荐将 JBOD 或 RAID0 用于 DataNode 磁盘,因为您不需要 RAID 的冗余,因为 HDFS 使用节点之间的复制来确保其数据冗余。 因此,单个磁盘发生故障时不会丢失数据。
选择 JBOD 还是 RAID0? 从理论上讲,JBOD 配置比 RAID 配置具有更好的性能。 这是因为,在 RAID 配置中,您必须等待阵列中最慢的磁盘完成,然后才能完成整个写入操作,这使得平均 I/O 时间相当于最慢的磁盘的 I/O 时间。 在 JBOD 配置中,速度较快的磁盘上的操作将独立于速度较慢的磁盘完成,这使得平均 I/O 时间比速度最慢的磁盘快。 然而,企业级 RAID 卡可能会带来很大的不同。 在决定使用哪种配置之前,您可能希望对 JBOD 和 RAID0 配置进行基准测试。
对于 JBOD 和 RAID0 配置,您将在不同的路径挂载磁盘。 这里的关键点是将 dfs.data.dir
属性设置为每个磁盘上创建的所有目录。 dfs.data.dir
属性指定 DataNode 应将其本地块存储在何处。 通过将其设置为逗号分隔的多个目录,DataNode 以循环方式跨所有磁盘存储数据块。 这会使 Hadoop 有效地将磁盘 I/O 分散到所有磁盘。
提示
警告
不要在 dfs.data.dir
属性值的目录路径之间留空,否则它不会按预期工作。
您需要在整个群集中同步更改并重新启动 HDFS 才能应用这些更改。
还有更多...
如果您运行 MapReduce,因为 MapReduce 将其临时文件存储在 TaskTracker 的本地文件系统上,您可能还希望设置 MapReduce 以扩展其磁盘 I/O:
-
在每个 TaskTracker 节点上,在每个磁盘上为 MapReduce 创建目录以存储其中间数据文件:
hadoop$ mkdir -p /mnt/d0/mapred/local hadoop$ mkdir -p /mnt/d1/mapred/local ... hadoop$ mkdir -p /mnt/dn/mapred/local
-
将以下内容添加到 MapReduce 的配置文件(mapred-site.xml):
hadoop@master1$ vi $HADOOP_HOME/conf/mapred-site.xml <property> <name>mapred.local.dir</name> <value>/mnt/d0/mapred/local,/mnt/d1/mapred/local,...,/mnt/dn/mapred/local </value> </property>
-
在群集中同步修改后的
mapred-site.xml
文件,然后重新启动 MapReduce。
MapReduce 在执行过程中会在 TaskTracker 的本地磁盘上生成大量临时文件。 与 HDFS 一样,在不同磁盘上设置多个目录有助于显著分散 MapReduce 磁盘 I/O。
使用网络拓扑脚本实现 Hadoop 机架感知
Hadoop 有“机架感知”的概念。 管理员可以定义群集中每个 DataNode 的机架。 使 Hadoop 机架感知极其重要,因为:
- 机架感知可防止数据丢失
- 机架感知可提高网络性能
在本食谱中,我们将介绍如何使 Hadoop 机架感知,以及为什么它很重要。
做好准备
您需要知道每个从节点所属的机架。 以启动 Hadoop 的用户身份登录到主节点。
怎么做……
以下步骤介绍如何使 Hadoop 机架感知:
-
Create a
topology.sh
script and store it under the Hadoop configuration directory. Change the path fortopology.data
, in line 3, to fit your environment:hadoop@master1$ vi $HADOOP_HOME/conf/topology.sh while [ $# -gt 0 ] ; do nodeArg=$1 exec< /usr/local/hadoop/current/conf/topology.data result="" while read line ; do ar=( $line ) if [ "${ar[0]}" = "$nodeArg" ] ; then result="${ar[1]}" fi done shift if [ -z "$result" ] ; then echo -n "/default/rack " else echo -n "$result " fi done
别忘了设置脚本文件的 EXECUTE 权限:
hadoop@master1$ chmod +x $HADOOP_HOME/conf/topology.sh
-
创建一个
topology.data
文件,如以下代码片段所示;更改 IP 地址和机架以适合您的环境:hadoop@master1$ vi $HADOOP_HOME/conf/topology.data 10.161.30.108 /dc1/rack1 10.166.221.198 /dc1/rack2 10.160.19.149 /dc1/rack3
-
将以下内容添加到 Hadoop 核心配置文件(
core-site.xml
):hadoop@master1$ vi $HADOOP_HOME/conf/core-site.xml <property> <name>topology.script.file.name</name> <value>/usr/local/hadoop/current/conf/topology.sh</value> </property>
-
跨群集中同步修改后的文件,然后重新启动 HDFS 和 MapReduce。
-
确保 HDFS 现在是机架感知的。 如果一切正常,您应该能够在 NameNode 日志文件中找到类似以下代码段的内容:
2012-03-10 13:43:17,284 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack3/10.160.19.149:50010 2012-03-10 13:43:17,297 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack1/10.161.30.108:50010 2012-03-10 13:43:17,429 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack2/10.166.221.198:50010
-
确保 MapReduce 现在是机架感知的。 如果一切正常,您应该能够在 JobTracker 日志文件中找到类似以下代码段的内容:
2012-03-10 13:50:38,341 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack3/ip-10-160-19-149.us-west-1.compute.internal 2012-03-10 13:50:38,485 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack1/ip-10-161-30-108.us-west-1.compute.internal 2012-03-10 13:50:38,569 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack2/ip-10-166-221-198.us-west-1.compute.internal
它是如何工作的.
下图显示了 Hadoop 机架感知的概念:
HDFS 文件的每个数据块将被复制到多个 DataNode,以防止因一台机器故障而丢失所有数据副本。 但是,如果数据的所有副本恰好在同一机架中的 DataNode 上复制,并且该机架出现故障,则所有数据副本都将丢失。 因此,为了避免这种情况,NameNode 需要知道网络拓扑,以便使用该信息进行智能数据复制。
如上图所示,在默认复制系数为 3 的情况下,两个数据副本将放置在同一机架中的计算机上,另一个数据副本将放置在不同机架中的计算机上。 这确保了单个机架故障不会导致丢失所有数据副本。
通常,与不同机架中的两台机器相比,同一机架中的两台机器之间具有更高的带宽和更低的延迟。 有了网络拓扑信息,Hadoop 能够通过从适当的 DataNode 读取数据来最大化网络性能。 如果本地机器上有数据可用,Hadoop 将从其中读取数据。 如果不是,Hadoop 将尝试从同一机架中的机器读取数据,如果两者都不可用,则将从不同机架中的机器读取数据。
在步骤 1 中,我们创建一个 topology.sh
脚本。 该脚本将 DNS 名称作为参数,并返回网络拓扑(Rack)名称作为输出。 DNS 名称到网络拓扑的映射由步骤 2 中创建的 topology.data
文件提供。如果在 topology.data
文件中未找到条目,脚本将返回 /default/rack
作为默认机架名称。
备注
请注意,我们在 topology.data
文件中使用的是 IP 地址,而不是主机名。 有一个已知缺陷,即 Hadoop 无法正确处理以字母“a”到“f”开头的主机名。 有关更多详细信息,请查看 HADOOP-6682:https://issues.apache.org/jira/browse/HADOOP-6682。
在步骤 3 中,我们设置 core-site.xml
中的 topology.script.file.name
属性,告诉 Hadoop 调用 topology.sh
将 DNS 名称解析为网络拓扑名称。
重新启动 Hadoop 后,如步骤 5 和 6 中的日志所示,HDFS 和 MapReduce 会将正确的机架名称作为前缀添加到从节点的 DNS 名称中。 这表明 HDFS 和 MapReduce 机架感知与上述设置配合良好。
带记号和记号的安装盘
如果您纯粹为 Hadoop 挂载磁盘,并且使用 ext3 或 ext4,或者 XFS 文件系统,我们建议您使用 noatime
和 nodiratime
属性挂载磁盘。
如果将磁盘挂载为 noatime
,则在文件系统上读取文件时不会更新访问时间戳。 在属性为 nodiratime
的情况下,挂载磁盘不会更新文件系统上的目录信息节点访问时间。 由于不再有用于更新访问时间戳的磁盘 I/O,这加快了文件系统的读取速度。
在本指南中,我们将介绍为什么推荐 Hadoop 使用 noatime
和 nodiratime
选项,以及如何使用 noatime
和 nodiratime
挂载磁盘。
做好准备
您需要在从节点上拥有 root 权限。 我们假设您有两个仅用于 Hadoop 的磁盘-/dev/xvdc 和 /dev/xvdd
。 这两个磁盘分别安装在 /mnt/is1
和 /mnt/is2
。 此外,我们还假设您使用的是 ext3 文件系统。
怎么做……
要使用 noatime
和 nodiratime
挂载磁盘,请在集群中的每个从节点上执行以下指令:
-
将以下内容添加到
/etc/fstab
文件:$ sudo vi /etc/fstab /dev/xvdc /mnt/is1 ext3 defaults,noatime,nodiratime 0 0 /dev/xvdd /mnt/is2 ext3 defaults,noatime,nodiratime 0 0
-
卸载磁盘并再次装载它们以应用更改:
$ sudo umount /dev/xvdc $ sudo umount /dev/xvdd $ sudo mount /dev/xvdc $ sudo mount /dev/xvdd
-
检查是否已应用装载选项:
$ mount /dev/xvdc on /mnt/is1 type ext3 (rw,noatime,nodiratime) /dev/xvdd on /mnt/is2 type ext3 (rw,noatime,nodiratime)
它是如何工作的.
由于 Hadoop(HDFS)使用 NameNode 管理其文件系统的元数据(Inode),因此 Hadoop 保存的任何访问时间信息都独立于单个块的 atime
属性。 因此,DataNode 的本地文件系统中的访问时间戳在这里没有任何意义。 这就是为什么我们建议您使用 noatime
和 nodiratime
挂载磁盘,如果这些磁盘仅用于 Hadoop。 使用 noatime
和 nodiratime
挂载磁盘可在每次访问本地文件时节省写入 I/O。
这些选项在 /etc/fstab
文件中设置。 要应用更改,请不要忘记再次卸载并挂载磁盘。
启用这些选项后,HDFS 读取的性能有望提高。 由于 HBase 将数据存储在 HDFS 上,因此 HBase 的读取性能也有望提高。
还有更多...
另一种优化方法是降低 ext3 或 ext4 文件系统保留块的百分比。 默认情况下,某些文件系统块保留供特权进程使用。 这是为了避免用户进程为了继续工作而填满系统守护进程所需的磁盘空间的情况。 这对于托管操作系统的磁盘非常重要,但对于仅由 Hadoop 使用的磁盘用处较小。
通常,这些仅用于 Hadoop 的磁盘具有非常大的存储空间。 降低保留块的百分比可以向 HDFS 群集添加相当多的存储容量。 通常,保留块的默认百分比为 5%。 可以降到 1%。
提示
警告:
不要减少托管操作系统的磁盘上的保留块。
为此,请在集群中每个从节点的每个磁盘上运行以下命令:
$ sudo tune2fs -m 1 /dev/xvdc
tune2fs 1.41.12 (17-May-2010)
Setting reserved blocks percentage to 1% (1100915 blocks)
将 vm.swappness 设置为 0 以避免交换
Linux 将一段时间内未访问的内存页移动到交换空间,即使有足够的空闲内存可用。 这就是所谓的换出。 另一方面,将交换出的数据从交换空间读出到内存称为换入。 交换在许多情况下是必要的,但由于Java 虚拟机(JVM)在交换下表现不佳,如果交换,HBase 可能会遇到麻烦。 ZooKeeper 会话到期是交换可能导致的典型问题。
在本食谱中,我们将介绍如何调优 Linux vm.swappiness
参数以避免交换。
做好准备
确保您在群集中的节点上具有 root 权限。
怎么做……。 要调优 Linux 参数以避免交换,请在集群中的每个节点上调用以下命令:
-
执行以下命令将
vm.swappiness
设置为0:
root# sysctl -w vm.swappiness=0 vm.swappiness = 0
- 此更改将一直持续到服务器下一次重新启动。
-
将以下内容添加到
/etc/sysctl.conf
文件中,以便在系统启动时启用该设置:root# echo "vm.swappiness = 0" >> /etc/sysctl.conf
它是如何工作的.
vm.swappiness
参数可用于定义内存页面交换到磁盘的积极程度。 它接受 0
到 100—a
之间的任何值。较低的值意味着内核不太可能交换应用,而较高的值将使内核更频繁地换出应用。 默认值为 60
。
我们在步骤 1 中将 vm.swappiness
设置为 0
,这将导致内核尽可能长时间地避免将进程换出物理内存。 这对 HBase 有好处,因为 HBase 进程消耗大量内存。 较高的 vm.swappiness
值将使 HBase 交换很多,并遇到非常慢的垃圾收集。 随着 ZooKeeper 会话超时,这可能会导致 RegionServer 进程被终止。 我们建议您将其设置为 0
或任何其他较小的数字(例如, 10)
),并观察交换状态。
请注意,由 sysctl
命令设置的值仅在服务器下一次重新启动之前有效。 您需要在 /etc/sysctl.conf
文件中设置 vm.swappiness
,以便在系统重新启动时启用该设置。
另请参阅
- 更改内核设置配方,第 1 章,设置 HBase 群集
Java GC 和 HBase 堆设置
由于 HBase 在 JVM 中运行,JVM垃圾收集(GC)设置对于 HBase 平稳、高性能运行非常重要。 除了配置 HBase 堆设置的一般指导原则之外,让 HBase 进程输出其 GC 日志,然后根据 GC 日志的输出调优 JVM 设置也很重要。
在本食谱中,我们将描述最重要的 HBase JVM 堆设置,以及如何启用和理解 GC 日志记录。 我们还将介绍一些针对 HBase 调优 Java GC 设置的一般指导原则。
做好准备
登录您的 HBase 区域服务器。
怎么做……
以下是推荐的 Java GC 和 HBase 堆设置:
-
通过编辑
hbase-env.sh
文件为 HBase 提供足够的堆大小。 例如,以下代码片段为 HBase 配置 8000 MB 的堆大小:$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_HEAPSIZE=8000
-
使用以下命令启用 GC 日志记录:
$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_OPTS="$HBASE_OPTS -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/local/hbase/logs/gc-hbase.log"
-
添加以下代码以早于默认值启动并发标记扫描 GC(CMS):
$ vi $HBASE_HOME/conf/hbase-env.sh export HBASE_OPTS= "$HBASE_OPTS -XX:CMSInitiatingOccupancyFraction=60"
-
跨群集中同步更改并重新启动 HBase。
-
Check that the GC logs were output to the specified log file (/usr/local/hbase/logs/gc-hbase.log).
GC 日志如下所示:
它是如何工作的.
在步骤 1 中,我们配置 HBase 堆内存大小。 默认情况下,HBase 使用 1 GB 的堆大小,这对于现代机器来说太低了。 4 GB 以上的堆大小对 HBase 是有好处的,而我们的建议是 8 GB 或更大,但在 16 GB 以下。
在步骤 2 中,我们启用 JVM 日志记录。 使用该设置,您将获得区域服务器的 JVM 日志,类似于我们在步骤 5 中显示的内容。理解日志输出需要有关 JVM 内存分配和垃圾收集的基本知识。 以下是 JVM 分代垃圾收集系统的示意图:
有三个堆世代:Perm(或永久)世代、老世代(或永久)世代和年轻世代。 年轻一代部分由三个独立的空间组成:伊甸园空间和两个幸存者空间S0和S1。
通常,对象分配在年轻一代的Eden空间中。 如果分配失败(Eden已满),所有 Java 线程都会暂停,并调用年轻一代 GC(Minor GC)。 年轻一代(Eden和S0空间)中的所有幸存对象都被复制到S1空间。 如果s1空间已满,则会将对象复制(升级)到旧版本。 升级失败时收集老一代(主要/完整 GC)。 永久世代和老世代通常聚集在一起。 永久生成用于保存对象的类和方法定义。
回到步骤 5 中的示例,上述选项的次要 GC 输出将以以下形式生成:
<timestamp>: [GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs] [Times: <user time> <system time>, <real time>]
在此输出中:
<timestamp>
是发生 GC 的时间,相对于应用启动。<collector>
是次要集合中使用的收集器的内部名称。<starting occupancy1>
是收藏前年轻一代的占有率。<ending occupancy1>
是收集后年轻一代的占有率。<pause time1>
是次要收集的暂停时间(秒)。<starting occupancy3>
是集合之前整个堆的占用率。<ending occupancy3>
是集合之后整个堆的占用率。<pause time3>
是整个垃圾收集的暂停时间。 这将包括一次大型收藏的时间。[Time:]
说明 GC 收集所花费的时间、用户时间、系统时间和实时。
步骤 5 中输出的第一行指示一个较小的 GC,这会使 JVM 暂停 0.0764200 秒。 它将年轻一代的空间从 14.8MB 减少到 1.6MB。
接下来,我们将看到 CMS GC 日志。 HBase 使用 CMS GC 作为老一代的默认垃圾收集器。
CMS GC 执行以下步骤:
- 首标
- 并发阅卷
- 评论 / 话语 / 注意 / 言辞
- 并发扫描
CMS 仅在初始标记和备注阶段暂停应用的线程。 在并发标记和清理阶段,CMS 线程与应用的线程一起运行。
示例中的第二行表示 CMS 初始标记花费了 0.0100050 秒,而并发标记花费了 6.496 秒。 请注意,这是一个并发标记;Java 没有暂停。
在以 1441.435: [GC[YG occupancy:
开始的行处有一个停顿...]。 在前面 GC 日志的屏幕截图中。 此处的停顿时间是 0.0413960 秒以重新标记堆。 在那之后,你可以看到清扫开始了。 Cms 扫描花了 3.446 秒,但是堆大小在这里没有太大变化(它一直占用大约 150MB)。
这里的调谐点是将所有这些停顿保持在较低的水平。 为了保持较低的暂停时间,您可能需要通过-XX:NewSize 和 -XX:MaxNewSize
JVM 标志调整年轻一代空间的大小,以便将它们设置为相对较小的值(例如,高达几百 MB)。 如果服务器的 CPU 能力更强,我们建议您通过设置 -XX:+UseParNewGC
选项来使用并行新收集器。 您可能还希望通过 -XX:ParallelGCThreads
JVM 标志调优年轻一代的并行 GC 线程数。
我们建议将上述设置添加到 HBASE_REGIONSERVER_OPTS
变量,而不是 hbase-env.sh
文件中的 HBASE_OPTS
变量。 HBASE_REGIONSERVER_OPTS
变量只影响区域服务器进程,这很好,因为 HBase 主服务器既不处理繁重的任务,也不参与数据进程。
对于老一辈人来说,并发收集(CMS)一般不会加速,但可以更早开始。 当旧一代中分配的空间百分比超过阈值时,CMS 开始运行。 此阈值由收集器自动计算。 对于某些情况,特别是在加载期间,如果 CMS 启动太晚,HBase 可能会运行完整的垃圾回收。 为了避免这种情况,我们建议设置 -XX:CMSInitiatingOccupancyFraction
JVM 标志,以明确指定 CMS 的启动百分比,就像我们在步骤 3 中所做的那样。从 60%或 70%开始是一种良好的做法。 当对老一代使用 CMS 时,默认的年轻一代 GC 将被设置为并行新收集器。
还有更多...
如果您使用的是 0.92 之前的 HBase 版本,请考虑启用 MemStore-Local 分配缓冲区,以防止在繁重的写入负载下出现老式堆碎片:
$ vi $HBASE_HOME/conf/hbase-site.xml
<property>
<name>hbase.hregion.memstore.mslab.enabled</name>
<value>true</value>
</property>
此功能在 HBase 0.92 中默认启用。
另请参阅
- 设置 Ganglia 以监视 HBase 群集配方,请参见第 5 章,监视和诊断
使用压缩
HBase 最重要的特性之一是使用数据压缩。 这很重要,因为:
- 压缩减少了写入 HDFS/从 HDFS 读取的字节数
- 节省磁盘使用量
- 提高从远程服务器获取数据时的网络带宽效率
HBase 支持 GZip 和 LZO 编解码器。 我们建议使用 LZO 压缩算法,因为它数据解压速度快,CPU 使用率低。 由于系统首选较好的压缩比,您应该考虑 GZip。
不幸的是,由于许可证问题,HBase 不能与 LZO 一起发布。 HBase 是 Apache 许可的,而 LZO 是 GPL 许可的。 因此,我们需要自己安装 LZO。 我们将使用 hadoop-lzo 库,它为 Hadoop 带来了可拆分的 LZO 压缩。
在本指南中,我们将介绍如何安装 LZO 以及如何配置 HBase 以使用 LZO 压缩。
做好准备
确保要在其上构建 Hadoop-lzo 的计算机上安装了 Java。
从源代码构建 Hadoop-lzo 需要 Apache Ant。 通过运行以下命令安装 Ant:
$ sudo apt-get -y install ant
群集中的所有节点都需要安装本机 LZO 库。 您可以使用以下命令进行安装:
$ sudo apt-get -y install liblzo2-dev
怎么做……
我们将使用 Hadoop-lzo 库将 LZO 压缩支持添加到 HBase:
-
从https://github.com/toddlipcon/hadoop-lzo获取最新的 hadoop-lzo 源代码。
-
从源代码构建本机和 Java Hadoop-lzo 库。 根据您的操作系统,您应该选择构建 32 位或 64 位二进制文件。 例如,要构建 32 位二进制文件,请运行以下命令:
$ export JAVA_HOME="/usr/local/jdk1.6" $ export CFLAGS="-m32" $ export CXXFLAGS="-m32" $ cd hadoop-lzo $ ant compile-native $ ant jar
- 这些命令将创建 hadoop-lzo/build/ative 目录和 hadoop-lzo/build/hadoop-lzo-x.y.z.jar 文件。 要构建 64 位二进制文件,只需将 CFLAGS 和 CXXFLAGS 的值更改为-m64 即可。
-
将构建的库复制到主节点上的
$HBASE_HOME/lib
和$HBASE_HOME/lib/native
目录:hadoop@master1$ cp hadoop-lzo/build/hadoop-lzo-x.y.z.jar $HBASE_HOME/lib hadoop@master1$ mkdir $HBASE_HOME/lib/native/Linux-i386-32 hadoop@master1$ cp hadoop-lzo/build/native/Linux-i386-32/lib/* $HBASE_HOME/lib/native/Linux-i386-32/
- 对于 64 位操作系统,将 linux-i386-32(在上一步中)更改为 linux-amd64-64。
-
将
hbase.regionserver.codecs
的配置添加到您的hbase-site.xml
文件:hadoop@master1$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.regionserver.codecs</name> <value>lzo,gz</value> </property>
-
在群集中同步
$HBASE_HOME/conf
和$HBASE_HOME/lib
目录。 -
HBase 附带了一个测试压缩设置是否正确的工具。 使用此工具测试群集的每个节点上的 LZO 设置。 如果一切配置正确,您将得到
SUCCESS
输出:hadoop@client1$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.CompressionTest /tmp/lzotest lzo 12/03/11 11:01:08 INFO hfile.CacheConfig: Allocating LruBlockCache with maximum size 249.6m 12/03/11 11:01:08 INFO lzo.GPLNativeCodeLoader: Loaded native gpl library 12/03/11 11:01:08 INFO lzo.LzoCodec: Successfully loaded & initialized native-lzo library [hadoop-lzo rev Unknown build revision] 12/03/11 11:01:08 INFO compress.CodecPool: Got brand-new compressor 12/03/11 11:01:18 INFO compress.CodecPool: Got brand-new decompressor SUCCESS
-
通过创建带有 LZO 压缩的表来测试配置,并在 HBase Shell 中进行验证:
$ hbase> create 't1', {NAME => 'cf1', COMPRESSION => 'LZO'} $ hbase> describe 't1' DESCRIPTION ENABLED {NAME => 't1', FAMILIES => [{NAME => 'cf1', BLOOMFILTER => 'NONE', true REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'LZO', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN _MEMORY => 'false', BLOCKCACHE => 'true'}]} 1 row(s) in 0.0790 seconds
它是如何工作的.
我们构建了 Hadoop-lzo Java 和本地库,并将它们分别安装在 $HBASE_HOME/lib
和 $HBASE_HOME/lib/native
目录下。 通过添加 LZO 压缩支持,HBase StoreFiles(HFiles)将在写入数据块时使用 LZO 压缩。 HBase 使用本机 LZO 库执行压缩,而本机库由 HBase 通过我们构建的 Hadoop-lzo Java 库加载。
为了避免在启动节点时丢失或安装错误的编解码器,我们将 LZO 添加到 hbase-site.xml
文件的 hbase.regionserver.codecs
设置中。 如果 LZO 安装不正确,此设置将导致区域服务器启动失败。 如果您看到诸如“无法加载本机 GPL 库”之类的日志,则说明 LZO 安装有问题。 为了修复它,请确保安装了本机 LZO 库并正确配置了路径。
压缩算法是以每个列族为基础指定的。 如步骤 7 所示,我们创建了一个表 t1
,其中包含一个使用 LZO 压缩的列族 cf1
。
虽然 LZO 增加了读取时间的损失,因为数据块在读取时可能会被解压缩,但作为实时压缩库,LZO 的速度已经足够快了。 我们建议使用 LZO 作为生产 HBase 中的默认压缩算法。
还有更多...
另一个压缩选项是使用最近发布的 Snappy 压缩库和 Hadoop Snappy 集成。 由于设置与我们之前所做的基本相同,因此我们将跳过细节。 查看以下 URL,了解如何将快速压缩添加到 HBase:
http://hbase.apache.org/book.html#snappy.compression
管理压缩
HBase 表具有以下物理存储结构:
它由多个区域组成。 虽然一个区域可能有多个商店,但每个商店都有一个柱族。 编辑首先写入宿主区域存储区的内存空间,称为 MemStore。 当 MemStore 的大小达到阈值时,它会刷新到 HDFS 上的 StoreFiles。
随着数据量的增加,HDFS 上可能会有很多 StoreFiles,这不利于其性能。 因此,HBase 会自动选取几个较小的 StoreFiles,并将它们重写为较大的 StoreFiles。 这一过程被称为次要压实。 对于某些情况,或者当由配置的时间间隔触发时(默认情况下为每天一次),主要压缩会自动运行。 重大压缩将丢弃已删除或过期的单元格,并将 Store 中的所有 StoreFiles 重写为单个 StoreFile;这通常会提高性能。
但是,由于主要压缩会重写存储区的所有数据,因此在此过程中可能会发生大量磁盘 I/O 和网络流量。 这在重载系统上是不可接受的。 您可能希望在较低的系统加载时间运行它。
在本食谱中,我们将介绍如何关闭此自动主要压缩功能,并手动运行它。
做好准备
以启动群集的用户身份登录到您的 HBase 主服务器。
怎么做……
以下步骤介绍如何禁用自动主要压缩:
-
将以下内容添加到
hbase-site.xml
文件:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.hregion.majorcompaction</name> <value>0</value> </property>
-
跨群集中同步更改并重新启动 HBase。
-
使用前述设置,将禁用自动主要压缩;您现在需要显式运行它。
-
要通过 HBase Shell 在特定区域手动运行主要压缩,请运行以下命令:
$ echo "major_compact 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.'" | $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 major_compact 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.' 0 row(s) in 1.7070 seconds
它是如何工作的.
hbase.hregion.majorcompaction
属性指定一个区域中所有 StoreFiles 的主要压缩之间的时间间隔(以毫秒为单位)。 默认值为 86400000
,表示一天一次。 我们在步骤 1 中将其设置为 0
,以禁用自动主要压缩。 这将防止在高负载期间运行主要压缩,例如,当 MapReduce 作业在 HBase 集群上运行时。
另一方面,为了提高性能,需要进行较大的压实。 在步骤 4 中,我们展示了如何通过 HBase Shell 在特定区域手动触发重大压缩的示例。 在本例中,我们向 major_compact
命令传递了一个区域名称,以便仅在单个区域上调用主要压缩。 通过将表名传递给命令,还可以对表的所有区域运行主要压缩。 major_compact
命令将指定的表或区域排队以进行主要压缩;这将由托管它们的区域服务器在后台执行。
正如我们前面提到的,您可能只想在较低的加载时间内手动执行主要压缩。 这可以通过从 cron 作业调用 major_compact
命令轻松完成。
还有更多...
调用主要压缩的另一种方法是使用 org.apache.hadoop.hbase.client.HBaseAdmin
类提供的 majorCompact
API。 在 Java 中很容易调用此 API,因此您可以从 Java 管理复杂的主要压缩调度。
管理区域拆分
通常,HBase 表从单个区域开始。 但是,随着数据的不断增长和区域达到其配置的最大大小,它会自动分为两部分,以便它们可以处理更多数据。 下图显示了 HBase 区域拆分:
这是 HBase 区域拆分的默认行为。 这种机制在许多情况下都工作得很好,但也有遇到问题的情况,例如分裂/压实风暴问题。
由于数据分布和增长大致一致,最终表中的所有区域都需要同时拆分。 拆分后,将立即对子区域运行压缩,以将其数据重写到单独的文件中。 这会导致大量的磁盘 I/O 和网络流量。
为了避免这种情况,您可以关闭自动拆分并手动调用它。 由于您可以控制何时调用拆分,因此有助于分散 I/O 负载。 另一个优点是,手动拆分可以让您更好地控制区域,从而帮助您跟踪和修复与区域相关的问题。
在本菜谱中,我们将介绍如何关闭自动区域拆分并手动调用它。
做好准备
以启动群集的用户身份登录到您的 HBase 主服务器。
怎么做……
要关闭自动区域拆分并手动调用它,请执行以下步骤:
-
将以下内容添加到
hbase-site.xml
文件:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.hregion.max.filesize</name> <value>107374182400</value> </property>
-
跨群集中同步更改并重新启动 HBase。
-
使用上述设置,在区域大小达到配置的 100 GB 阈值之前,不会发生区域拆分。 您需要在选定的区域显式触发它。
-
要运行通过 HBase Shell 拆分的区域,请使用以下命令:
$ echo "split 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.'" | $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 split 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.' 0 row(s) in 1.6810 seconds
它是如何工作的.
hbase.hregion.max.filesize
属性以字节指定最大区域大小。 默认情况下,该值为 1 GB(对于 HBase 0.92 之前的版本,为 256MB),这意味着当一个区域超过此大小时,它将被一分为二。 在步骤 1 中,我们将此最大区域大小设置为 100 GB,这是一个非常高的数字。
由于在区域达到 100 GB 上限之前不会发生拆分,因此我们需要显式调用它。 在步骤 4 中,我们使用 split
命令通过 HBase Shell 调用指定区域的拆分。
别忘了分割大区域。 区域是 HBase 中数据分发和平衡的基本单位。 区域应该分割成合适的大小,并在较低的加载时间。
另一方面,太多的分裂是不好的。 区域服务器上的区域太多会降低其性能。
您可能还希望在手动拆分区域后触发主要压缩和平衡。
还有更多...
我们之前设置的设置会导致整个群集的默认最大区域大小为 100 GB。 除了更改整个簇之外,还可以在创建表格时以列族为基础指定 MAX_FILESIZE
属性:
$ hbase> create 't1', {NAME => 'cf1', MAX_FILESIZE => '107374182400'}
与主要压缩一样,您也可以使用 org.apache.hadoop.hbase.client.HBaseAdmin
Java 类提供的 split
API。
另请参阅
- 使用自己的算法预制区域配方,第 9 章,高级配置和调整
九、高级配置和调整
在本章中,我们将介绍:
- 使用 YCSB 对 HBase 群集进行基准测试
- 增加区域服务器处理程序计数
- 使用您自己的算法预先创建区域
- 避免写入密集型群集上的更新阻塞
- 调整 MemStore 的内存大小
- 针对低延迟系统的客户端调整
- 为列族配置块缓存
- 增加读取密集型群集上的数据块缓存大小
- 客户端扫描仪设置
- 调整数据块大小以提高寻道性能
- 启用 Bloom Filter 以提高整体吞吐量
简介
这是关于性能调优的另一章。 在章,基本性能调优中,我们描述了一些调整 Hadoop、操作系统设置、Java 和 HBase 本身以提高 HBase 集群整体性能的方法。 这些都是对许多用例的一般性改进。 在本章中,我们将描述更多的“具体”配方;其中一些是针对写入繁重的集群,而另一些则旨在提高集群的读取性能。
在调优 HBase 集群之前,您需要知道其性能如何。 因此,我们将首先介绍如何使用Yahoo! 云服务基准(YCSB)用于测量(基准)HBase 集群的性能。
在第 2 章中的配方在将数据移动到 HBase之前预先创建区域中,我们介绍了如何使用 HBase 的 RegionSplitter
实用程序创建包含预先创建的区域的表,以提高数据加载速度。 虽然 RegionSplitter
默认情况下使用 MD5 编号边界预先创建区域,但对于行键不能表示为 MD5 编号的情况,我们需要使用其他拆分算法。 我们将描述一种预创建具有您想要指定的任何边界的区域的方法。
基本上有两种负载类型的 HBase 集群,写密集型集群和读密集型集群。 每种类型都有不同的调优选项。 许多调优选项在写性能和读性能之间进行权衡。 我们将有几个配方来描述如何调优 HBase 集群以获得更好的写入性能;同时,我们还将介绍调优读密集型 HBase 集群的配方。 这些方法包括服务器端配置调优、客户端设置和表模式选择。
不存在适用于所有情况的调优。 您需要仔细考虑系统的性能要求,并调整集群以获得读写性能之间的最佳平衡。
我们假设您对 HBase 架构有基本的了解,并且已经对您的集群进行了常规调优。 您可以参考章,基本性能调优了解 HBase 体系结构和基本 HBase 调优。
以 YCSB 为基准的 HBase 集群
测量 HBase 集群的性能或对集群进行基准测试与调整集群本身一样重要。 我们应该测量的 HBase 集群的性能特征至少包括以下内容:
- 群集的总体吞吐量(每秒操作数)
- 群集的平均延迟(每次操作的平均时间)
- 最小延迟
- 最大延迟
- 操作延迟的分布
YCSB 是对 HBase 集群性能进行基准测试的一个很好的工具。 YCSB 支持并行运行可变负载测试,以评估系统的插入、更新、删除和读取性能。 因此,您可以使用 YCSB 对写密集型和读密集型 HBase 集群进行基准测试。 每次测试都可以配置要加载的记录数、要执行的操作、读写比例以及许多其他属性,因此可以很容易地使用 YCSB 来测试集群的不同负载场景。
YCSB 还可用于评估许多其他不同键值存储的性能。 YCSB 的一个常见用途是对多个系统进行基准测试,并比较它们的性能。
在本指南中,我们将介绍如何安装 YCSB,并使用它测试写密集型和读密集型 HBase 集群。
做好准备
启动您的 HBase 群集并登录到您的 HBase 客户端节点。
怎么做……
要使用 YCSB 对您的 HBase 集群进行基准测试,需要遵循以下步骤:
-
在您的 HBase 客户端节点上下载 YCSB 并解压缩:
$ wget https://github.com/downloads/brianfrankcooper/YCSB/ycsb-0.1.4.tar.gz $ tar xfvz ycsb-0.1.4.tar.gz $ cd ycsb-0.1.4
-
Add the HBase configuration file (
hbase-site.xml
) to YCSB HBase binding's classpath:$ rm hbase-binding/conf/hbase-site.xml $ ln -s $HBASE_HOME/conf/hbase-site.xml hbase-binding/conf/hbase-site.xml
仅对于 HBase 0.92,将 HBase JAR 文件添加到 YCSB HBase 绑定的类路径中:
$ cp $HBASE_HOME/hbase-0.92.0.jar hbase-binding/lib
仅对于 HBase 0.92,将 ZooKeeper JAR 文件添加到 YCSB HBase 绑定的类路径中:
$ cp $ZOOKEEPER_HOME/zookeeper-3.4.2.jar hbase-binding/lib
-
在 HBase 中创建测试表:
$ $HBASE_HOME/bin/hbase shell hbase> create 'usertable', {NAME => 'f1', VERSIONS => '1', COMPRESSION => 'LZO'}
-
调用写入繁重基准:
$ bin/ycsb load hbase -P workloads/workloada -p columnfamily=f1 -p recordcount=1000000 -p threadcount=4 -s | tee -a workloada.dat YCSB Client 0.1 Command line: -db com.yahoo.ycsb.db.HBaseClient -P workloads/workloada -p columnfamily=f1 -p recordcount=1000000 -p threadcount=4 -s -load Loading workload... Starting test. 0 sec: 0 operations; 10 sec: 49028 operations; 4902.8 current ops/sec; [INSERT AverageLatency(us)=759.91] 20 sec: 98060 operations; 4899.28 current ops/sec; [INSERT AverageLatency(us)=777.67] ... 160 sec: 641498 operations; 0 current ops/sec; 170 sec: 641498 operations; 0 current ops/sec; 180 sec: 682358 operations; 4086 current ops/sec; [INSERT AverageLatency(us)=2850.51] ... 240 sec: 1000000 operations; 4721.1 current ops/sec; [INSERT AverageLatency(us)=525.48] 240 sec: 1000000 operations; 0 current ops/sec; [OVERALL], RunTime(ms), 240132.0 [OVERALL], Throughput(ops/sec), 4164.376259723818 [INSERT], Operations, 1000000 [INSERT], AverageLatency(us), 935.844141 [INSERT], MinLatency(us), 10 [INSERT], MaxLatency(us), 26530269 [INSERT], 95thPercentileLatency(ms), 0 [INSERT], 99thPercentileLatency(ms), 0 [INSERT], Return=0, 1000000 [INSERT], 0, 999296 [INSERT], 1, 42 ... [INSERT], 999, 0 [INSERT], >1000, 240
-
调用读密集型基准测试:
bin/ycsb run hbase -P workloads/workloadb -p columnfamily=f1 -p recordcount=1000000 -p operationcount=100000 -p threadcount=4 -s | tee -a workloadb.dat YCSB Client 0.1 Command line: -db com.yahoo.ycsb.db.HBaseClient -P workloads/workloadb -p columnfamily=f1 -p recordcount=1000000 -p threadcount=4 -s -t Loading workload... Starting test. 0 sec: 0 operations; 10 sec: 11651 operations; 1165.1 current ops/sec; [UPDATE AverageLatency(us)=95.15] [READ AverageLatency(us)=3576.62] 20 sec: 26265 operations; 1461.25 current ops/sec; [UPDATE AverageLatency(us)=43.71] [READ AverageLatency(us)=2877.47] ... 60 sec: 100000 operations; 544.22 current ops/sec; [UPDATE AverageLatency(us)=25.15] [READ AverageLatency(us)=3139.45] [OVERALL], RunTime(ms), 60740.0 [OVERALL], Throughput(ops/sec), 1646.3615409944023 [UPDATE], Operations, 5082 [UPDATE], AverageLatency(us), 45.35615899252263 [UPDATE], MinLatency(us), 12 [UPDATE], MaxLatency(us), 6155 [UPDATE], 95thPercentileLatency(ms), 0 [UPDATE], 99thPercentileLatency(ms), 0 [UPDATE], Return=0, 5082 [UPDATE], 0, 5080 [UPDATE], 1, 1 [UPDATE], 2, 0 [UPDATE], 3, 0 ... [UPDATE], >1000, 0 [READ], Operations, 94918 [READ], AverageLatency(us), 2529.312764702164 [READ], MinLatency(us), 369 [READ], MaxLatency(us), 484754 [READ], 95thPercentileLatency(ms), 8 [READ], 99thPercentileLatency(ms), 13 [READ], Return=0, 94918 [READ], 0, 31180 [READ], 1, 21938 [READ], 2, 18331 [READ], 3, 10227 ...
它是如何工作的.
撰写本文时,YCSB 的最新版本是 0.1.4。 此版本具有预编译的 HBase 绑定。 要使用 YCSB HBase 绑定,我们将 HBase 集群的配置文件(hbase-site.xml
)的链接添加到 YCSB 安装下的 hbase-binding/conf
文件夹。 这将告诉 YCSB 我们的 HBase 集群的连接信息。
由于 YCSB 0.1.4 tarball 是使用 HBase 0.90.5 构建的,如果您使用的是集群的 HBase 0.92,则需要将 YCSB 的 HBase 和 ZooKeeper JAR 文件更新为您的集群正在运行的版本,否则输出中可能会出现错误 Not a host:port pair
。 要实现这一点,只需将 HBase 和 ZooKeeper JAR 文件复制到 YCSB 安装下的 hbase-binding/lib
文件夹。
在运行负载测试之前,我们首先需要在 HBase 中创建测试表。 测试表的名称是固定的:它必须是 usertable
。 我们还需要为表创建柱族。 在步骤 3 中,我们使用名为 f1
的列族创建了 usertable
,该列族只支持一个版本和 LZO 压缩。
创建测试表之后,我们可以使用 ycsb
命令运行 YCSB 负载测试。 执行不带参数的 ycsb
命令将显示命令用法。
$ bin/ycsb
Usage: bin/ycsb command database [options]
Commands:
load Execute the load phase
run Execute the transaction phase
shell Interactive mode
Databases:
basic https://github.com/brianfrankcooper/YCSB/tree/master/basic
cassandra-10 https://github.com/brianfrankcooper/YCSB/tree/master/cassandra
cassandra-7 https://github.com/brianfrankcooper/YCSB/tree/master/cassandra
cassandra-8 https://github.com/brianfrankcooper/YCSB/tree/master/cassandra
gemfire https://github.com/brianfrankcooper/YCSB/tree/master/gemfire
hbase https://github.com/brianfrankcooper/YCSB/tree/master/hbase
infinispan https://github.com/brianfrankcooper/YCSB/tree/master/infinispan
jdbc https://github.com/brianfrankcooper/YCSB/tree/master/jdbc
mapkeeper https://github.com/brianfrankcooper/YCSB/tree/master/mapkeeper
mongodb https://github.com/brianfrankcooper/YCSB/tree/master/mongodb
nosqldb https://github.com/brianfrankcooper/YCSB/tree/master/nosqldb
redis https://github.com/brianfrankcooper/YCSB/tree/master/redis
voldemort https://github.com/brianfrankcooper/YCSB/tree/master/voldemort
Options:
-P file Specify workload file
-p key=value Override workload property
-s Print status to stderr
-target n Target ops/sec (default: unthrottled)
-threads n Number of client threads (default: 1)
Workload Files:
There are various predefined workloads under workloads/ directory.
See https://github.com/brianfrankcooper/YCSB/wiki/Core-Properties
for the list of workload properties.
在步骤 4 中,我们调用了一个写入繁重的测试,将 100 万条记录加载到 HBase 中的测试表中。 测试行为由工作负载文件定义。 工作负载指定将加载到表中的数据,以及将对数据执行的操作。 YCSB 的 workloads
目录下有预定义的工作负载。 在这里,我们选择 workloads/workloada
作为工作负载文件。 这是一个更新繁重的工作负载。 我们还通过在命令行中指定 -p recordcount=1000000
来覆盖工作负载的默认记录计数。 通过将 -p threadcount=4
传递给命令,我们调用了四个客户端线程来运行测试。 -s
选项使 YCSB 定期向输出报告状态。
正如您从 HBase web UI 中看到的那样,YCSB 开始将测试数据加载到我们之前在步骤 3 中创建的表中:
负载测试耗时 240132.0 毫秒完成,平均吞吐量为每秒 4,164 个操作。 输出还报告插入操作数量、总延迟和详细延迟。 所有插入都已成功完成(返回=0)。 几乎所有的插入(999296)在不到 1ms 的时间内完成,而 42 个插入在 1-2ms 之间完成。 还有 240 个插入操作耗时超过 1000ms 才能完成。 由于高负载,这些插件可能被区域服务器阻塞了一段时间。
我们还在步骤 5 中调用了读密集型测试。我们对测试数据执行了 100,000 个操作(-p operationcount=100000)
,而 95%的操作是读操作(在 workloads/workloadb)
中定义)。 测试在 60740.0 毫秒内完成;吞吐量为每秒 1646 次操作。 如您所见,HBase 写入比读取快得多。
YCSB 支持其他有用的属性,例如报告时间序列延迟。 有关工作量属性的列表,请参阅以下内容:
https://github.com/brianfrankcooper/YCSB/wiki/Core-Properties
还有更多...
HBase 附带了自己的性能评估(PE)工具,该工具也可用于对 HBase 进行基准测试。 以下是 HBase PE 工具的用法:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation
Usage: java org.apache.hadoop.hbase.PerformanceEvaluation \
[--miniCluster] [--nomapred] [--rows=ROWS] <command> <nclients>
Options:
miniCluster Run the test on an HBaseMiniCluster
nomapred Run multiple clients using threads (rather than use mapreduce)
rows Rows each client runs. Default: One million
flushCommits Used to determine if the test should flush the table. Default: false
writeToWAL Set writeToWAL on puts. Default: True
Command:
filterScan Run scan test using a filter to find a specific row based on its value (make sure to use --rows=20)
randomRead Run random read test
randomSeekScan Run random seek and scan 100 test
randomWrite Run random write test
scan Run scan test (read every row)
scanRange10 Run random seek scan with both start and stop row (max 10 rows)
scanRange100 Run random seek scan with both start and stop row (max 100 rows)
scanRange1000 Run random seek scan with both start and stop row (max 1000 rows)
scanRange10000 Run random seek scan with both start and stop row (max 10000 rows)
sequentialRead Run sequential read test
sequentialWrite Run sequential write test
Args:
nclients Integer. Required. Total number of clients (and HRegionServers)
running: 1 <= value <= 500
Examples:
To run a single evaluation client:
$ bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation sequentialWrite 1
以下是使用 HBase PE 测试顺序写入性能的示例:
$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation sequentialWrite 1
12/03/20 16:34:42 INFO hbase.PerformanceEvaluation: Start class org.apache.hadoop.hbase.PerformanceEvaluation$SequentialWriteTest at offset 0 for 1048576 rows
12/03/20 16:34:50 INFO hbase.PerformanceEvaluation: 0/104857/1048576
12/03/20 16:34:57 INFO hbase.PerformanceEvaluation: 0/209714/1048576
...
12/03/20 16:36:11 INFO hbase.PerformanceEvaluation: Finished class org.apache.hadoop.hbase.PerformanceEvaluation$SequentialWriteTest in 88730ms at offset 0 for 1048576 rows
我们使用一个客户端将大约 100 万行(每行 100 字节)顺序写入 HBase 集群中的测试表。 这项测试花了 88 秒才完成。 不需要指定表和列族名称,因为 HBase PE 工具将创建一个名为 TestTable
的表,其代码中包含一个名为 info
的列族。
备注
请注意,在使用 PE 运行读取测试之前,您需要执行写入测试,因为读取测试使用写入测试插入的数据。
增加区域服务器处理程序计数
区域服务器保持多个正在运行的线程,以应答对用户表的传入请求。 为防止区域服务器内存不足,默认情况下将此数字设置为非常低。 对于许多情况,特别是当您有许多并发客户端时,您将需要增加此数量以处理更多请求。
在本菜谱中,我们将介绍如何调优区域服务器处理程序计数。
做好准备
以启动 HBase 的用户身份登录主节点。
怎么做……
要增加区域服务器处理程序计数,需要执行以下步骤:
-
在主节点上,将以下内容添加到您的
hbase-site.xml
文件:hadoop@master1$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.regionserver.handler.count</name> <value>40</value> </property>
-
在群集中同步更改:
hadoop@master1$ for slave in `cat $HBASE_HOME/conf/regionservers` do rsync -avz $HBASE_HOME/conf/ $slave:$HBASE_HOME/conf/ done
-
重新启动 HBase 以应用更改。
它是如何工作的.
hbase.regionserver.handler.count
属性控制 RPC 侦听器线程的计数。 默认情况下,该属性设置为 10。这是一个相当低的值,可防止区域服务器在某些情况下内存不足。
如果您的区域服务器可用内存不足,则应将其设置为较低的值。 较低的值也适用于处理需要大量内存的请求,例如将较大的值放入 HBase 或使用较大的缓存配置扫描数据。 将 hbase.regionserver.handler.count
设置为高意味着更多的并发客户端,这可能会消耗区域服务器中的过多内存,甚至会耗尽所有内存。
如果您的请求只需要少量内存,但需要较高的每秒事务处理量(TPS),请考虑将其设置为更大的值,以便区域服务器可以处理更多并发请求。
调优此值时,我们建议您启用 RPC 级日志记录,并监视每个 RPC 请求的内存使用情况和 GC 状态。
您需要在整个集群中同步更改并重新启动 HBase 才能应用它。
另请参阅
- 启用 HBase RPC 调试级日志记录配方,参见第 6 章,维护和安全
使用您自己的算法预先创建区域
当我们在 HBase 中创建表时,该表从单个区域开始。 插入到该表中的所有数据都将进入单个区域。 随着数据的不断增长,当区域大小达到阈值时,会发生区域分割。 单个区域被分成两半,以便表可以处理更多数据。
在写入繁重的 HBase 集群中,此方法有几个问题需要解决:
-
The split/compaction storm issue.
随着数据的均匀增长,大部分区域同时被分割,造成了巨大的磁盘 I/O 和网络流量。
-
Load is not well balanced until enough regions have been split.
尤其是在创建表之后,所有请求都会转到部署第一个区域的同一个区域服务器。
拆分/压缩问题已在章,基本性能调整的管理区域拆分配方中进行了讨论。 通过使用手动分割方法。 对于第二个问题,我们在第 2 章中的在将数据移入 HBase配方之前的预创建区域中介绍了如何通过在创建表时创建区域来避免它。 我们描述了如何使用 HBase RegionSplitter
实用程序预先创建区域。
默认情况下, RegionSplitter
实用程序使用 MD5 算法生成 MD5 校验和的区域起始键。 键的范围在“00000000”到“7FFFFFFF”之间。 这在许多情况下都很有效;但在某些情况下,您可能希望使用自己的算法生成密钥,以便在集群中很好地分配负载。
由于 HBase 行键完全由将数据放入 HBase 的应用控制,因此在许多情况下,行键的范围和分布在某种程度上是可以预测的。 因此,可以计算区域分割密钥并使用它们来创建预分割区域。
我们将在本食谱中描述如何实现这一目标。 我们将使用文本文件中指定的区域起始键,创建一个具有预定义区域的表。
做好准备
登录到您的 HBase 客户端节点,并在那里创建一个 split-keys
文件。 将您的区域分割密钥放入文件中,每行一个密钥。 例如,我们假设该文件包含以下密钥:
$ cat split-keys
a0000
affff
b0000
bffff
怎么做……
按照以下说明使用您自己的算法预先创建面域:
-
创建实现
org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm
接口的FileSplitAlgorithm
Java 类:$ vi FileSplitAlgorithm.java import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm; public class FileSplitAlgorithm implements SplitAlgorithm { public static final String SPLIT_KEY_FILE = "split-keys"; }
-
接口的
split()
方法实现如下:$ vi FileSplitAlgorithm.java @Override public byte[][] split(int numberOfSplits) { BufferedReader br = null; try { File keyFile = new File(SPLIT_KEY_FILE); if (!keyFile.exists()) { throw new FileNotFoundException("Splitting key file not found: " + SPLIT_KEY_FILE); } List<byte[]> regions = new ArrayList<byte[]> (); br = new BufferedReader(new FileReader(keyFile)); String line; while ((line = br.readLine()) != null) { if (line.trim().length() > 0) { regions.add(Bytes.toBytes(line)); } } return regions.toArray(new byte[0][]); } catch (IOException e) { throw new RuntimeException("Error reading splitting keys from " + SPLIT_KEY_FILE, e); } finally { if (br != null) { try { br.close(); } catch (IOException e) { // ignore } } } }
-
要编译 Java 类,我们还需要实现接口的其他几个方法。 因为我们实际上并不使用它们,所以只需为每个方法创建一个空实现,如下所示(我们在这里跳过了一些方法):
$ vi FileSplitAlgorithm.java @Override public byte[] firstRow() { return null; } @Override public byte[] lastRow() { return null; }
-
编译 Java 文件:
$ javac -classpath $HBASE_HOME/hbase-0.92.0.jar FileSplitAlgorithm.java
-
将包含拆分密钥的
split-keys
文件复制到编译FileSplitAlgorithm
的目录。 -
运行以下脚本以在创建表时预先创建区域:
$ export HBASE_CLASSPATH=$HBASE_CLASSPATH:./ $ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.RegionSplitter -D split.algorithm=FileSplitAlgorithm -c 2 -f f1 test_table 12/03/25 08:09:42 DEBUG util.RegionSplitter: -D configuration override: split.algorithm=FileSplitAlgorithm 12/03/25 08:09:42 DEBUG util.RegionSplitter: Creating table test_table with 1 column families. Presplitting to 2 regions 12/03/25 08:09:49 DEBUG util.RegionSplitter: Table created! Waiting for regions to show online in META... 12/03/25 08:09:49 DEBUG util.RegionSplitter: Finished creating table with 2 regions
-
Confirm that the table and predefined regions has been created correctly via the HBase web UI:
它是如何工作的.
HBase 附带一个 RegionSplitter
实用程序类,用于:
- 创建带有预分割区域的 HBase 表
- 对现有表中的所有区域执行滚动拆分
- 使用自定义算法分割区域
我们的说明基于这个实用程序类。
首先,我们创建了一个实现 SplitAlgorithm
接口的 FileSplitAlgorithm
Java 类。 SplitAlgorithm
是在 RegionSplitter
类中声明的 Java 接口,用于定义 RegionSplitter
的功能。 我们还在 FileSplitAlgorithm
类中定义了一个 SPLIT_KEY_FILE
常量来引用包含区域起始键的文件。
SplitAlgorithm
接口定义了实现类需要实现的几个方法。 要在创建表时拆分区域,我们只需像在步骤 2 中那样实现 split()
方法。此方法由 RegionSplitter
类调用以拆分整个表。 在我们的实现中,它从我们准备的文件中读取拆分密钥,每行一个密钥。 然后,将它们转换为表示表初始区域的拆分键的 byte[]
数组。
其他 SplitAlgorithm
接口方法对于我们的使用是不必要的,所以我们在步骤 3 中只将这些方法的实现放在空的位置。
在步骤 4 中,我们将 HBase JAR 添加到类路径中,然后运行 javac
命令编译 Java 代码。
要使用该类分割区域,我们将 FileSplitAlgorithm
类添加到 HBASE_CLASSPATH
,然后使用以下参数调用 RegionSplitter
实用程序:
-D split.algorithm=FileSplitAlgorithm:
拆分算法。-c 2:
要将表拆分到的区域数;在我们的实现中不使用。-f f1:
创建名为“F1”的单个柱族。test_table:
要创建的表的名称。
正如您在步骤 7 中看到的, test_table
被分成五个区域,由四个拆分键分隔。 拆分密钥与我们放在 split-keys
文件中的密钥完全相同。
其他表属性都有缺省值;您可能希望通过 HBase Shell 使用 alter
命令更改其中一些属性。
请注意,即使以前拆分区域,您也需要在应用层设计行键,以避免向单个区域写入过多的连续行键。 您需要仔细选择拆分算法以适合您的数据访问模式。
还有更多...
此方法的另一个有用场景是加快将数据从导出的备份 HBase 表导入到 HBase 中。
正如第 4 章,备份和还原 HBase 数据中的配方备份区域起始密钥中提到的,您可以通过一个简单的脚本备份您的区域起始密钥。 通过 FileSplitAlgorithm
类,我们可以使用这些键先前恢复 HBase 表的区域边界,然后通过从导出的数据文件导入来恢复数据。
与从单个区域导入数据相比,由于先将区域恢复并均衡到多个区域服务器,因此数据恢复速度将显著提高。
另请参阅
避免写入密集型群集上的更新阻塞
在写入繁重的 HBase 群集上,您可能会发现写入速度不稳定。 大多数写操作都非常快,但也有一些写得很慢。 对于在线系统,即使平均速度非常快,这种不稳定的写入速度也是不可接受的。
这种情况很可能是由以下两个原因造成的:
- 拆分/压缩会使群集负载非常高
- 更新被区域服务器阻止
正如我们在第 8 章,基本性能调优中所描述的,您可以通过禁用自动拆分/压缩并在低加载时间调用它们来避免拆分/压缩问题。
Grep 您所在地区的服务器日志,如果您发现许多消息说“阻止更新...”,则可能是许多更新被阻止,并且这些更新的响应时间可能较短。
要解决此问题,我们需要调整服务器端和客户端配置以获得稳定的写入速度。 在本食谱中,我们将描述避免更新阻塞的最重要的服务器端调优。
做好准备
由启动 HBase 的用户登录到您的主节点。
怎么做……
要避免更新阻止,请执行以下步骤:
-
增加
hbase-site.xml
文件中的hbase.hregion.memstore.block.multiplier
属性值。$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.hregion.memstore.block.multiplier</name> <value>8</value> </property>
-
增加
hbase-site.xml
文件中的hbase.hstore.blockingStoreFiles
属性值。$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.hstore.blockingStoreFiles</name> <value>20</value> </property>
-
跨群集中同步更改并重新启动 HBase 以应用更改。
它是如何工作的.
以下是 HBase 写操作的流程:
编辑首先写入 RegionServer 的HLog(预写日志),其中编辑在HDFS上持久化。 之后,编辑转到宿主 HRegion,然后转到它的列族 HStore 的内存空间,称为MemStore。 当MemStore的大小达到阈值时,它被刷新到HDFS上的StoreFile。 StoreFiles 在内部使用HFile文件格式保存数据。
HBase 是一种多版本并发控制(MVCC)架构。 要更新/删除任何旧数据,而不是覆盖它,HBase 会向数据添加一个较新的版本。 这使得 HBase 写入非常快,因为所有写入都是顺序操作。
当 HDFS 上有许多小的 StoreFiles 时,HBase 开始压缩,将它们重写为更少但更大的文件。 如果一个区域的大小达到阈值,它将被一分为二。
为防止长时间压缩/拆分和内存不足错误,如果区域的 MemStore 大小达到阈值,HBase 会阻止更新,阈值由以下条件定义:
hbase.hregion.memstore.flush.size
乘以 hbase.hregion.memstore.block.multiplier
hbase.hregion.memstore.flush.size
属性指定 MemStore 将刷新到磁盘的大小。 其默认值为 128MB(在 0.90 版本中为 64MB)。
hbase.hregion.memstore.block.multiplier
属性的默认值为 2
,这意味着如果 MemStore 的大小为 256MB(128x2),则该区域上的更新将被阻止。 在更新流量峰值期间,该值在写入繁重的群集中太小,因此我们需要提高阻塞阈值。
我们通常将 hbase.hregion.memstore.flush.size
属性保留为其默认值,并将 hbase.hregion.memstore.block.multiplier
属性调优为一个大得多的值(如 8),以增加 MemStore 阻塞大小,从而减少更新阻塞计数。
请注意,增加 hbase.hregion.memstore.block.multiplier
更有可能在刷新时触发压缩/拆分,因此请仔细调整。
步骤 2 适用于另一个阻塞场景。 如果任何一个Store的 StoreFiles 数超过 hbase.hstore.blockingStoreFiles
(默认情况下为 7)(每个 MemStore 刷新一个 StoreFile),则此区域的更新将被阻止,直到压缩完成或超过 hbase.hstore.blockingWaitTime
(默认情况下为 90 秒)。 我们将其增加到 20,对于写入繁重的群集来说,这是一个相当大的值。 副作用是将有更多文件需要压缩。
此调优通常会降低发生更新阻塞的可能性。 同时,正如我们刚才提到的,它也有副作用。 我们建议您仔细调整这些设置,并在调整过程中观察写入吞吐量和延迟,以找到最佳配置值。
另请参阅
- 调整 MemStores的内存大小
调整 MemStore 的内存大小
正如我们在配方避免写繁重集群上的更新阻塞中所描述的那样,HBase 写操作首先在托管区域的 MemStore 中应用,然后在 MemStore 大小达到阈值时刷新到 HDFS 以节省内存空间。 MemStore 刷新使用 MemStore 的快照在后台线程上运行。 因此,即使在刷新 MemStore 时,HBase 也会继续处理写入。 这使得 HBase 写得非常快。 如果写入峰值过高,以致 MemStore 刷新无法跟上,则填充 MemStore 的写入速度和 MemStore 使用的内存将持续增长。 如果区域服务器中所有 MemStore 的大小达到可配置阈值,则会阻止更新并强制刷新。
在本文中,我们将介绍如何调优此总 MemStore 内存大小以避免更新阻塞。
做好准备
以启动 HBase 的用户身份登录到您的主节点。
怎么做……
需要执行以下步骤来调整 MemStore 的内存大小:
-
增加
hbase-site.xml
文件中的hbase.regionserver.global.memstore.upperLimit
属性值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.regionserver.global.memstore.upperLimit</name> <value>0.45</value> </property>
-
增加
hbase-site.xml
文件中的hbase.regionserver.global.memstore.lowerLimit
属性值:vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.regionserver.global.memstore.lowerLimit</name> <value>0.4</value> </property>
-
跨群集中同步更改并重新启动 HBase 以应用更改。
它是如何工作的.
在阻止新更新并强制刷新之前, hbase.regionserver.global.memstore.upperLimit
属性控制区域服务器中所有 MemStore 的最大大小。 这是一种防止 HBase 因写入峰值而耗尽内存的配置。 默认设置为 0.4,表示区域服务器堆大小的 40%。
默认值适用于许多情况。 但是,如果您在您的区域服务器日志中检测到许多日志条目显示 Flush of region xxxx due to global heap pressure
,那么您可能需要调优此属性来处理高写入速率。
我们在步骤 2 中调优的 hbase.regionserver.global.memstore.lowerLimit
属性指定当 MemStore 被强制刷新时,它们会一直刷新,直到 MemStore 占用的内存大小减少到这个标记。 默认值为区域服务器堆大小的 35%。
在写入繁重的群集上,增加这两个值有助于降低由于 MemStore 大小限制而阻止更新的可能性。 另一方面,您需要仔细地调优它,以避免出现垃圾回收满或内存不足错误的情况。
还有更多...
通常,读取性能不如写入密集型群集上的写入重要。 因此,我们可以调整群集以优化写入。 优化之一是减少分配给 HBase 块缓存的内存空间,并为 MemStore 腾出空间。 有关如何调优块高速缓存大小的信息,请参见配方在读密集型集群上增加块高速缓存大小。
另请参阅
- 避免写入密集型群集上的更新阻塞
- 为列族配置块缓存
- 增加读密集型群集上的数据块缓存大小
针对低延迟系统的客户端调整
我们介绍了几种避免服务器端阻塞的方法。 这些配方应该可以帮助集群稳定运行并具有高性能。 通过服务器端调优,集群吞吐量和平均延迟将显著提高。
然而,在低延迟和实时系统中,仅在服务器端调优是不够的。 即使只出现轻微的暂停,长时间暂停在低延迟系统中也是不可接受的。
有一些客户端配置我们可以调优,以避免长时间暂停。 在本食谱中,我们将介绍如何调优这些配置以及它们的工作方式。
做好准备
以访问 HBase 的用户身份登录到您的 HBase 客户端节点。
怎么做……
按照以下说明为写入繁重的群集执行客户端调整:
-
减少
hbase-site.xml
文件中的hbase.client.pause
属性值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.client.pause</name> <value>20</value> </property>
-
调整
hbase-site.xml
文件中的hbase.client.retries.number
属性值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.client.retries.number</name> <value>11</value> </property>
-
在
hbase-site.xml
文件中将hbase.ipc.client.tcpnodelay
设置为true
:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.ipc.client.tcpnodelay</name> <value>true</value> </property>
-
减少
hbase-site.xml
文件中的ipc.ping.interval
值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>ipc.ping.interval</name> <value>4000</value> </property>
它是如何工作的.
在步骤 1 和 2 中调整 hbase.client.pause
和 hbase.client.retries.number
属性的目的是让客户端在连接到群集失败时在短时间内快速重试。
hbase.client.pause
属性控制客户端应该在两次重试之间休眠多长时间。 其默认值为 1000 毫秒(1 秒)。 hbase.client.retries.number
属性用于指定最大重试次数。 默认情况下,它的值为 10。
使用以下命令计算每次重试之间的休眠时间:
pause_time = hbase.client.pause * RETRY_BACKOFF[retries]
其中 RETRY_BACKOFF
是重试退避乘数表,其定义如下:
public static int RETRY_BACKOFF[] = { 1, 1, 1, 2, 2, 4, 4, 8, 16, 32 };
重试 10 次以上后,HBase 将始终使用最后一个乘数(32)来计算暂停时间。
由于我们将暂停时间配置为 20ms,最大重试次数为 11,因此两次重试群集之间的暂停时间如下:
{ 20, 20, 20, 40, 40, 80, 80, 160, 320, 640, 640 }
这意味着客户端将在 2060ms 内重试 11 次,然后才会放弃连接到群集。
在步骤 3 中,我们将 hbase.ipc.client.tcpnodelay
设置为 true。 此设置为客户端和服务器之间的套接字传输禁用Nagle 算法。
Nagle 的算法是一种提高网络效率的方法,它通过缓冲大量小的传出消息,并一次性发送它们。 默认情况下启用 Nagle 算法。 低延迟系统应通过将 hbase.ipc.client.tcpnodelay
设置为 true
来禁用Nagle 算法。
在步骤 4 中,我们将 ipc.ping.interval
设置为 4000 毫秒(4 秒),以便在客户端和服务器之间的套接字传输期间不会超时。 默认的 ipc.ping.interval
是 1 分钟,这对于低延迟系统来说有点太长了。
还有更多...
您还可以在客户端代码中使用 org.apache.hadoop.hbase.HBaseConfiguration
类来覆盖在 hbase-site.xml
中设置的前面属性的值。 下面的示例代码与前面步骤 1 和 2 中的设置具有相同的效果:
Configuration conf = HBaseConfiguration.create();
conf.setInt("hbase.client.pause", 20);
conf.setInt("hbase.client.retries.number", 11);
HTable table = new HTable(conf, "tableName");
为列族配置块缓存
HBase 支持块缓存,提高读取性能。 执行扫描时,如果启用了块缓存并且还有剩余空间,则从 HDFS 上的 StoreFiles 读取的数据块将被缓存在区域服务器的 Java 堆空间中,以便下次访问同一块中的数据时,缓存的块可以提供服务。 数据块缓存有助于减少用于检索数据的磁盘 I/O。
块缓存可在表的列族级别进行配置。 不同的列族可以具有不同的高速缓存优先级,甚至可以禁用块高速缓存。 应用利用此缓存机制来适应不同的数据大小和访问模式。
在本食谱中,我们将介绍如何为列族配置块缓存,并介绍如何利用 HBase 块缓存。
做好准备
登录到您的 HBase 客户端节点。
怎么做……
要在列族级别配置数据块缓存,需要执行以下步骤:
-
启动 HBase 外壳:
$ $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 hbase(main):001:0>
-
执行以下命令以创建具有三个列族的表:
hbase> create 'table1', {NAME => 'f1'}, {NAME => 'f2', IN_MEMORY => 'true'}, {NAME => 'f3', BLOCKCACHE => 'false'} 0 row(s) in 1.0690 seconds
-
显示先前创建的表的属性:
hbase> describe 'table1' DESCRIPTION ENABLED {NAME => 'table1', FAMILIES => [{NAME => 'f1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', true COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOC KCACHE => 'true'}, {NAME => 'f2', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION = > 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'true', BLOCKCACHE => 'true'}, {NAME => 'f3', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NONE', MIN_ VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'false'}]} 1 row(s) in 0.0290 seconds
它是如何工作的.
我们创建了一个具有三个列族 f1, f2
和 f3
的表(table1
)。 对于 Family f1
,我们没有指定任何属性,因此它的所有属性都设置为默认值。 如步骤 3 所示,其块高速缓存被启用(BLOCKCACHE => 'true')
,并且存储器中的块高速缓存被关闭(IN_MEMORY => 'false')
。
HBase 块缓存包含三个块优先级级别:单次访问、多次访问和内存访问。 如有必要,将使用内存中标志添加一个块(这意味着 HBase 将尝试更积极地将该块保留在内存中,但不能保证),否则它将成为单一访问优先级。 一旦数据块再次被访问,它将被标记为多路访问。 如下图所示,这三个优先级的缓存空间不同,单次访问和内存访问分别为 25%,多次访问分别为总缓存空间的 50%:
我们上面创建的列族 f2
被标记为已启用内存。 因此,属于该系列的块以内存中的优先级进行缓存。 对于系列 f3
,块高速缓存被禁用,这意味着列系列的数据块将不会被高速缓存。 不建议禁用数据块缓存。
由于数据是以块为单位进行缓存的,因此访问同一块中的数据非常高效。 对于小尺寸的行来说尤其如此。 因此,将同时访问的数据放在同一列族中是表模式设计的良好实践。 例如,当使用 HBase 存储从互联网上抓取的网页时,最好有一个 meta
列族和一个 raw
列族,其中 meta
列族启用内存来保存网页的元数据,而 raw
列族则存储页面的原始内容。
还有更多...
您还可以通过 HBase Shell 使用 alter
命令更改现有列族的块缓存属性:
-
禁用要更改的表:
hbase> disable 'table1' 0 row(s) in 7.0580 seconds
-
使用
alter
命令更改表的块大小:hbase> alter 'table1', {NAME => 'f1', IN_MEMORY => 'true'} Updating all regions with the new schema... 1/1 regions updated. Done. 0 row(s) in 6.0990 seconds
-
使用
describe
命令确认您的更改:hbase> describe 'table1' DESCRIPTION ENABLED {NAME => 'table1', FAMILIES => [{NAME => 'f1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', false COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'true', BLOCK CACHE => 'true'}, {NAME => 'f2'... 1 row(s) in 0.0300 seconds
-
Enable the table again:
hbase> enable 'table1' 0 row(s) in 2.0530 seconds
在这里,我们刚刚为表
table1
中的现有列族f1
启用了内存中。
另请参阅
- 增加读密集型群集上的数据块缓存大小
- 客户端扫描仪设置
增加读密集型群集上的数据块缓存大小
如配方调优 MemStores 的内存大小和为列族配置块缓存中所述,区域服务器为 MemStores 分配大量的 Java 堆空间以提高写入性能。 它还使用大量堆空间缓存 StoreFile 块,以提高读取性能。
写入和读取性能之间存在平衡。 在读取密集型群集上,由于读取性能更重要,您可能希望为块缓存分配更多内存。
在本配方中,我们将介绍如何增加块缓存大小。 我们还将提供有关确定块缓存是否足够的提示。
做好准备
以启动 HBase 的用户身份登录到您的主节点。
怎么做……
要增加数据块缓存大小,需要执行以下步骤:
-
增加
hbase-site.xml
文件中的hfile.block.cache.size
属性值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hfile.block.cache.size</name> <value>0.3</value> </property>
-
跨群集中同步更改并重新启动 HBase 以应用更改。
它是如何工作的.
区域服务器的总块缓存空间由 hfile.block.cache.size
属性配置。 此属性指定要分配给块缓存的最大区域服务器堆的百分比。 默认情况下,它分配最大堆大小的 25%。
在步骤 1 中,我们将块缓存的总空间增加到最大区域服务器堆大小的 30%。 在读取密集型群集上,建议减少 MemStore 的空间,并为块缓存分配更多内存。 MemStore 和块缓存通常消耗大约 60%~70%的最大区域服务器堆大小。 这是一个合理的值。 MemStore 上限和块缓存的合计值不应高于此级别,除非您绝对确定它会很好。
确定应该为块缓存分配多少内存的另一个指标是检查 Ganglia 或 HBase web UI 上的区域服务器指标。 例如,单击 HBase web UI 上的区域服务器链接;您将找到区域服务器的最重要指标:
检查页面上的 MemStore 大小、块缓存大小、命中率等;您会发现这些信息有助于调整集群。 如果块缓存命中率非常低,您可能需要检查您的表模式和数据访问模式;如果列总是同时被访问,则将它们放在一起。 使用 Bloom Filter 是提高块缓存命中率的另一种解决方案。 如果您遇到许多数据块逐出,请考虑增加数据块缓存大小以容纳更多数据块。
另请参阅
- 调整 MemStores的内存大小
- 为列族配置块缓存
- 启用 Bloom Filter 提高整体吞吐量
客户端扫描仪设置
要获得更好的读取性能,除了服务器端调优之外,重要的是客户端应用端的扫描仪设置。 更好的客户端扫描仪设置可使扫描过程更加高效。 相比之下,配置不佳的扫描仪不仅会降低扫描速度,还会对区域服务器造成负面影响。 因此,我们需要仔细配置客户端扫描仪设置。
最重要的扫描仪设置包括扫描缓存、扫描属性选择和扫描块缓存。 在本指南中,我们将介绍如何正确配置这些设置。
做好准备
由访问 HBase 的用户登录到您的 HBase 客户端节点。
怎么做……
要更改客户端扫描仪设置,需要执行以下步骤:
-
要在扫描仪上调用
next()
方法时提取更多行,请增加hbase-site.xml
文件中的hbase.client.scanner.caching
属性值:$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.client.scanner.caching</name> <value>500</value> </property>
-
仅通过指定列族和限定符来获取所需的列。 示例代码如下所示:
Scan scan = new Scan(); // your scan filtering code scan.addColumn(family1, qualifier1); scan.addColumn(family1, qualifier2);
-
要禁用特定扫描的块缓存,请在代码中添加以下内容。 请注意,这不会禁用服务器端的块缓存,但会阻止缓存扫描仪扫描的块。
Scan scan = new Scan(); // your scan filtering code scan.setCacheBlocks(false);
它是如何工作的.
在步骤 1 中,我们将扫描缓存更改为 500,这远远大于默认值 1。这意味着区域服务器将一次向客户端传输 500 行进行处理。 对于某些情况,比如运行 MapReduce 从 HBase 读取数据,将此值设置为更大的值会使扫描过程比缺省值高效得多。 但是,较高的缓存值需要更多内存来缓存客户端和区域服务器的行。
它还存在这样的风险,即客户端可能会在完成处理日期集并调用扫描仪上的 next()
之前超时。 如果客户端进程较快,请将缓存设置得更高。 否则,您需要将其设置得更低。 我们将介绍如何在中设置基于每次扫描的缓存。 。 一节。
步骤 2 显示的想法是,如果只处理列族的一小部分,则只应将所需的数据传输到客户端。 当要处理大量行时(例如,在 MapReduce 期间),这一点尤其重要。 不需要的数据传输开销会影响大型数据集的性能。 因此,我们不应该使用 scan.addFamily()
将列族中的所有列返回给客户端,而应该调用 scan.addColumn()
来只指定我们需要的列。
如果在扫描数据块的扫描仪上禁用了数据块缓存,则不会将数据块添加到数据块缓存中。 正如我们在步骤 3 中所做的那样,禁用特定扫描的块缓存有时非常重要。 例如,使用 MapReduce 完全扫描表时,应禁用扫描的块缓存,因为表中的所有块都将被扫描表中的所有块,这会用一次性访问块填满块缓存空间,并会一次又一次地触发缓存逐出过程。
在 HBase web 用户界面中,您将发现两个数据块缓存命中率指标: blockCacheHitRatio
和 blockCacheHitCachingRatio
。 它们的不同之处在于, blockCacheHitRatio
是块缓存命中计数占总请求计数的百分比,而 blockCacheHitCachingRatio
仅包括打开缓存的请求。 我们可以通过禁用某些扫描的块缓存来增加 blockCacheHitCachingRatio
。
还有更多...
在上一节中,我们将 hbase-site.xml
中的 hbase.client.scanner.caching
属性更改为 500
。 更改后,该节点上的所有客户端会话都将继承此默认缓存行号。 但是,您还可以使用 HBase 客户端扫描 API 指定每个扫描的缓存行。 下面的代码将扫描器设置为在调用 next()
时提取 1000 行:
Scan scan = new Scan();
// your scan filtering code
scan.setCaching(1000);
另请参阅
- 针对低延迟系统的客户端调整
调整块大小以提高寻道性能
HBase 数据以 HFile 格式存储为 StoreFile。 StoreFile 由 HFile 块组成。 HFile 块是 HBase 从其 StoreFiles 读取的最小数据单位。 它也是区域服务器在块缓存中缓存的基本元素。
HFile 块的大小是一个重要的调优参数。 为了获得更好的性能,我们应该根据平均键/值大小和磁盘 I/O 速度选择不同的块大小。 与块缓存和 Bloom filter 一样,HFile 块大小也可以在列族级别进行配置。
在本文中,我们将介绍如何显示平均键/值大小和调优块大小以提高查找性能。
做好准备
登录到您的 HBase 客户端节点。
怎么做……
需要执行以下步骤来调整块大小以提高寻道性能:
-
使用以下命令显示 HFile 中的平均键/值大小。 更改文件路径以适合您的环境。 H 特定列族的文件存储在 HDFS 的
${hbase.rootdir}/table_name/region_name/column_family_name
下。$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile -m -f /hbase/hly_temp/0d1604971684462a2860d43e2715558d/n/1742023046455748097 Block index size as per heapsize: 12240 reader=/hbase/hly_temp/0d1604971684462a2860d43e2715558d/n/1742023046455748097, compression=lzo, inMemory=false, firstKey=USW000128350706/n:v01/1323026325955/Put, lastKey=USW000138830522/n:v24/1323026325955/Put, avgKeyLen=31, avgValueLen=4, entries=288024, length=2379789 fileinfoOffset=2371102, dataIndexOffset=2371361, dataIndexCount=190, metaIndexOffset=0, metaIndexCount=0, totalBytes=12387203, entryCount=288024, version=1 Fileinfo: MAJOR_COMPACTION_KEY = \x00 MAX_SEQ_ID_KEY = 96573 TIMERANGE = 1323026325955....1323026325955 hfile.AVG_KEY_LEN = 31 hfile.AVG_VALUE_LEN = 4 hfile.COMPARATOR = org.apache.hadoop.hbase.KeyValue$KeyComparator hfile.LASTKEY = \x00\x0FUSW000138830522\x01nv24\x00\x00\x014\x0A\x83\xA1\xC3\x04 Could not get bloom data from meta block
-
启动 HBase 外壳:
$ $HBASE_HOME/bin/hbase shell
-
通过 HBase Shell 设置特定列族的块大小:
hbase> create 'hly_temp', {NAME => 'n', BLOCKSIZE => '16384'} 0 row(s) in 1.0530 seconds
-
显示先前创建的表的属性:
hbase> describe 'hly_temp' DESCRIPTION ENABLED {NAME => 'hly_temp', FAMILIES => [{NAME => 'n', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', true COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '16384', IN_MEMORY => 'false', BLOC KCACHE => 'true'}]} 1 row(s) in 0.0330 seconds
它是如何工作的.
首先,我们想知道特定列族的平均键/值大小。 在步骤 1 中,我们调用了 HFile 工具(org.apache.hadoop.hbase.io.hfile.HFile 类)来显示 HFile 的元数据。 如配方HFile Tool,View Textualized HFile Contentin第 3 章中所述,通过 -m
选项,我们可以使用此工具获取 HFile 文件的元数据,包括平均键/值大小。 在步骤 1 中,我们传递了 -f
选项和文件路径来显示单个 HFile 的元数据。 如果您的表只有一个列族,您还可以使用 -r
选项和一个区域名称来显示属于该区域的每个 HFile 的元数据。 有了这些信息,我们就能够获得列族的大致平均键/值大小。
从步骤 1 的输出中可以看到,HFile 的平均键/值大小非常小(35 字节)。 在这种情况下,键/值的平均大小非常小(例如,100 字节);我们应该使用较小的块(例如,16KB),以避免在每个块中存储过多的键/值对,这会增加块内查找的延迟,因为查找操作总是在块内按顺序从第一个键/值对中查找键。
从步骤 2 到步骤 4,我们演示了如何通过 HBase Shell 配置列族的块大小。在步骤 3 中,我们创建了具有单个列族‘n’的 hly_temp
表,并将列族的块大小指定为 16384 字节(16KB)。这比 64KB 的默认块大小小得多。
这里,我们选择较小的块大小以牺牲较大的块索引(更多内存消耗)来实现更快的随机访问。 另一方面,如果平均键/值较大,或者磁盘速度较慢导致瓶颈,则应选择较大的块大小,以便单个磁盘 I/O 可以获取更多数据。
还有更多...
您还可以使用 HBase Shell 中的 alter
命令更改现有列族的块大小。 这将在创建新的 StoreFiles 时应用。
另请参阅
- HFile 工具,查看第 3 章中的文本化 HFile 内容
启用 Bloom Filter 以提高整体吞吐量
HBase 支持 Bloom Filter,提升集群整体吞吐量。 HBase Bloom Filter 是一种节省空间的机制,用于测试 StoreFile 是否包含特定的行或行-列单元格。 下面是 Bloom filter 的详细信息:http://en.wikipedia.org/wiki/Bloom_filter。
在没有 Bloom Filter 的情况下,确定 StoreFile 中是否包含行键的唯一方法是检查 StoreFile 的块索引,它存储 StoreFile 中每个块的起始行键。 我们找到的行键很可能会落在两个块起始键之间;如果是这样,那么 HBase 必须加载块并从块的起始键开始扫描,以确定行键是否确实存在。
这里的问题是,在主要压缩将它们聚合成单个 StoreFiles 之前,将存在许多 StoreFiles。 因此,几个 StoreFiles 可能具有请求的行键的一些单元格。
考虑一下下面的示例;这是一张显示 HBase 如何在 StoreFile 中存储数据、块索引和 Bloom 过滤器的图像:
单个列族有四个 StoreFiles,它们是由 MemStore 刷新创建的。 StoreFiles 在行键中有类似的跨页。 一些行键(例如, row-D)
)被本地化到几个 StoreFiles,而另一些(例如, row-F)
)则分布在多个 StoreFiles 中。 虽然只有 StoreFile1 实际包含 row-D
,但 HBase 还需要从 StoreFiles 2 和 3 加载第一个数据块,以确定该块是否包含 row-D
的单元格,因为 row-D
位于第一个数据块之间。 如果有一种机制告诉 HBase 跳过加载 StoreFiles 2 和 3,那就更好了。
在这种情况下,Bloom Filter 会有所帮助。 Bloom Filter 用于减少这些不必要的磁盘 I/O,从而提高集群的整体吞吐量。
在本食谱中,我们将介绍如何为列系列启用 Bloom Filter,以及如何利用 HBase Bloom Filter 提高总体吞吐量的提示。
做好准备
登录到您的 HBase 客户端节点。
怎么做……
要启用列族的 Bloom Filter,需要执行以下步骤:
-
启动 HBase 外壳:
$ $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012 hbase(main):001:0>
-
执行以下命令以创建具有三个列族的表:
hbase> create 'table2', {NAME => 'f1'}, {NAME => 'f2', BLOOMFILTER => 'ROW'}, {NAME => 'f3', BLOOMFILTER => 'ROWCOL'} 0 row(s) in 1.0690 seconds
-
显示先前创建的表的属性:
hbase> describe 'table2' DESCRIPTION ENABLED {NAME => 'table2', FAMILIES => [{NAME => 'f1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', true COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOC KCACHE => 'true'}, {NAME => 'f2', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'f3', BLOOMFILTER => 'ROWCOL', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NONE', MI N_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}]} 1 row(s) in 0.0360 seconds
它是如何工作的.
我们创建了一个具有三个列族 f1, f2
和 f3
的表(table2
)。 对于 Family f1
,我们没有指定任何属性,因此它的所有属性都设置为默认值。 如步骤 3 所示,其布隆过滤器被禁用(BLOOMFILTER => 'NONE')
。 我们为 f2``(BLOOMFILTER => 'ROW')
指定了行级布隆过滤器,为 f3``(BLOOMFILTER => 'ROWCOL')
指定了行+列级布隆过滤器。
如果启用 Bloom Filter,HBase 会在创建 StoreFile 时添加 Bloom Filter 和数据块。 Bloom filter 用于让 HBase 高效地判断 StoreFile 是否包含特定的行或单元格,而无需实际加载文件和扫描块。 Bloom 过滤器可能是假阳性,这意味着查询返回 a row is contained in a file
,但实际上不是。 但是 Bloom Filter 不允许假阴性,因此当查询返回 a row is not in a file
时,该行肯定不在文件中。
正常情况下,错误率为 0.01(由 io.storefile.bloom.error.rate
设置配置),因此 Bloom Filter 报告 StoreFile 包含行的可能性为 1%,但事实并非如此。 降低错误率需要更多的空间来存储 Bloom filter。
默认情况下,Bloom Filter 处于关闭状态,即 HBase 在创建 StoreFiles 时不会存储 Bloom Filter。 它可以基于列族进行配置,以启用行级或行+列级布隆过滤器。 行级 Bloom 过滤器用于测试 StoreFile 中是否包含行键,而行+列 Bloom 过滤器可以告诉 HBase StoreFile 是否包含特定单元。 行+列级布隆过滤器占用更多磁盘空间,因为单元格条目比行条目大得多。
即使启用了 Bloom Filter,单个 GET 操作也可能无法立即获得性能,因为 HBase 是并行读取数据的,并且延迟受磁盘 I/O 速度的限制。 但是,由于加载的块数量大大减少,因此显著提高了总体吞吐量,尤其是在负载较重的集群中。
另一个优点是使用 Bloom Filter 可以提高块缓存率。 启用 Bloom Filter 后,HBase 可以加载更少的块来获取客户端请求的数据。 由于不加载不必要的块,因此包含客户端实际请求的数据的块有更多机会保留在块缓存中。 这提高了整个群集的读取性能。
缺点是,Bloom Filter 中的每个条目都使用大约一个字节的存储空间。 如果您有较小的单元(例如,包含键/值信息开销的 20 个字节),则 Bloom 过滤器将是文件的 1/20。 另一方面,如果您的平均单元格大小为 1KB,则 Bloom Filter 的大小约为文件大小的 1/1000。 假设 StoreFile 是 1 GB,那么筛选器只需要 1 MB 的存储空间。 因此,我们建议您对小型单元格列族禁用布隆过滤器,并始终为中型或大型单元格系列启用布隆过滤器。
如上所述,HBase 支持行级和行级+列级 Bloom 过滤器。 使用哪一种取决于您的数据访问模式。 返回到我们的示例 HStore,只有几个 StoreFiles 保存 row-D
。 当 row-D
的单元格被批量更新时,就会发生这种情况。 如果一行中的大多数单元格一起更新,则行级筛选器更好。 相反,就像我们示例中的 row-F
一样,如果更新分布在多个 StoreFiles 中,并且大多数 StoreFiles 包含行的一部分,则建议使用行+列筛选器,因为它能够识别哪个 StoreFile 包含您所请求的行的确切部分。但是,您还需要考虑数据读取模式。 很明显,如果您总是请求整个行,行+列筛选器就没有意义了。 例如,要加载整个 row-F
,区域服务器无论如何都需要加载所有四个 StoreFiles。 当您的读取模式是只加载一行的几列时,例如,只加载 StoreFile4 中的 row-F
列,那么行+列筛选器很有用,因为区域服务器将跳过加载其他 StoreFiles。
备注
请注意,HFile 版本 1 是 HBase 0.92 之前版本的默认 HFile 格式,Bloom 过滤器可以容纳的元素数是最大的。 此数字由 io.storefile.bloom.max.keys
属性控制,默认值为 128M 键。 如果 StoreFile 中的单元格太多,可能会超过此数字。 您需要使用行级筛选器来减少 Bloom 筛选器中的键数。
Bloom Filter 是提高集群整体性能的有效方法。 行级 Bloom 筛选器在许多情况下都可以很好地工作;您应该将其作为首选,并且只有在行级筛选器不适合您的使用时才考虑行+列 Bloom 筛选器。
还有更多...
您还可以通过 HBase Shell 使用 alter
命令更改现有表的列族的 Bloom filter 属性。 它将应用于更改后创建的 StoreFiles。