HBase适合场景:单表超千万,上亿,且高并发!

HBase不适合场景:主要需求是数据分析,比如做报表。数据量规模不大,对实时性要求高!

HBase的查询工具有很多,如:Hive、Tez、Impala、Spark SQL、Kylin、Phoenix等。

一、HBase的安装

①、保证安装Hive的Linux服务器的环境变量中有JAVA_HOME
②、基于HADOOP工作,保证安装Hive的Linux服务器的环境变量中有HADOOP_HOME

1、HBase解压与安装(在hadoop102服务器上)

在hadoop102服务器上解压HBase到指定目录:

[whx@hadoop102 software]$ tar -zxvf HBase-1.3.1-bin.tar.gz -C /opt/module

修改/opt/module/hbase-1.3.1目录名称为/opt/module/hbase

[whx@hadoop102 module]$ ll
total 28
drwxrwxr-x.  9 whx whx 4096 Jan 31 14:45 flume
drwxr-xr-x. 11 whx whx 4096 Jan 31 10:43 hadoop-2.7.2
drwxrwxr-x.  7 whx whx 4096 Feb  2 10:11 hbase-1.3.1
drwxrwxr-x.  9 whx whx 4096 Jan 30 19:27 hive
drwxr-xr-x.  8 whx whx 4096 Dec 13  2016 jdk1.8.0_121
drwxr-xr-x.  8 whx whx 4096 Feb  1 16:32 kafka
drwxr-xr-x. 11 whx whx 4096 Jan 29 22:01 zookeeper-3.4.10
[whx@hadoop102 module]$ mv hbase-1.3.1/ hbase
[whx@hadoop102 module]$ ll
total 28
drwxrwxr-x.  9 whx whx 4096 Jan 31 14:45 flume
drwxr-xr-x. 11 whx whx 4096 Jan 31 10:43 hadoop-2.7.2
drwxrwxr-x.  7 whx whx 4096 Feb  2 10:11 hbase
drwxrwxr-x.  9 whx whx 4096 Jan 30 19:27 hive
drwxr-xr-x.  8 whx whx 4096 Dec 13  2016 jdk1.8.0_121
drwxr-xr-x.  8 whx whx 4096 Feb  1 16:32 kafka
drwxr-xr-x. 11 whx whx 4096 Jan 29 22:01 zookeeper-3.4.10
[whx@hadoop102 module]$

2、修改HBase配置文件

2.1 hbase-env.sh

JAVA_HOME配置(可不用改 因为Linux服务器的环境变量文件/etc/profile里面已经把JAVA_HOME设为全局了!)

export JAVA_HOME=/opt/module/jdk1.8.0_144

注释掉Configure PermSize(所用的JDK为1.8以上,不需要此配置)

# Configure PermSize. Only needed in JDK7. You can safely remove it for JDK8+
#export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
#export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"

使用HBase外部的Zookeeper,而不是用HBase内置的Zookeeper。默认是使用HBase内置的Zookeeper(HBASE_MANAGES_ZK=true)

# Tell HBase whether it should manage it's own instance of Zookeeper or not.
export HBASE_MANAGES_ZK=false

2.2 hbase-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
/**
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->
<configuration>
	<!-- 每个regionServer的共享目录,用来持久化Hbase,默认情况下在/tmp/hbase下面 -->  
	<property>     
		<name>hbase.rootdir</name>     
		<value>hdfs://hadoop101:9000/HBase</value>   
	</property>
	<!-- hbase集群模式,false表示hbase的单机,true表示是分布式模式 -->  
	<property>   
		<name>hbase.cluster.distributed</name>
		<value>true</value>
	</property>
	<!-- hbase依赖的外部Zookeeper地址 -->  
	<property>    
		<name>hbase.zookeeper.quorum</name>
	     <value>hadoop101:2181,hadoop102:2181,hadoop103:2181</value>
	</property>
	<!--外部Zookeeper各个Linux服务器节点上保存数据的目录-->
	<property>   
		<name>hbase.zookeeper.property.dataDir</name>
	     <value>/opt/module/zookeeper-3.4.10/datas</value>
	</property>
</configuration>

3、将hbase配置到系统环境变量中

[whx@hadoop102 ~]$ sudo vim /etc/profile
JAVA_HOME=/opt/module/jdk1.8.0_121
HADOOP_HOME=/opt/module/hadoop-2.7.2
HIVE_HOME=/opt/module/hive
FLUME_HOME=/opt/module/flume
HBASE_HOME=/opt/module/hbase
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$FLUME_HOME/bin:$HBASE_HOME/bin

export JAVA_HOME HADOOP_HOME HIVE_HOME FLUME_HOME HBASE_HOME PATH
[whx@hadoop102 ~]$ source /etc/profile

4、分发hbase安装目录、环境变量文件到hadoop101、hadoop103

[whx@hadoop102 module]$ xsync.sh hbase/
[whx@hadoop102 ~]$ xsync.sh /etc/profile

二、HBase的启动

1、HBase启动前先启动Zookeeper、HDFS

启动Zookeeper

[whx@hadoop102 ~]$ xcall.sh /opt/module/zookeeper-3.4.10/bin/zkServer.sh start
要执行的命令是/opt/module/zookeeper-3.4.10/bin/zkServer.sh start
----------------------------hadoop101----------------------------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
----------------------------hadoop102----------------------------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
----------------------------hadoop103----------------------------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[whx@hadoop102 ~]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
3037 QuorumPeerMain
3071 Jps
----------------------------hadoop102----------------------------------
3633 QuorumPeerMain
3677 Jps
----------------------------hadoop103----------------------------------
3072 Jps
3038 QuorumPeerMain
[whx@hadoop102 ~]$

启动HDFS

[whx@hadoop102 ~]$ start-dfs.sh
Starting namenodes on [hadoop101]
hadoop101: starting namenode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-whx-namenode-hadoop101.out
hadoop102: starting datanode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-whx-datanode-hadoop102.out
hadoop101: starting datanode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-whx-datanode-hadoop101.out
hadoop103: starting datanode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-whx-datanode-hadoop103.out
Starting secondary namenodes [hadoop103]
hadoop103: starting secondarynamenode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-whx-secondarynamenode-hadoop103.out
[whx@hadoop102 ~]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
3361 Jps
3153 NameNode
3275 DataNode
3037 QuorumPeerMain
----------------------------hadoop102----------------------------------
3633 QuorumPeerMain
3876 DataNode
4106 Jps
----------------------------hadoop103----------------------------------
3281 SecondaryNameNode
3154 DataNode
3331 Jps
3038 QuorumPeerMain
[whx@hadoop102 ~]$

hbase htable 多线程 hbase支持多大的并发查询_hadoop


HBase中的对象的表现形式

  • 库以目录的形式存放在 /HBase/data中
  • 表是以子目录的形式存在在 /HBase/data/库名 中
  • region也是以子目录的形式存在 /HBase/data/库名/表名 中
  • 列族也是以子目录的形式存在 /HBase/data/库名/表名/region 中
  • 数据以文件的形式存放在 /HBase/data/库名/表名/region/列族 目录中

2、HBase服务的启动/停止

2.1 启动/停止方式一:分别启动/停止master(选择任意一台节点)、regionserver(所有节点)

  • 提示:如果集群之间的节点时间不同步,会导致regionserver无法启动,抛出ClockOutOfSyncException异常。
  • 修复提示:
  1. 同步时间服务
[whx@hadoop102 ~]$ xcall.sh sudo ntpdate -u ntp4.aliyun.com
要执行的命令是sudo ntpdate -u ntp4.aliyun.com
----------------------------hadoop101----------------------------------
 2 Feb 11:37:55 ntpdate[5838]: adjust time server 203.107.6.88 offset -0.010311 sec
----------------------------hadoop102----------------------------------
 2 Feb 11:37:56 ntpdate[8752]: adjust time server 203.107.6.88 offset -0.000953 sec
----------------------------hadoop103----------------------------------
 2 Feb 11:37:56 ntpdate[5930]: adjust time server 203.107.6.88 offset -0.004596 sec
[whx@hadoop102 ~]$
  1. 属性:hbase.master.maxclockskew设置更大的值
<property>
        <name>hbase.master.maxclockskew</name>
        <value>180000</value>
        <description>Time difference of regionserver from master</description>
 </property>

启动任意一个节点上的master(只启动一个节点上的master,比如hadoop102 )

[whx@hadoop102 ~]$ /opt/module/hbase/bin/hbase-daemon.sh start master
starting master, logging to /opt/module/hbase/bin/../logs/hbase-whx-master-hadoop102.out
[whx@hadoop102 ~]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
3153 NameNode
3275 DataNode
3037 QuorumPeerMain
3670 Jps
----------------------------hadoop102----------------------------------
3876 DataNode
4426 HMaster
3633 QuorumPeerMain
4641 Jps
----------------------------hadoop103----------------------------------
3281 SecondaryNameNode
3154 DataNode
3038 QuorumPeerMain
3654 Jps
[whx@hadoop102 ~]$

停止hadoop102 节点上的master

[whx@hadoop102 ~]$ /opt/module/hbase/bin/hbase-daemon.sh stop master
stopping master.
[whx@hadoop102 ~]$

启动所有服务器上的regionserver

[whx@hadoop102 ~]$ xcall.sh /opt/module/hbase/bin/hbase-daemon.sh start regionserver
要执行的命令是/opt/module/hbase/bin/hbase-daemon.sh start regionserver
----------------------------hadoop101----------------------------------
starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop101.out
----------------------------hadoop102----------------------------------
starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop102.out
----------------------------hadoop103----------------------------------
starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop103.out
[whx@hadoop102 ~]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
3153 NameNode
3275 DataNode
3037 QuorumPeerMain
3447 HRegionServer
3610 Jps
----------------------------hadoop102----------------------------------
3876 DataNode
4426 HMaster
3633 QuorumPeerMain
4196 HRegionServer
4363 Jps
----------------------------hadoop103----------------------------------

3281 SecondaryNameNode
3154 DataNode
3038 QuorumPeerMain
3425 HRegionServer
3592 Jps
[whx@hadoop102 ~]$

停止所有服务器上的regionserver

[whx@hadoop102 ~]$ xcall.sh /opt/module/hbase/bin/hbase-daemon.sh stop regionserver
要执行的命令是/opt/module/hbase/bin/hbase-daemon.sh stop regionserver
----------------------------hadoop101----------------------------------
stopping regionserver.
----------------------------hadoop102----------------------------------
stopping regionserver.
----------------------------hadoop103----------------------------------
stopping regionserver.
[whx@hadoop102 ~]$

端口说明:

  • 16000是master进程的RPC端口!
  • 16010是master进程的http端口!
  • 16020是RegionServer进程的RPC端口!
  • 16030是RegionServer进程的http端口!

通过Web浏览器,输入 hadoop102:16010,可以通过hadoop102查看HMaster信息

hbase htable 多线程 hbase支持多大的并发查询_大数据_02


hbase htable 多线程 hbase支持多大的并发查询_hbase htable 多线程_03


hbase htable 多线程 hbase支持多大的并发查询_大数据_04

2.2 启动/停止方式二:群起/群停所有节点上的regionserver

hbase-daemons.sh、start-hbase.sh、stop-hbase.sh命令的前提:

  • 先配置要执行这些命令所在的机器的 /opt/module/hbase/conf/regionservers文件。
  • 在hadoop102节点的 /opt/module/hbase/conf/regionservers 配置文件中添加要群起的 regionserver 所在的节点主机名,将 regionserver 配置文件默认值 localhost 改为:
hadoop101
hadoop102
hadoop103
2.2.1 群起/群停所有服务器上的regionserver【hbase-daemons.sh命令】

群起所有服务器上的regionserver

[whx@hadoop102 ~]$ /opt/module/hbase/bin/hbase-daemons.sh start regionserver
hadoop102: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop102.out
hadoop101: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop101.out
hadoop103: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop103.out
[whx@hadoop102 ~]$

群停所有服务器上的regionserver

[whx@hadoop102 ~]$ /opt/module/hbase/bin/hbase-daemons.sh stop regionserver
hadoop102: stopping regionserver.....
hadoop103: stopping regionserver.....
hadoop101: stopping regionserver........
[whx@hadoop102 ~]$
2.2.2 群起/群停 master®ionserver【start-hbase.sh命令、stop-hbase.sh命令】
[whx@hadoop102 ~]$ /opt/module/hbase/bin/start-hbase.sh
starting master, logging to /opt/module/hbase/bin/../logs/hbase-whx-master-hadoop102.out
hadoop103: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop103.out
hadoop101: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop101.out
hadoop102: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop102.out
[whx@hadoop102 ~]$ xcall.sh jps
[whx@hadoop102 ~]$ /opt/module/hbase/bin/stop-hbase.sh
stopping hbase..................
[whx@hadoop102 ~]$

三、HBase数据库的使用

先在HBase数据库中创建一个测试表:whx_table

hbase(main):019:0> create 'whx_table','cf_user','cf_company'
0 row(s) in 1.2170 seconds

=> Hbase::Table - whx_table
hbase(main):020:0> desc 'whx_table'
Table whx_table is ENABLED                                                                                                                         
whx_table                                                                                                                                          
COLUMN FAMILIES DESCRIPTION                                                                                                                        
{NAME => 'cf_company', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                 
{NAME => 'cf_user', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                    
2 row(s) in 0.0100 seconds

hbase(main):021:0>

向whx_table表中插入数据

hbase(main):027:0> put 'whx_table','1001','cf_user:firstname','Nick'
0 row(s) in 0.0470 seconds
hbase(main):029:0> put 'whx_table','1001','cf_user:lastname','Lee'
0 row(s) in 0.0150 seconds
hbase(main):030:0> put 'whx_table','1001','cf_company:name','HUAWEI'
0 row(s) in 0.0140 seconds
hbase(main):031:0> put 'whx_table','1001','cf_company:address','changanjie10hao'
0 row(s) in 0.0080 seconds
hbase(main):033:0> get 'whx_table','1001'
COLUMN                                                              CELL                                                                            
 cf_company:address                                                 timestamp=1612408142513, value=changanjie10hao                                  
 cf_company:name                                                    timestamp=1612408141461, value=HUAWEI                                           
 cf_user:firstname                                                  timestamp=1612408054676, value=Nick                                             
 cf_user:lastname                                                   timestamp=1612408141421, value=Lee                                              
1 row(s) in 0.0200 seconds

hbase(main):034:0>

四、HBase与Hive的集成

1、HBase与Hive的对比

1.1 Hive

  • 数据仓库:Hive的本质其实就相当于将HDFS中已经存储的文件在Mysql中做了一个双射关系,以方便使用HQL去管理查询。
  • 用于数据分析、清洗:Hive适用于离线的数据分析和清洗,延迟较高。
  • 基于HDFS、MapReduce:Hive存储的数据依旧在DataNode上,编写的HQL语句终将是转换为MapReduce代码执行。

1.2 HBase

  • 数据库:是一种面向列存储的非关系型数据库。
  • 用于存储结构化和非结构化的数据:适用于单表非关系型数据的存储,不适合做关联查询,类似JOIN等操作。
  • 基于HDFS:数据持久化存储的体现形式是Hfile,存放于DataNode中,被ResionServer以region的形式进行管理。
  • 延迟较低,接入在线业务使用:面对大量的企业数据,HBase可以直线单表大量数据的存储,同时提供了高效的数据访问速度。

2、HBase与Hive集成

2.1 环境准备

首先保障环境变量文件 /etc/profile 文件中配备了 Hive、HBase的环境变量

HIVE_HOME=/opt/module/hive
HBASE_HOME=/opt/module/hbase
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$FLUME_HOME/bin:$HBASE_HOME/bin

export JAVA_HOME HADOOP_HOME HIVE_HOME FLUME_HOME HBASE_HOME PATH

2.2 创建软连接

因为我们后续可能会在操作Hive的同时对HBase也会产生影响,所以Hive需要持有操作HBase的Jar,那么接下来拷贝Hive所依赖的Jar包(或者使用软连接的形式)。在hadoop102服务器上执行以下命令,创建软连接:

ln -s $HBASE_HOME/lib/HBase-common-1.3.1.jar  $HIVE_HOME/lib/HBase-common-1.3.1.jar
ln -s $HBASE_HOME/lib/HBase-server-1.3.1.jar $HIVE_HOME/lib/HBase-server-1.3.1.jar
ln -s $HBASE_HOME/lib/HBase-client-1.3.1.jar $HIVE_HOME/lib/HBase-client-1.3.1.jar
ln -s $HBASE_HOME/lib/HBase-protocol-1.3.1.jar $HIVE_HOME/lib/HBase-protocol-1.3.1.jar
ln -s $HBASE_HOME/lib/HBase-it-1.3.1.jar $HIVE_HOME/lib/HBase-it-1.3.1.jar
ln -s $HBASE_HOME/lib/htrace-core-3.1.0-incubating.jar $HIVE_HOME/lib/htrace-core-3.1.0-incubating.jar
ln -s $HBASE_HOME/lib/HBase-hadoop2-compat-1.3.1.jar $HIVE_HOME/lib/HBase-hadoop2-compat-1.3.1.jar
ln -s $HBASE_HOME/lib/HBase-hadoop-compat-1.3.1.jar $HIVE_HOME/lib/HBase-hadoop-compat-1.3.1.jar

上面的命令是在$HIVE_HOME/lib 目录中创建 $HBASE_HOME/lib/中源文件的软连接,创建软连接替代jar文件的复制来避免服务器中的jar包冗余。

2.3 配置Hive的Zookeeper管理器

Hive读取HBase里的数据时,需要使用Zookeeper,所以在hive-site.xml中添加zookeeper的属性,如下:

<property>
  <name>hive.zookeeper.quorum</name>
  <value>hadoop101,hadoop102,hadoop103</value>
  <description>The list of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
</property>
<property>
  <name>hive.zookeeper.client.port</name>
  <value>2181</value>
  <description>The port of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
</property>

添加后的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://hadoop102:3306/metastore?createDatabaseIfNotExist=true</value>
	  <description>JDBC connect string for a JDBC metastore</description>
	</property>

	<property>
	  <name>javax.jdo.option.ConnectionDriverName</name>
	  <value>com.mysql.jdbc.Driver</value>
	  <description>Driver class name for a JDBC metastore</description>
	</property>

	<property>
	  <name>javax.jdo.option.ConnectionUserName</name>
	  <value>root</value>
	  <description>username to use against metastore database</description>
	</property>

	<property>
	  <name>javax.jdo.option.ConnectionPassword</name>
	  <value>123456</value>
	  <description>password to use against metastore database</description>
	</property>
	<!--自定义Hive的数据仓库在HDFS中的位置。默认为:/user/hive/warehouse-->
	<property>
		<name>hive.metastore.warehouse.dir</name>
		<value>/user/hive/warehouse</value>
		<description>location of default database for the warehouse</description>
	</property>
	<!--实现显示当前数据库,以及查询表的头信息配置-->
	<property>
		<name>hive.cli.print.header</name>
		<value>true</value>
	</property>
	<property>
		<name>hive.cli.print.current.db</name>
		<value>true</value>
	</property>
	<!--Hive读取HBase里的数据时,需要用到Zookeeper-->
	<property>
	  <name>hive.zookeeper.quorum</name>
	  <value>hadoop101,hadoop102,hadoop103</value>
	  <description>The list of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
	</property>
	<property>
	  <name>hive.zookeeper.client.port</name>
	  <value>2181</value>
	  <description>The port of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
	</property>
	
</configuration>

2.4 在Hive建表,该表需要与HBase中的数据进行映射

  • 在建表时,Hive中的表字段的类型要和HBase中表列的类型一致,以避免类型转换失败造成数据丢失;
  • row format 的作用是指定表在读取数据时,使用什么分隔符来切割数据,只有正确的分隔符,才能正确切分字段;
  • 管理表(managed_table):由Hive的表管理数据的生命周期!在Hive中,执行droptable时,不仅将Hive中表的元数据删除,还把表目录中的数据删除;
  • 外部表(external_table):Hive表不负责数据的生命周期!在Hive中,执行droptable时,只会将hive中表的元数据删除,不把表目录中的数据删除;
2.4.1 数据已经在HBase中的情况

数据已经在HBase中,只需要在Hive建表,查询即可

create external table hbase_t3(
   id int,
   age int,
   gender string,
   name string
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:age,info:gender,info:name")
TBLPROPERTIES ("hbase.table.name" = "t3");
  • external 表示所创建的是外部表,因为此时的数据实在HBase里,而不是在Hive中。所以Hive只能创建外部表;
  • Storage Handlers是Hive的一个扩展模块,帮助Hive分析不在hdfs存储的数据。
  • HBaseStorageHandler:存储在hbase上的数据可以使用Hive提供的对HBase的Storage Handlers(即HBaseStorageHandler)来读写HBase中的数据
  • native table: 本地表。 Hive无需通过Storage Handlers就能访问的表。数据由hive负责存储,通常是存储在HDFS上。
  • non-native table : 数据由其他框架存储,如果使用非本地表,在建表时,需要指定 stored by “storageHandler” 。Hive必须通过Storage Handlers才能访问非本地表。例如和HBase集成的表。
  • SERDE:SerDe是序列化器和反序列化器所在库的简写。SerDe的作用是在运行MR程序时,从输入目录中读取数据,反序列化为Mapper输入的key-value对象,或将Reducer写出的key-value对象,使用序列化存储到指定的输出目录中。输入目录或输出目录中数据的格式不同,就需要使用不同的SerDe。普通的文件数据,以及在建表时,如果不指定serde,默认使用LazySimpleSerDe!
  • 纯文本: row format delimited ,默认使用LazySimpleSerDe
  • JSON格式: 使用JsonSerde
  • ORC: 使用读取ORC的SerDe
  • Paquet: 使用读取PaquetSerDe

例如: 数据中全部是JSON格式

{"name":"songsong","friends":["bingbing","lili"]}
{"name":"songsong1","friends": ["bingbing1" , "lili1"]}

错误写法:

create table testSerde(
name string,
friends array<string>
)
ROW FORMAT DELIMITED fields terminated by ','
collection items terminated by ','
lines terminated by '\n';

如果指定了row format delimited ,此时默认使用LazySimpleSerDe,而LazySimpleSerDe只能处理有分隔符的普通文本。现在数据是JSON,格式{},只能用JSONSerDE

create table testSerde2(
name string,
friends array<string>
)
ROW FORMAT SERDE 
'org.apache.hive.hcatalog.data.JsonSerDe' 
STORED AS TEXTFILE
  • WITH SERDEPROPERTIES (“hbase.columns.mapping” = “:key,info:age,info:gender,info:name”) 表示HBase与Hive之间字段的一对一对应;

Hive字段

HBase字段

备注

id

:key

:key 代表 HBase里的 rowkey,即使用 id 作为主键

age

info:age

info:age 表示 列族info里的age列

gender

info:gender

info:gender表示列族info里的gender列

name

info:name

info:name 表示列族info里的name列

  • TBLPROPERTIES (“hbase.table.name” = “t3”) 表示所创建的Hive表“hbase_t3”所对应的HBase表名为“t3”
2.4.2 数据还尚未插入到HBase的情况

数据还尚未插入到hbase,可以在hive中建表,建表后,在hive中执行数据的导入,将数据导入到hbase,再分析。

在Hive中创建表

CREATE  TABLE `hbase_emp`(
  `empno` int, 
  `ename` string, 
  `job` string, 
  `mgr` int, 
  `hiredate` string, 
  `sal` double, 
  `comm` double, 
  `deptno` int)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,
info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "emp");
  • 表必须是管理表(managed non-native table) ;
  • WITH SERDEPROPERTIES (“hbase.columns.mapping” = “:key,info:ename,info:job,info:mgr,
    info:hiredate,info:sal,info:comm,info:deptno”) 表示HBase与Hive之间字段的一对一对应;

Hive字段

HBase字段

备注

empno

:key 代表 HBase里的 rowkey,即使用 empno 作为主键

ename

info:ename

info:ename 表示 列族info里的ename列

job

info:job

info:job 表示 列族info里的job列

mgr

info:mgr

info:mgr 表示 列族info里的mgr列

hiredate

info:hiredate

info:hiredate 表示 列族info里的hiredate列

sal

info:sal

info:sal 表示 列族info里的sal列

comm

info:comm

info:comm 表示 列族info里的comm列

deptno

info:deptno

info:deptno表示 列族info里的deptno列

  • TBLPROPERTIES (“hbase.table.name” = “emp”) 表示所创建的Hive表“hbase_emp”所对应的HBase表名为“emp”

使用insert向Hive的表里导入数据,导入的数据就保存在HBase里了。

insert into table hbase_emp select * from emp

向关联了HBase的Hive中导入数据不能使用load,因为load导入数据是一个put操作,而insert操作则会调用MapReduce向HBase里插入数据。

导入成功后:

  • HBase做在的HDFS目录/HBase目录下就有了所插入的数据;
  • Hive所在的HDFS目录/hive下会多一个hbase_emp空文件夹,里面没有数据;

2.5 Hive与HBase版本适配问题

HBase版本

Hive版本

Hadoop版本

0.94

1.2.1

**

1.3.1

2.x

2.5.2

如果所用的Hive版本是1.2.1,而HBase的版本是1.3.1,则二者不匹配。

需要重新编译Hive(1.2.1) 的源码里的hive-hbase-handler-1.2.1.jar(因为只有这一部分不适配)。

  • 将Hive的源码文件压缩包apache-hive-1.2.1-src.zip里的hbase-handler文件夹解压出来
  • 将解压出来的hbase-handler文件夹导入到IntellJ里(Maven工程),导入时禁止pom.xml里的依赖自动下载;
  • 创建一个与src目录同级的lib目录
  • 在lib目录下放与HBase的1.3.1版本匹配的jar包
  • 编译,将src重新打包成jiar包,名称依旧为hive-hbase-handler-1.2.1.jar,覆盖原来的hive-hbase-handler-1.2.1.jar

五、HBase的优化

1、HBase的高可用配置(配置多个HMaster)

在HBase中Hmaster负责监控RegionServer的生命周期,均衡RegionServer的负载。如果Hmaster挂掉了,那么整个HBase集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以HBase支持对Hmaster的高可用配置。

  • 关闭HBase集群(如果没有开启则跳过此步)
[whx@hadoop102 hbase]$ bin/stop-hbase.sh
  • 在conf目录下创建backup-masters文件
[whx@hadoop102 hbase]$ touch conf/backup-masters
  • 在backup-masters文件中配置高可用HMaster节点
hadoop101
hadoop102
hadoop103
  • 将backup-masters文件分发到其他节点
[whx@hadoop102 conf]$ xsync.sh backup-masters
  • 重启HBase后,可以看到hadoop101、hadoop102、hadoop103都为HMaster,HBase会从3个HMaster中挑选一个作为Active状态,其余为Standby状态。
[whx@hadoop102 conf]$ /opt/module/hbase/bin/start-hbase.sh
starting master, logging to /opt/module/hbase/logs/hbase-whx-master-hadoop102.out
hadoop102: starting regionserver, logging to /opt/module/hbase/logs/hbase-whx-regionserver-hadoop102.out
hadoop101: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop101.out
hadoop103: starting regionserver, logging to /opt/module/hbase/bin/../logs/hbase-whx-regionserver-hadoop103.out
hadoop103: starting master, logging to /opt/module/hbase/bin/../logs/hbase-whx-master-hadoop103.out
hadoop101: starting master, logging to /opt/module/hbase/bin/../logs/hbase-whx-master-hadoop101.out
hadoop102: master running as process 8814. Stop it first.
[whx@hadoop102 conf]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
6032 HMaster
5621 DataNode
6213 Jps
5501 NameNode
5773 QuorumPeerMain
5903 HRegionServer
----------------------------hadoop102----------------------------------
9304 Jps
8984 HRegionServer
8601 QuorumPeerMain
8301 DataNode
8814 HMaster
----------------------------hadoop103----------------------------------
6210 SecondaryNameNode
6085 DataNode
6328 QuorumPeerMain
6841 Jps
6462 HRegionServer
6591 HMaster
[whx@hadoop102 conf]$
  • 通过Web浏览器,输入 hadoop102:16010,可以通过hadoop102查看HMaster信息
  • 通过Web浏览器,输入 hadoop101:16010,可以通过hadoop102查看HMaster信息
  • 模拟处于Active状态的HMaster所在的节点hadoop102出现故障
[whx@hadoop102 conf]$ xcall.sh jps
要执行的命令是jps
----------------------------hadoop101----------------------------------
6032 HMaster
5621 DataNode
6213 Jps
5501 NameNode
5773 QuorumPeerMain
5903 HRegionServer
----------------------------hadoop102----------------------------------
9304 Jps
8984 HRegionServer
8601 QuorumPeerMain
8301 DataNode
8814 HMaster
----------------------------hadoop103----------------------------------
6210 SecondaryNameNode
6085 DataNode
6328 QuorumPeerMain
6841 Jps
6462 HRegionServer
6591 HMaster
[whx@hadoop102 conf]$ kill -9 8814
[whx@hadoop102 conf]$
  • 此时,hadoop101由Standby状态变为Active状态,hadoop102节点的HMaster进程即使恢复,hadoop102也只能为Standby状态

2、预分区

通常情况下,每次建表时,默认只有一个region!随着这个region的数据不断增多,region会自动切分!

  • 自动切分: 讲当前region中的所有的rowkey进行排序,排序后取start-key和stopkey中间的rowkey,由这个rowkey一分为二,一分为二后,生成两个region,新增的Region会交给其他的RS负责,目的是为了达到负载均衡,但是通常往往会适得其反!
  • 为例避免某些热点Region同时分配到同一个RegionServer,可以在建表时,自己提前根据数据的特征规划region!
  • 每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。
  • 如果使用HexStringSplit算法,随机生成region的边界。在插入一条数据时,rowkey必须先采取HexString,转为16进制,再插入到表中。

2.1 预分区方式①:手动设定预分区

HBase> create 'staff1','info','partition1',SPLITS => ['1000','2000','3000','4000']

2.2 预分区方式②:生成16进制序列预分区

create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

2.3 预分区方式③:按照文件中设置的规则预分区

创建splits.txt文件内容如下:

aaaa
bbbb
cccc
dddd

然后执行:

create 'staff3','partition3',SPLITS_FILE => 'splits.txt'

2.4 预分区方式④:使用JavaAPI创建预分区

//自定义算法,产生一系列Hash散列值存储在二维数组中
byte[][] splitKeys = 某个散列值函数
//创建HBaseAdmin实例
HBaseAdmin hAdmin = new HBaseAdmin(HBaseConfiguration.create());
//创建HTableDescriptor实例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通过HTableDescriptor实例和散列值二维数组创建带有预分区的HBase表
hAdmin.createTable(tableDesc, splitKeys);

3、RowKey设计

一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪个一个预分区的区间内。

设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。

3.1 RowKey常用的设计方案①:生成随机数、hash、散列值

原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7

原本rowKey为3001的,SHA1后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd

原本rowKey为5001的,SHA1后变成:7b61dec07e02c188790670af43e717f0f46e8913

在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowKey来Hash后作为每个分区的临界值。

3.2 RowKey常用的设计方案②:字符串反转

时间戳的反转:

20170524000001转成10000042507102

20170524000002转成20000042507102

这样也可以在一定程度上散列逐步put进来的数据。

3.1 RowKey常用的设计方案③:字符串拼接

20170524000001_a12e

20170524000001_93i7

使用字符串拼接设计Rowkey,原则:

  1. Rowkey作为数据的唯一主键,需要紧密和业务相关,从业务中选择某个代表性的字段作为Rowkey
  2. 保证Rowkey字段选取时的唯一性,不重复性
  3. Rowkey足够散列,负载均衡
  4. 让有业务关联的Rowkey尽量分布到一个Region中

例如: 转账的场景
| 流水号 | 转入账户 | 转出账户 | 时间 | 金额 | 用户 |
|–|–|–|–|–|–|–|
| *** |*** |*** |*** |*** |*** |

流水号适合作为RowKey,将流水号再拼接字符串,生成完整的RowKey

格式:

  • 流水号+时间戳
  • 时间戳+流水号
  • 流水号+随机数
  • 随机数+流水号

如果流水号在设计时,足够散列,可以使用流水号在前,拼接随机数。

如果流水号不够散列,可以使用函数计算其散列值,或拼接一个散列的值。

举例:如何让一个月的数据,分布到同一个Region!可以取月份的时间,作为计算的参数,使用hash运算,将运算后的字符串,拼接到Rowkey前部。

4、内存优化

  • HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
  • 在/opt/module/hbase/conf/hbase-env.sh 中,编写regionserver进程启动时的JVM参数!
  • -Xms : JVM堆的起始值
  • -Xmx : JVM堆的最大值
  • export HBASE_HEAPSIZE=1G 或 export HBASE_OPTS=“-XX:+UseConcMarkSweepGC”

5、基础优化

5.1 允许在HDFS的文件中追加内容(hdfs-site.xml、HBase-site.xml)

  • 属性:dfs.support.append
  • 解释:开启HDFS追加同步,可以优秀的配合HBase的数据同步和持久化。默认值为true。

此处dfs.support.append如果设置为false,则HBase的所有功能则没法实现。所以一定设置为true。

5.2 优化DataNode允许的最大文件打开数(hdfs-site.xml)

  • 属性:dfs.datanode.max.transfer.threads
  • 解释:HBase一般都会同一时间开启大量IO线程来操作大量的文件,根据集群的数量和规模以及数据动作,设置IO线程数量为4096或者更高。默认值:4096

5.3 优化延迟高的数据操作的等待时间(hdfs-site.xml)

  • 属性:dfs.image.transfer.timeout
  • 解释:如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒,即1分钟),以确保socket不会被timeout掉。
  • 比如:如果项目是一个高并发场景,而且RegionServer的性能也不是很好,可以把参数调大一些,防止数据操作失败。

5.4 优化数据的写入效率(mapred-site.xml)

  • 属性:
  • mapreduce.map.output.compress
  • mapreduce.map.output.compress.codec
  • 解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为true,第二个属性值修改为:org.apache.hadoop.io.compress.GzipCodec或者其他压缩方式。

5.5 设置RPC监听数量(hbase-site.xml)

  • 属性:HBase.regionserver.handler.count
  • 解释:默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。

5.6 优化HStore文件大小(hbase-site.xml)

  • 属性:HBase.hregion.max.filesize
  • 解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。

5.7 优化HBase客户端缓存(hbase-site.xml)

  • 属性:HBase.client.write.buffer
  • 解释:用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。

5.8 指定scan.next扫描HBase所获取的行数(hbase-site.xml)

  • 属性:HBase.client.scanner.caching
  • 解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。

5.9 flush、compact、split机制

当MemStore达到阈值,将Memstore中的数据Flush进Storefile;compact机制则是把flush出来的小文件合并成大的Storefile文件。split则是当Region达到阈值,会把过大的Region一分为二。

  • HBase.hregion.memstore.flush.size:134217728
    即:这个参数的作用是当单个HRegion内所有的Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模型来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。
  • HBase.regionserver.global.memstore.upperLimit:0.4
  • HBase.regionserver.global.memstore.lowerLimit:0.38
    即:当MemStore使用内存总量达到HBase.regionserver.global.memstore.upperLimit指定值时,将会有多个MemStores flush到文件中,MemStore flush 顺序是按照大小降序执行的,直到刷新到MemStore使用内存略小于lowerLimit

六、HBase中设置布隆过滤器

HBase中通过“列族”设置过滤器,HBase支持两种布隆过滤器: ROW|ROWCOL

注意: 旧版本,只有get操作,才会用到布隆过滤器,scan用不到! 1.x之后,scan也可用用布隆过滤器,稍微起点作用!

启用布隆过滤器后,会占用额外的内存,布隆过滤器通常是在blockcache和memstore中!

1、ROW布隆过滤器

ROW布隆过滤器在计算时,使用每行的Rowkey作为参数,进行判断。举例:info1列族、info2列族:

  • info1 storefile1: (r1,info1:age,20) ,(r2,info1:age,20)
  • info2 storefile2: (r3,info2:age,20) ,(r4,info2:age,20)

查询 r1 时,如果命中,判断storefile2中一定没有r1的数据,在storefile1中可能有!

2、ROWCOL布隆过滤器

ROWCOL 布隆过滤器在计算时,使用每行的rowkey和column一起作为参数,进行判断!举例: info info1

  • info1 storefile1: (r1,info1:age,20) ,(r2,info1:age,20)
  • info2 storefile2: (r3,info2:age,20) ,(r4,info2:age,20)

查询rowkey=r1,只查info1:age=20 列时,如果命中,判断storefile2中一定没有此数据,在storefile1中可能有!

3、举例: 执行 get ‘t1’,‘r1’

  • 扫描r1所在region的所有列族的memstore,扫memstore时,先通过布隆过滤器判断r1是否存在,如果不存在,就不扫!可能存在,再扫描!
  • 扫描Storefile时,如果storefile中,r1所在的block已经缓存在blockcache中,直接扫blockcache,在扫描blockcache时,先使用布隆过滤器判断r1是否存在,如果不存在,就不扫!可能存在,再扫描!

七、HBase在商业项目中的能力(每天)

  • 消息量:发送和接收的消息数超过60亿
  • 将近1000亿条数据的读写
  • 高峰期每秒150万左右操作
  • 整体读取数据占有约55%,写入占有45%
  • 超过2PB的数据,涉及冗余共6PB数据
  • 数据每月大概增长300千兆字节