大数据之HBase

1 NoSQL简介

1.1 关系型数据库的查询瓶颈

当用户表的数据达到几千万甚至几亿级别的时候,对单条数据的检索将花费数秒甚至达到分钟级别。实际情况更复杂,查询的操作速度将会受到以下两个因素的影响:

  1. 高并发的更新(插入、修改、删除)操作。大中型网站的并发操作一般能达到几十乃至几百并发,此时单条数据查询的延时将轻而易举地达到分钟级别。
  2. 多表关联后的复杂查询,以及频繁的group by或者order by操作,此时,性能下降较为明显。

1.2 CAP定理

分摊读写压力的有效方式是将单个关系型数据库扩展为分布式数据库。但是,随之而来的问题则是很难保证原子性。没有了原子性,事务也无从谈起,关系型数据库也就没有了存在的意义。为了保证原子性,则需要增加很多额外的必要操作,此时一次写操作的性能却面临大幅下降了。
20世纪90年代初期Berkerly大学有位Eric Brewer教授提出了一个CAP理论。全称是Consistency Availability and Partition tolerance。

  • Consistency(强一致性):数据更新操作的一致性,所有数据变动都是同步的。
  • Availability(高可用性):良好的响应性能。
  • Partition tolerance(高分区容错性):可靠性。

Brewer教授给出的定理是:任何分布式系统只可同时满足二点,没法三者兼顾。
Brewer教授给出的忠告是:架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。所以专家们始终没有办法构建出一个既有完美原子性又兼具高性能的分布式数据库。
CAP
当前比较好的解决方案:分区容错性+高可用+最终一致性

1.3 NoSQL

有些数据库在实现性能的同时会牺牲一部分一致性,即数据在更新时,不会立刻同步,而是经过了一段时间才达到一致性。这个特性也称之为最终一致性!例如你发了一条朋友圈,你的一部分朋友立马看到了这条信息,而另一部分朋友可能要等到1分钟之后才能刷出这条消息。虽然有延时,但是对于这样一个社交的场景,这个延时是可以容忍的。而如果使用传统关系型数据库,可能这些即时通信软件就早已崩溃!
NoSQL数据库最初指不使用SQL标准的数据库,现在泛指非关系型数据库。NoSQL一词最早出现于1998年,是Carlo Strozzi开发的一个轻量、开源、不提供SQL功能的数据库。现在NoSQL被普遍理解理解为“Not Only SQL”,意为不仅仅是SQL。NoSQL和传统的关系型数据库在很多场景下是相辅相成的,谁也不能完全替代谁。

2 Hbase简介

2.1 Hbase的来源

2006年Google技术人员Fay Chang发布了一篇文章《Bigtable: ADistributed Storage System for Structured Data》。该文章向世人介绍了一种分布式的数据库,这种数据库可以在局部几台服务器崩溃的情况下继续提供高性能的服务。
2007年Powerset公司的工作人员基于此文研发了BigTable的Java开源版本,即HBase。刚开始它只是Hadoop的一部分。
2008年HBase成为了Apache的顶级项目。HBase几乎实现了BigTable的所有特性。它被称为一个开源的非关系型分布式数据库。
2010年HBase的开发速度打破了一直以来跟Hadoop版本一致的惯例,因为HBase的版本发布速度已经超越了Hadoop。它的版本号一下从0.20.x跳跃到了0.89.x。其Logo也进行了更换!

2.2 Hbase定义

Apache HBase™ 是一个分布式的、可扩展的、支持海量数据存储的Hadoop NoSQL数据库。

Apache HBase™ 是一个分布式的、面向列的开源数据库,支持对 数十亿行x数百万列 数据的实时随机读写。该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。就像Bigtable利用了Google文件系统(Google File System)所提供的分布式数据存储一样,HBase在Hadoop之上提供了类似于Bigtable的能力。

问题: HBase是基于Hadoop的数据库,HDFS并不支持随机写,HBase是如何支持实时随机读写的?

1)实时

  • HBase在设计时,基于列族存储,列式存储对大数据的检索效率高
  • HBase采用K-V结构存储,即便数据量再大,也可以方便检索一条记录
  • HBase采取了LSM树作为数据的存储模型,在检索数据时,效率高
  • HBase会占用大量的内存,提前缓存部分数据(BlockCache)
  • HBase还采取了例如布隆过滤器等提高查询效率的一些其他组件

2)随机写

HBase的读写操作还是借助HDFS完成,要完成随机写,根本上还是需要符合HDFS的特性,HDFS只支持追加写。
HBase随机写的操作:Update+Delete 借助 HDFS追加写+时间戳(版本号)
只允许客户端查询时返回时间戳最新的数据!

2.3 HBase的特点

  • 海量存储
    HBase适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与HBase的极易扩展性息息相关。正式因为HBase良好的扩展性,才为海量数据的存储提供了便利。
  • 列式存储
    这里的列式存储其实说的是列族存储,HBase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定。
  • 极易扩展
    HBase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储的扩展(HDFS)。通过横向添加RegionSever的机器,进行水平扩展,提升HBase上层的处理能力,提升Hbsae服务更多Region的能力。
  • 高并发
    由于目前大部分使用HBase的架构,都是采用的廉价PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,HBase的单个IO延迟下降并不多。能获得高并发、低延迟的服务。
  • 稀疏
    稀疏主要是针对HBase列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。

2.3.1 HBase的优点

  • HDFS有高容错,高扩展的特点,而HBase基于HDFS实现数据的存储,因此HBase拥有与生俱来的超强的扩展性和吞吐量。
  • HBase采用的是Key/Value的存储方式,这意味着,即便面临海量数据的增长,也几乎不会导致查询性能下降。
  • HBase是一个列式数据库,相对于于传统的行式数据库而言。当你的单张表字段很多的时候,可以将相同的列(以region为单位)存在到不同的服务实例上,分散负载压力。

2.3.2 HBase的缺点

  • 架构设计复杂,且使用HDFS作为分布式存储,因此只是存储少量数据,它也不会很快。在大数据量时,它慢的不会很明显。
  • Hbase不支持表的关联操作,因此数据分析是HBase的弱项。常见的 group by或order by只能通过编写MapReduce来实现。
  • Hbase部分支持了ACID。

2.4 HBase应用场景

适合场景:单表超千万,上亿,且高并发。
不适合场景:主要需求是数据分析,比如做报表。数据量规模不大,对实时性要求高。

2.5 Hbase数据模型

2.5.1 逻辑结构

HBase通过Region进行表的横向切分(高表切片),通过列族进行表的纵向切分(宽表切片),通过Row Key(自带,字典排序)区别同一行数据。

在这里插入图片描述

2.5.2 物理存储结构

Hbase的物理存储是每一行的存储方式如下,TimeStamp对于Hbase极其重要,如果多台机器的时间不同步,将会造成bug。

Hbase物理模型

2.5.3 数据模型

1)Name Space
命名空间,类似于关系型数据库的database概念,每个命名空间下有多个表。HBase两个自带的命名空间,分别是hbase和default,hbase中存放的是HBase内置的表,default表是用户默认使用的命名空间。
一个表可以自由选择是否有命名空间,如果在创建表的时候加上了命名空间后,这个表名字以<Namespace>:<Table>作为区分!

2)Table
类似于关系型数据库的表概念。不同的是,HBase定义表时只需要声明列族即可,数据属性,比如超时时间(TTL),压缩算法(COMPRESSION)等,都在列族的定义中定义,不需要声明具体的列。
这意味着,往HBase写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase能够轻松应对字段变更的场景。

3)Row
HBase表中的每行数据都由一个RowKey和多个Column(列)组成。一个行包含了多个列,这些列通过列族来分类,行中的数据所属列族只能从该表所定义的列族中选取,不能定义这个表中不存在的列族,否则报错NoSuchColumnFamilyException。

4) RowKey
Rowkey由用户指定的一串不重复的字符串定义,是一行的唯一标识。数据是按照RowKey的字典顺序存储的,并且查询数据时只能根据RowKey进行检索,所以RowKey的设计十分重要。
如果使用了之前已经定义的RowKey,那么会将之前的数据更新掉。

5)Column Family
列族是多个列的集合。一个列族可以动态地灵活定义多个列。表的相关属性大部分都定义在列族上,同一个表里的不同列族可以有完全不同的属性配置,但是同一个列族内的所有列都会有相同的属性。
列族存在的意义是HBase会把相同列族的列尽量放在同一台机器上,所以说,如果想让某几个列被放到一起,你就给他们定义相同的列族。
官方建议一张表的列族定义的越少越好,列族太多会极大程度地降低数据库性能,且目前版本Hbase的架构,容易出BUG。

6) Column Qualifier
Hbase中的列是可以随意定义的,一个行中的列不限名字、不限数量,只限定列族。因此列必须依赖于列族存在,列的名称前必须带着其所属的列族,例如 info:name,info:age。
因为HBase中的列全部都是灵活的,可以随便定义的,因此创建表的时候并不需要指定列,列只有在你插入第一条数据的时候才会生成。其他行有没有当前行相同的列是不确定,只有在扫描数据的时候才能得知。

7)TimeStamp
用于标识数据的不同版本(version)。时间戳默认由系统指定,也可以由用户显式指定。在读取单元格的数据时,版本号可以省略,如果不指定,Hbase默认会获取最后一个版本的数据返回。

8)Cell
一个列中可以存储多个版本的数据。而每个版本就称为一个单元格(Cell)。Cell由{rowkey, column Family:column Qualifier, timeStamp}确定。
Cell中的数据是没有类型的,全部是字节码形式存贮。
cell

9)Region
Region由一个表的若干行组成,在Region中行的排序按照行键(rowkey)字典排序。Region不能跨RegionSever,且当数据量大的时候,HBase会拆分Region。Region由RegionServer进程管理。HBase在进行负载均衡的时候,一个Region有可能会从当前RegionServer移动到其他RegionServer上。Region是基于HDFS的,它的所有数据存取操作都是调用了HDFS的客户端接口来实现的。

3 HBase安装

3.1 HBase下载安装

安装前请确认具有JDK环境,因为HBase是使用Java编写。

  1. 下载tar包,下载页面https://archive.apache.org/dist/hbase/
	#下载
	[yut@aliyun220 module]$ wget https://archive.apache.org/dist/hbase/1.3.1/hbase-1.3.1-bin.tar.gz
  1. 解压tar包
	[yut@aliyun220 module]$ tar -zxvf hbase-1.3.1-bin.tar.gz -C ./
  1. 配置HBase运行环境,修改hbase-env.sh
	#配置JDK目录,如果是JDK8+需要注释掉HBASE_MASTER_OPTS和HBASE_REGIONSERVER_OPTS配置
	[yut@aliyun220 hbase-1.3.1]$ vim conf/hbase-env.sh 
	export JAVA_HOME=/bme/jdk
	# 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"

3.2 HBase独立模式

  1. 配置数据存放目录,默认目录为${java.io.tmpdir}/hbase-${user.name}/hbase,当前用户即/tmp/hbase-yut/hbase,重启的时候操作系统会清理/tmp目录,可选择以下方式更改:
	<configuration>
	  <property>
	    <name>hbase.rootdir</name>
	    <value>file:///data/hbase</value>
	  </property>
	</configuration>
  1. 启动HBase
	[yut@aliyun220 hbase-1.3.1]$ bin/start-hbase.sh
  1. 通过JDK命令jps查看是否启动成功,
	#启动成功后,存在Master和RegionServer进程
	[yut@aliyun220 hbase-1.3.1]$ jps
	14481 HMaster
	14619 HRegionServer

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

  1. 停止HBase
	[yut@aliyun220 hbase-1.3.1]$ bin/stop-hbase.sh

3.3 HBase分布式模式

分布式模式必要条件:具有JDK环境;启动Hadoop和Zookeeper;操作系统必须安装ssh和sshd,并且配置系统间免密登录,Hadoop的脚本才可以远程操控其他的Hadoop和HBase进程;集群时间同步(NTP)。

#启动hadoop
[yut@aliyun220 hadoop-2.10.0]$ sbin/start-dfs.sh
[yut@aliyun221 hadoop-2.10.0]$ sbin/start-yarn.sh

#启动Zookeeper
[root@aliyun220 zookeeper]# bin/zkServer.sh start
  1. 修改hbase-env.sh
	#默认为true,使用HBase内嵌的Zookeeper,设置为false可以使用用户自己集群的Zookeeper
	export HBASE_MANAGES_ZK=false
  1. 修改hbase-site.xml配置
	
	<configuration>
	 	<!--启用集群分布式模式;默认为false,会启动hbase自带的zookeeper发生冲突-->
	    <property>
		    <name>hbase.cluster.distributed</name>
		    <value>true</value> 
		</property>
		
		<!--将数据存储到hdfs上-->
		<property>     
			<name>hbase.rootdir</name>     
			<value>hdfs://aliyun220:9000/HBase</value>   
		</property>
		
		<!--配置用户Zookeeper地址,多个以,分割-->
	    <property>
	        <name>hbase.zookeeper.quorum</name>
	        <value>aliyun220:2181</value>
	    </property>
		<!-- Zookeeper存储HBase数据的目录,默认为${hbase.tmp.dir}/zookeeper-->
		<property>   
			<name>hbase.zookeeper.property.dataDir</name>
		     <value>/opt/module/zookeeper-3.4.10/zkData</value>
		</property>
		
	    <!-- 0.98后的新变动,之前版本没有.port,默认端口为60000 -->
		<property>
			<name>hbase.master.port</name>
			<value>16000</value>
		</property>
		
	</configuration>
  1. 如果存在多个HBase节点,配置regionservers,并且分发HBase到其他节点
	#配置节点
	[yut@aliyun220 hbase-1.3.1]$ vim conf/regionservers 
	aliyun220
	aliyun221
	aliyun222
	#分发HBase
	[yut@aliyun220 hbase-1.3.1]$ xsync.sh ../hbase-1.3.1
  1. 启动HBase
	#启动方式一
	[yut@aliyun220 hbase-1.3.1]$ bin/hbase-daemon.sh start master
	[yut@aliyun220 hbase-1.3.1]$ bin/hbase-daemon.sh start regionserver
	[yut@aliyun221 hbase-1.3.1]$ bin/hbase-daemon.sh start regionserver
	[yut@aliyun222 hbase-1.3.1]$ bin/hbase-daemon.sh start regionserver
	#启动方式二
	[yut@aliyun220 hbase-1.3.1]$ bin/start-hbase.sh
	starting master, logging to /data1/module/hbase-1.3.1/bin/../logs/hbase-yut-master-aliyun220.out
	aliyun220: starting regionserver, logging to /data1/module/hbase-1.3.1/bin/../logs/hbase-yut-regionserver-aliyun220.out
	aliyun221: starting regionserver, logging to /data1/module/hbase-1.3.1/bin/../logs/hbase-yut-regionserver-aliyun221.out
	aliyun222: starting regionserver, logging to /data1/module/hbase-1.3.1/bin/../logs/hbase-yut-regionserver-aliyun222.out

启动成功后,web访问地址:http://aliyun220:16010(Master主机:16010)。
端口说明:

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

如果集群时间不同步可能会造成HBase启动失败,集群间的最大时间倾斜如下:

<!-- regionserver与master最大时钟倾斜 -->
<property>
        <name>hbase.master.maxclockskew</name>
        <value>180000</value>
        <description>Time difference of regionserver from master</description>
 </property>

可通过sudo ntpdate -u ntp4.aliyun.com命令同步时间,ntp服务器可以自定义。

3.4 HBase说明

hbase

HBase默认有两张系统表(hbase命名空间下):

  • hbase:meta: 保存的是用户的表和region的对应信息
  • hbase:namespace: 保存的是用户自己创建的namespace的信息

hdfs-hbase

hbase中的对象的表现形式:

  1. 库(nameSpace)以目录的形式存放在 /HBase/data
  2. 表()是以子目录的形式存在在/HBase/data/库名
  3. region也是以子目录的形式存在/HBase/data/库名/表名
  4. 列族也是以子目录的形式存在/HBase/data/库名/表名/region
  5. 数据以文件的形式存放在/HBase/data/库名/表名/region/列族目录中

脚本启动流程:
在这里插入图片描述

4 HBase Shell

[yut@aliyun220 hbase-1.3.1]$ bin/hbase
Usage: hbase [<options>] <command> [<args>]
Options:
  --config DIR     Configuration direction to use. Default: ./conf
  --hosts HOSTS    Override the list in 'regionservers' file
  --auth-as-server Authenticate to ZooKeeper using servers configuration

Commands:
Some commands take arguments. Pass no args or -h for usage.
  shell           Run the HBase shell
  hbck            Run the hbase 'fsck' tool
  snapshot        Tool for managing snapshots
  snapshotinfo    Tool for dumping snapshot information
  wal             Write-ahead-log analyzer
  hfile           Store file analyzer
  zkcli           Run the ZooKeeper shell
  upgrade         Upgrade hbase
  master          Run an HBase HMaster node
  regionserver    Run an HBase HRegionServer node
  zookeeper       Run a Zookeeper server
  rest            Run an HBase REST server
  thrift          Run the HBase Thrift server
  thrift2         Run the HBase Thrift2 server
  clean           Run the HBase clean up script
  classpath       Dump hbase CLASSPATH
  mapredcp        Dump CLASSPATH entries required by mapreduce
  pe              Run PerformanceEvaluation
  ltt             Run LoadTestTool
  canary          Run the Canary tool
  version         Print the version
  CLASSNAME       Run the class named CLASSNAME

HBase Shell操作:

hbase(main):001:0> help
HBase Shell, version 1.3.1, r930b9a55528fe45d8edce7af42fef2d35e77677a, Thu Apr  6 19:36:54 PDT 2017
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, table_help, version, whoami

  Group name: ddl
  Commands: alter, alter_async, alter_status, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, locate_region, show_filters

  Group name: namespace
  Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables

  Group name: dml
  Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve

  Group name: tools
  Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, close_region, compact, compact_rs, flush, major_compact, merge_region, move, normalize, normalizer_enabled, normalizer_switch, split, splitormerge_enabled, splitormerge_switch, trace, unassign, wal_roll, zk_dump

  Group name: replication
  Commands: add_peer, append_peer_tableCFs, disable_peer, disable_table_replication, enable_peer, enable_table_replication, get_peer_config, list_peer_configs, list_peers, list_replicated_tables, remove_peer, remove_peer_tableCFs, set_peer_tableCFs, show_peer_tableCFs

  Group name: snapshots
  Commands: clone_snapshot, delete_all_snapshot, delete_snapshot, delete_table_snapshots, list_snapshots, list_table_snapshots, restore_snapshot, snapshot

  Group name: configuration
  Commands: update_all_config, update_config

  Group name: quotas
  Commands: list_quotas, set_quota

  Group name: security
  Commands: grant, list_security_capabilities, revoke, user_permission

  Group name: procedures
  Commands: abort_procedure, list_procedures

  Group name: visibility labels
  Commands: add_labels, clear_auths, get_auths, list_labels, set_auths, set_visibility

SHELL USAGE:
Quote all names in HBase Shell such as table and column names.  Commas delimit
command parameters.  Type <RETURN> after entering a command to run it.
Dictionaries of configuration used in the creation and alteration of tables are
Ruby Hashes. They look like this:

  {'key1' => 'value1', 'key2' => 'value2', ...}

and are opened and closed with curley-braces.  Key/values are delimited by the
'=>' character combination.  Usually keys are predefined constants such as
NAME, VERSIONS, COMPRESSION, etc.  Constants do not need to be quoted.  Type
'Object.constants' to see a (messy) list of all constants in the environment.

If you are using binary keys or values and need to enter them in the shell, use
double-quote'd hexadecimal representation. For example:

  hbase(main):046:0> get 't1', "key\x03\x3f\xcd"
  hbase(main):046:0> get 't1', "key\003\023\011"
  hbase(main):046:0> put 't1', "test\xef\xff", 'f1:', "\x01\x33\x40"

The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added.
For more on the HBase Shell, see http://hbase.apache.org/book.html

使用help 'COMMAND'(例如help ‘get’,引号是必须的)可以获取指定命令的帮助信息;所有命令是被分组的,使用help 'COMMAND_GROUP'(例如help “general”)可以获取有关命令组的帮助信息。hbase shell使用ruby编写,不支持中文。

4.1 一般(general)操作

使用status可以查看集群状态,默认为summary,可以选择‘simple’和‘detailed’来查看详情,通过help 'status'查看用法。

hbase(main):009:0> status
1 active master, 0 backup masters, 1 servers, 0 dead, 2.0000 average load

查看版本。

hbase(main):029:0> version
1.3.1, r930b9a55528fe45d8edce7af42fef2d35e77677a, Thu Apr  6 19:36:54 PDT 2017

查看操作用户及组信息。

hbase(main):030:0> whoami
yut (auth:SIMPLE)
    groups: yut

查看表相关操作信息

hbase(main):001:0> table_help

4.1 库操作(namespace)

4.1.1 list_namespace

列出所有的namespace

hbase(main):046:0> list_namespace
hbase(main):046:0> list_namespace 'abc.*'

4.1.2 list_namespace_tables

列出指定namespace下所有表。

hbase(main):046:0> list_namespace_tables 'ns1'

4.1.3 create_namespace

创建namespace,命名空间的字典配置可选。

hbase(main):046:0> create_namespace 'ns1'
hbase(main):046:0> create_namespace 'ns1', {'PROPERTY_NAME'=>'PROPERTY_VALUE'}

4.1.4 describe_namespace

查看namespace详情

hbase(main):046:0> describe_namespace 'ns1

4.1.5 alter_namespace

修改namespace属性。

#添加/修改属性
hbase(main):046:0> alter_namespace 'ns1', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
#删除属性
hbase(main):046:0> alter_namespace 'ns1', {METHOD => 'unset', NAME=>'PROPERTY_NAME'}

4.1.6 drop_namespace

删除指定命名空间。

#删除namespace,namespace必须是空的
hbase(main):009:0> drop_namespace 'ns1'

4.3 表操作(DDL)

4.3.1 list

列出所有的表(除hbase命名空间)。

hbase(main):046:0> list
hbase(main):046:0> list 'abc.*'
hbase(main):046:0> list 'ns:abc.*'
hbase(main):046:0> list 'ns:.*'

list后可以使用*等通配符来进行表的过滤。

4.3.2 create

在创建表,需要指定表名和列族名,而且至少需要指定一个列族;创建表时,还可以指定表的属性,表的属性需要指定在列族上。列族使用NAME属性指定。
语法:

create '[namespace:]tableName', { NAME => '列族名1', 属性名 => 属性值}, {NAME => '列族名2', 属性名 => 属性值}, …,{表属性名=>表属性值,...}

示例:

#创建一个namespace=ns1和表名为t1的表,列族为f1,列族属性VERSIONS=5
hbase(main):007:0> create 'ns1:t1', {NAME => 'f1', VERSIONS => 5}

#创建一个namespace=default和表名为t1的表,列族为f1、f2、f3
hbase(main):007:0> create 't1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}
#上面命令的简写
hbase(main):007:0> create 't1', 'f1', 'f2', 'f3'

#创建一个namespace=default和表名为t1的表,列族为f1并赋值相关属性
hbase(main):007:0> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}
hbase(main):007:0> create 't1', {NAME => 'f1', CONFIGURATION => {'hbase.hstore.blockingStoreFiles' => '10'}}

#表的配置选项放在末尾
hbase(main):007:0> create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
hbase(main):007:0> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
hbase(main):007:0> create 't1', 'f1', SPLITS_FILE => 'splits.txt', OWNER => 'johndoe'
hbase(main):007:0> create 't1', {NAME => 'f1', VERSIONS => 5}, METADATA => { 'mykey' => 'myvalue' }
# Optionally pre-split the table into NUMREGIONS, using
# SPLITALGO ("HexStringSplit", "UniformSplit" or classname)
hbase(main):007:0> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
hbase(main):007:0> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit', REGION_REPLICATION => 2, CONFIGURATION => {'hbase.hregion.scan.loadColumnFamiliesOnDemand' => 'true'}}
hbase(main):007:0> create 't1', {NAME => 'f1', DFS_REPLICATION => 1}

4.3.3 describe

查看指定表的描述。

#查看表详情
hbase(main):016:0> desc 't1'
hbase(main):016:0> desc 'ns1:t1'

4.3.4 disable、disable_all、is_disabled

停用表后,可以防止在对表做一些维护时,客户端依然可以持续写入数据到表。一般在删除表前,必须停用表。在对表中的列族进行修改时,也需要停用表。

#禁用指定表
hbase(main):016:0> disable 't1'
hbase(main):016:0> disable 'ns1:t1'

#禁用所有匹配规则的表
hbase(main):016:0> disable_all 't.*'
hbase(main):016:0> disable_all 'ns:t.*'
hbase(main):016:0> disable_all 'ns:.*'

#判断指定表是否禁用
hbase(main):016:0> is_disabled 't1'
hbase(main):016:0> is_disabled 'ns1:t1'

4.3.5 enable、enable_all、is_enabled

#启用指定的表
hbase(main):016:0> enable 't1'
hbase(main):016:0> enable 'ns1:t1'

#启用所有匹配规则的表
hbase(main):016:0> enable_all 't.*'
hbase(main):016:0> enable_all 'ns:t.*'
hbase(main):016:0> enable_all 'ns:.*'

#判断指定表是否启用
hbase(main):016:0> is_enabled 't1'
hbase(main):016:0> is_enabled 'ns1:t1'

4.3.6 exists

查看表是否存在。

#判断指定表是否存在
hbase(main):016:0> exists 't1'
hbase(main):016:0> exists 'ns1:t1'

4.3.7 locate_region

查找指定表名和行键的region。

hbase(main):016:0> locate_region 'ns1:t1','r1'

4.3.8 show_filters

显示hbase中所有的filter。

hbase(main):033:0> show_filters
DependentColumnFilter                                                                                        
KeyOnlyFilter                                                                                                
ColumnCountGetFilter                                                                                         
SingleColumnValueFilter                                                                                      
PrefixFilter                                                                                                 
SingleColumnValueExcludeFilter                                                                               
FirstKeyOnlyFilter                                                                                           
ColumnRangeFilter                                                                                            
TimestampsFilter                                                                                             
FamilyFilter                                                                                                 
QualifierFilter                                                                                              
ColumnPrefixFilter                                                                                           
RowFilter                                                                                                    
MultipleColumnPrefixFilter                                                                                   
InclusiveStopFilter                                                                                          
PageFilter                                                                                                   
ValueFilter                                                                                                  
ColumnPaginationFilter 

4.3.9 get_table

获取给定的表名并将其作为实际对象返回给被用户操纵。

hbase(main):037:0> t1=get_table 'ns1:t1'

4.3.10 alter、alter_async、alter_status

alter命令可以添加、修改或删除列族或更改表配置选项,修改列族与create命令相似。如果hbase.online.schema.update.enable=false,alter表之前必须先disable;如果hbase.online.schema.update.enable=true,则不需要先disable,在过去,更改启用的表会导致问题,因此在生产中使用之前,请谨慎使用并测试它。

#改变或添加列族f1
hbase(main):037:0> alter 't1', NAME => 'f1', VERSIONS => 5

#对多个列族操作
hbase(main):037:0> alter 't1', 'f1', {NAME => 'f2', IN_MEMORY => true}, {NAME => 'f3', VERSIONS => 5}

#删除列族
hbase(main):037:0> alter 'ns1:t1', NAME => 'f1', METHOD => 'delete'
hbase(main):037:0> alter 'ns1:t1', 'delete' => 'f1'

#改变表属性
hbase(main):037:0> alter 't1', MAX_FILESIZE => '134217728'

4.3.11 drop、drop_all

删除表,匹配的表必须先被disabled

#删除某个表
hbase(main):046:0> drop 't1'
hbase(main):046:0> drop 'ns1:t1'

#删除匹配的表
hbase(main):046:0> drop_all 't.*'
hbase(main):046:0> drop_all 'ns:t.*'
hbase(main):046:0> drop_all 'ns:.*'

4.4 数据操作(DML)

4.4.1 append

在指定的table/row/column位置处追加cell

#添加cell
hbase(main):046:0> append 't1', 'r1', 'c1', 'value', ATTRIBUTES=>{'mykey'=>'myvalue'}
hbase(main):046:0> append 't1', 'r1', 'c1', 'value', {VISIBILITY=>'PRIVATE|SECRET'}

#通过表引用添加cell
hbase(main):046:0> t=get_table 't1'
hbase(main):046:0> t.append 'r1', 'c1', 'value', ATTRIBUTES=>{'mykey'=>'myvalue'}
hbase(main):046:0> t.append 'r1', 'c1', 'value', {VISIBILITY=>'PRIVATE|SECRET'}

4.4.2 count

计算表中的行数。这个操作可能需要很长时间(运行’$HADOOP_HOME/bin/hadoop jar hbase.jar rowcount’以运行计数mapreduce作业)。默认情况下,当前计数每1000行显示一次。可以选择指定计数间隔。默认情况下,对计数扫描启用扫描缓存。默认缓存大小为10行。如果您的行太小,可能需要增加此参数。

hbase(main):046:0> count 'ns1:t1'
hbase(main):046:0> count 't1'
hbase(main):046:0> count 't1', INTERVAL => 100000
hbase(main):046:0> count 't1', CACHE => 1000
hbase(main):046:0> count 't1', INTERVAL => 10, CACHE => 1000

hbase(main):046:0> t.count
hbase(main):046:0> t.count INTERVAL => 100000
hbase(main):046:0> t.count CACHE => 1000
hbase(main):046:0> t.count INTERVAL => 10, CACHE => 1000

4.4.3 get_splits

获取表所对应的Region个数。每个表在一开始只有一个region,之后记录增多后,region会被自动拆分。

hbase(main):046:0> get_splits 't1'
hbase(main):046:0> get_splits 'ns1:t1'

hbase(main):046:0> t.get_splits

4.4.4 scan

hbase(main):046:0> scan 'hbase:meta'			#全表扫描
hbase(main):046:0> scan 'hbase:meta', {COLUMNS => 'info:regioninfo'}		#扫描表info列族所有的regioninfo列
hbase(main):046:0> scan 'ns1:t1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}	#从rowkey起始行xyz扫描表c1、c2列
hbase(main):046:0> scan 't1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}	#从rowkey起始行xyz扫描表c1、c2列,返回10行
hbase(main):046:0> scan 't1', {COLUMNS => 'c1', TIMERANGE => [1303668804000, 1303668904000]}	#扫描表一段时间戳范围内的c1列
hbase(main):046:0> scan 't1', {REVERSED => true}	#全表扫描,rowkey倒序
hbase(main):046:0> scan 't1', {ALL_METRICS => true}	#全表扫描,并返回所有指标
hbase(main):046:0> scan 't1', {METRICS => ['RPC_RETRIES', 'ROWS_FILTERED']} #全表扫描,并返回指定指标
hbase(main):046:0> scan 't1', {ROWPREFIXFILTER => 'row2', FILTER => "(QualifierFilter (>=, 'binary:xyz')) AND (TimestampsFilter ( 123, 456))"}	#使用rowkey前缀过滤器过滤row2为前缀的行,使用QualifierFilter和TimestampsFilter过滤器过滤列名和时间戳
hbase(main):046:0> scan 't1', {FILTER => org.apache.hadoop.hbase.filter.ColumnPaginationFilter.new(1, 0)} #使用列分页过滤器
hbase(main):046:0> scan 't1', {CONSISTENCY => 'TIMELINE'}	#使用TIMELINE一致性
hbase(main):046:0> scan 't1', {ISOLATION_LEVEL => 'READ_UNCOMMITTED'}	#使用隔离级别READ_UNCOMMITTED
hbase(main):046:0> scan 't1', {MAX_RESULT_SIZE => 123456}	#全表扫描,多次扫描每次扫描的数据最大数
hbase(main):046:0> scan 't1', { COLUMNS => ['c1', 'c2'], ATTRIBUTES => {'mykey' => 'myvalue'}} #扫描具有指定属性的c1、c2列
hbase(main):046:0> scan 't1', { COLUMNS => ['c1', 'c2'], AUTHORIZATIONS => ['PRIVATE','SECRET']}	#权限
hbase(main):046:0> scan 't1', {COLUMNS => ['c1', 'c2'], CACHE_BLOCKS => true}	#扫描块存储,默认true
hbase(main):046:0> scan 't1', {RAW => true, VERSIONS => 10}	#扫描所有的列,包括删除的,RAW不可与COLUMNS一起使用,默认禁用
hbase(main):046:0> scan 't1', {FORMATTER => 'toString'}	#指定格式化方法
hbase(main):046:0> scan 't1', {FORMATTER_CLASS => 'org.apache.hadoop.hbase.util.Bytes', FORMATTER => 'toString'}	#默认的格式化方法和类
hbase(main):046:0> scan 't1', {COLUMNS => ['cf:qualifier1:toInt','cf:qualifier2:c(com.yt.Bytes).toInt'] }		#cf:qualifier1使用org.apache.hadoop.hbase.util.Bytes的toInt方法格式化,cf:qualifier2使用自定义的com.yt.Bytes的toInt方法格式化

4.4.5 get

hbase(main):046:0> get 'ns1:t1', 'r1'
hbase(main):046:0> get 't1', 'r1'
hbase(main):046:0> get 't1', 'r1', {TIMERANGE => [ts1, ts2]}
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1'}
hbase(main):046:0> get 't1', 'r1', {COLUMN => ['c1', 'c2', 'c3']}
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1}
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1', TIMERANGE => [ts1, ts2], VERSIONS => 4}
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS => 4}
hbase(main):046:0> get 't1', 'r1', {FILTER => "ValueFilter(=, 'binary:abc')"}
hbase(main):046:0> get 't1', 'r1', 'c1'
hbase(main):046:0> get 't1', 'r1', 'c1', 'c2'
hbase(main):046:0> get 't1', 'r1', ['c1', 'c2']
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1', ATTRIBUTES => {'mykey'=>'myvalue'}}
hbase(main):046:0> get 't1', 'r1', {COLUMN => 'c1', AUTHORIZATIONS => ['PRIVATE','SECRET']}
hbase(main):046:0> get 't1', 'r1', {CONSISTENCY => 'TIMELINE'}
hbase(main):046:0> get 't1', 'r1', {CONSISTENCY => 'TIMELINE', REGION_REPLICA_ID => 1}

4.4.6 get_counter


4.4.7 incr

增加某rowkey的某一列数据指定的值

hbase(main):046:0> incr 'ns1:t1', 'r1', 'c1'
hbase(main):046:0> incr 't1', 'r1', 'c1'
hbase(main):046:0> incr 't1', 'r1', 'c1', 1
hbase(main):046:0> incr 't1', 'r1', 'c1', 10
hbase(main):046:0> incr 't1', 'r1', 'c1', 10, {ATTRIBUTES=>{'mykey'=>'myvalue'}}
hbase(main):046:0> incr 't1', 'r1', 'c1', {ATTRIBUTES=>{'mykey'=>'myvalue'}}
hbase(main):046:0> incr 't1', 'r1', 'c1', 10, {VISIBILITY=>'PRIVATE|SECRET'}

4.4.8 put

hbase(main):016:0> put 'ns1:t1', 'r1', 'f1:c1', 'value'
hbase(main):016:0> put 't1', 'r1', 'f1:c1', 'value'
hbase(main):016:0> put 't1', 'r1', 'f1:c1', 'value', ts1
hbase(main):016:0> put 't1', 'r1', 'f1:c1', 'value', {ATTRIBUTES=>{'mykey'=>'myvalue'}}
hbase(main):016:0> put 't1', 'r1', 'f1:c1', 'value', ts1, {ATTRIBUTES=>{'mykey'=>'myvalue'}}
hbase(main):016:0> put 't1', 'r1', 'f1:c1', 'value', ts1, {VISIBILITY=>'PRIVATE|SECRET'}

4.4.9 delete、deleteall

删除某rowkey的某一列数据

hbase(main):046:0> delete 'ns1:t1', 'r1', 'c1', ts1
hbase(main):046:0> delete 't1', 'r1', 'c1', ts1
hbase(main):046:0> delete 't1', 'r1', 'c1', ts1, {VISIBILITY=>'PRIVATE|SECRET'}

删除某rowkey的全部数据或使用一个rowkey前缀删除一个rowkey区间的所有数据

Delete all cells in a given row; pass a table name, row, and optionally
a column and timestamp. Deleteall also support deleting a row range using a
row key prefix. Examples:

  hbase(main):046:0> deleteall 'ns1:t1', 'r1'
  hbase(main):046:0> deleteall 't1', 'r1'
  hbase(main):046:0> deleteall 't1', 'r1', 'c1'
  hbase(main):046:0> deleteall 't1', 'r1', 'c1', ts1
  //'' means no specific column, will delete all cells in the row which timestamp is lower than the one specified in the command
  hbase(main):046:0> deleteall 't1', 'r1', '', ts1
  hbase(main):046:0> deleteall 't1', 'r1', 'c1', ts1, {VISIBILITY=>'PRIVATE|SECRET'}

ROWPREFIXFILTER can be used to delete row ranges
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix'}
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix'}, 'c1'        //delete certain column family in the row ranges
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix'}, 'c1', ts1
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix'}, '', ts1
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix'}, 'c1', ts1, {VISIBILITY=>'PRIVATE|SECRET'}

//CACHE can be used to specify how many deletes batched to be sent to server at one time, default is 100
  hbase(main):046:0> deleteall 't1', {ROWPREFIXFILTER => 'prefix', CACHE => 100}

4.4.10 truncate、truncate_preserve

清空表,一般不推荐使用

  hbase(main):046:0> truncate 'ns1:t1'

5 Hbase架构

5.1 架构

在这里插入图片描述
1)Region Server

RegionServer是一个服务,负责多个Region的管理。其实现类为HRegionServer,
主要作用如下:
a. 对于数据的操作:get, put, delete;
b. 对于Region的操作:splitRegion、compactRegion。
c. 客户端从ZooKeeper获取RegionServer的地址,从而调用相应的服务,获取数据。

2)Master

Master是所有Region Server的管理者,其实现类为HMaster,
主要作用如下:
a. 对于表的操作:create, delete, alter,这些操作可能需要跨多个ReginServer,因此需要Master来进行协调!
b. 对于RegionServer的操作:分配regions到每个RegionServer,监控每个RegionServer的状态,负载均衡和故障转移。
c. 在一个集群中,工作状态的master只能有一个,可以有多个备用的。即使Master进程宕机,集群依然可以执行数据的读写,只是不能进行表的创建和修改等操作!当然Master也不能宕机太久,有很多必要的操作,比如创建表、修改列族配置,以及更重要的分割和合并都需要它的操作。

3)Zookeeper

RegionServer非常依赖ZooKeeper服务,ZooKeeper管理了HBase所有RegionServer的信息,包括具体的数据段存放在哪个RegionServer上。
a. 客户端每次与HBase连接,其实都是先与ZooKeeper通信,查询出哪个RegionServer需要连接,然后再连接RegionServer。Zookeeper中记录了读取数据所需要的元数据表hbase:meata,因此关闭Zookeeper后,客户端是无法实现读操作的!
b. HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作。

4)HDFS

HDFS为Hbase提供最终的底层数据存储服务,同时为HBase提供高可用的支持。

1)StoreFile

保存实际数据的物理文件,StoreFile以Hfile的形式存储在HDFS上。每个Store会有一个或多个StoreFile(HFile),数据在每个StoreFile中都是有序的。

2)MemStore

写缓存,由于HFile中的数据要求是有序的,所以数据是先存储在MemStore中,排好序后,等到达刷写时机才会刷写到HFile,每次刷写都会形成一个新的HFile。

3)WAL

由于数据要经MemStore排序后才能刷写到HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入MemStore中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
每间隔hbase.regionserver.optionallogflushinterval(默认1s), HBase会把操作从内存写入WAL。
一个RegionServer上的所有Region共享一个WAL实例。
WAL的检查间隔由hbase.regionserver.logroll.period定义,默认值为1小时。检查的内容是把当前WAL中的操作跟实际持久化到HDFS上的操作比较,看哪些操作已经被持久化了,被持久化的操作就会被移动到.oldlogs文件夹内(这个文件夹也是在HDFS上的)。一个WAL实例包含有多个WAL文件。WAL文件的最大数量通过hbase.regionserver.maxlogs(默认是32)参数来定义。

4)BlockCache

读缓存,每次查询出的数据会缓存在BlockCache中,方便下次查询。

5.2 读流程

在这里插入图片描述

在这里插入图片描述
读流程:

  1. Client先访问zookeeper的/hbase/meta-region-server节点,读取到hbase:meta表所在RegionServer
  2. 访问对应的RegionServer,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个RegionServer中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
  3. 与目标Region Server进行通讯;
  4. 初始化两种scanner(扫描器)。memstorescannner:负责扫描store的memstore区域;storefileScanner: 负责扫描store的若干storefile。
  5. 分别在BlockCache(读缓存),MemStore和Store File(HFile)中查询目标数据。如果扫了storefile,会将storefile数据所在的block(HFile数据存储单元,默认大小64k)缓存到blockcache中,blockcache是rs的读缓存,默认占用rs所在堆的40%容量,采取LRU回收策略进行缓存的回收。此后如果读取的storefile文件已经缓存在blockcache了,那么就会从blockcache中读取数据,而无需扫描磁盘读取storefile。
  6. 将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
  7. 将合并后的最终结果返回给客户端。

5.3 写流程

在这里插入图片描述

写流程:

  1. Client先访问zookeeper的/hbase/meta-region-server节点,读取到hbase:meta表所在RegionServer
  2. 访问对应的RegionServer,获取hbase:meta表,根据写请求的namespace:table/rowkey,查询出rowkey所属Region的RegionServer。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
  3. 与目标RegionServer进行通讯;
  4. 向RegionServer发put请求,尝试尽可能获取多个锁,至少需要获取一把。
  5. 如果当前数据没有设置时间戳,更新时间戳。
  6. 构建WAL对象。
  7. 将数据操作写入WAL对象的buffer中,但是并不sync到磁盘。
  8. 将数据写入对应的MemStore。
  9. 将WAL对象buffer中的数据sync到磁盘。
  10. 滚动MVCC版本号,滚动后,客户端就可以查询到数据,如果在整个写入过程中发生异常,此时,会将已经写入到MemStore中的数据回滚。
  11. 等达到MemStore的刷写时机后,将数据刷写到HFile。

5.4 MemStore Flush

将MemStore中的数据,刷写到磁盘,生成storefile的过程称为flush。flush的目的是在MemStore中对数据的rowkey进行排序,排序后刷写到磁盘后的数据是有序的,方便检索。

HBase shell手动flush命令

flush '表名'| 'region名'

5.4.1 自动Flush

在这里插入图片描述

1. 从容量上来说

  • 如果单个MemStore使用的容量超过hbase.hregion.memstore.flush.size(默认128M),其所在region的所有MemStore都会刷写。
    在刷写时,客户端依然可以向RegionServer发起写请求,写的数据依然是存储在MemStore中,但是如果MemStore使用的容量超过
    hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier(默认值4)
    此时MemStore会自动block(阻塞),阻止继续往该memstore写数据。

  • 整个RegionServer所有的MemStore使用的容量总和超过
    java_heapsize * hbase.regionserver.global.memstore.size(默认值0.4)* hbase.regionserver.global.memstore.size.lower.limit(默认值0.95)
    region会按照其所有MemStore的大小顺序(由大到小)依次进行刷写。直到RegionServer中所有MemStore的总大小减小到上述值以下。
    在刷写时,如果RegionServer所有的MemStore使用的容量总和超过
    java_heapsize * hbase.regionserver.global.memstore.size(默认值0.4)
    此时所有的MemStore也会block。

2. 从时间上来说
每间隔hbase.regionserver.optionalcacheflushinterval(默认值1h),自动flush。

3. 从WAL正在写入的日志数量上来说(已废弃,无法手动设置,最大值为32)
如果有大量的正在使用的WAL日志,说明MemStore中有大量尚未刷写的数据(MemStore已经持久过的日志会放入.oldlogs文件夹),一旦数据过多,RegionServer进程崩溃,此时恢复数据时间过长。所以,一旦正在使用的WAL日志文件的数量超过hbase.regionserver.max.logs(默认值32),此时,会根据WAL中记录的日志的先后顺序依次刷写MemStore。

5.5 StoreFile Compaction

由于Hbase依赖HDFS存储,HDFS只支持追加写。所以,当新增一个Cell的时候,HBase在HDFS上新增一条数据。当修改一个Cell的时候,HBase在HDFS又新增一条数据,只是版本号比之前那个大(或者自定义)。当删除一个Cell的时候,HBase还是新增一条数据,只是这条数据没有value,类型为DELETE,也称为墓碑标记(Tombstone)。HBase每间隔一段时间都会进行一次合并(Compaction),合并的对象为HFile文件。

目的: 对store中的多个HFile文件定期合并,消除每次Flush产生的大量的小文件。对HFile中无效的过期的数据,进行合并整理,减少数据量。

合并分为两种minor compactionmajor compaction
在这里插入图片描述

  • minor compaction:会将临近的若干个较小的HFile合并成一个较大的HFile,但不会清理过期和删除的数据。
  • major compaction:会把多个HFile合并成1个HFile,在这个过程中,一旦检测到有被打上墓碑标记的记录,在合并的过程中就忽略这条记录。这样在新产生的HFile中,就没有这条记录了,自然也就相当于被真正地删除了,由于MemStore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的HFile中,因此查询时需要遍历所有的HFile。

对于major_compact建议取消自动合并的设置,改为在集群空闲时,手动执行合并。

5.6 Region Split

默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动进行拆分。刚拆分时,两个子Region都位于当前的RegionServer,但处于负载均衡的考虑,HMaster有可能会将某个Region转移给其他的RegionServer。

Region Split时机:

  1. 0.94版本之前,当1个region中的某个Store下所有StoreFile的总大小超过hbase.hregion.max.filesize,该Region就会进行拆分。

  2. 0.94版本之后,默认使用IncreasingToUpperBoundRegionSplitPolicy策略切分region, getSizeToCheck()是检查region的大小以判断是否满足切分条件。
    在这里插入图片描述

    /**
    * initialSize默认为hbase.hregion.memstore.flush.size*2,即256M
    */
    @Override
    protected void configureForRegion(HRegion region) {
      super.configureForRegion(region);
      Configuration conf = getConf();
      //默认hbase.increasing.policy.initial.size 没有在配置文件中指定
      initialSize = conf.getLong("hbase.increasing.policy.initial.size", -1);
      if (initialSize > 0) {
        return;
      }
      // 获取用户表中自定义的memstoreFlushSize大小,默认也为128M
      TableDescriptor desc = region.getTableDescriptor();
      if (desc != null) {
        initialSize = 2 * desc.getMemStoreFlushSize();
      }
      // 判断用户指定的memstoreFlushSize是否合法,如果不合法,则hbase.hregion.memstore.flush.size=128M.
      if (initialSize <= 0) {
        initialSize = 2 * conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,
                                       TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE);
      }
    }
    

默认切分策略总结:

tableRegionsCount为0或大于100时,切分条件为10G。
tableRegionsCount在0和100之间,则为initialSize(默认为2*128) * tableRegionsCount^3,例如:
第一次split:1^3 * 256 = 256MB
第二次split:2^3 * 256 = 2048MB
第三次split:3^3 * 256 = 6912MB
第四次split:4^3 * 256 = 16384MB > 10GB,因此取较小的值10GB
后面每次split的size都是10GB了。

6 HBase优化

6.1 高可用

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

  1. 关闭HBase集群(如果没有开启则跳过此步)
    [yut@aliyun220 hbase-1.3.1]$  bin/stop-hbase.sh
    
  2. 在conf目录下创建backup-masters文件
    [yut@aliyun220 hbase-1.3.1]$ touch conf/backup-masters
    
  3. 在backup-masters文件中配置高可用HMaster节点
    [yut@aliyun220 hbase-1.3.1]$ echo aliyun221 > conf/backup-masters
    
  4. 将整个conf目录scp到其他节点
    [yut@aliyun220 hbase-1.3.1]$ scp -r conf/ aliyun221:/data1/module/hbase-1.3.1/
    [yut@aliyun220 hbase-1.3.1]$ scp -r conf/ aliyun222:/data1/module/hbase-1.3.1/
    

6.2 预分区

每一个region维护着rowkey的StartKey与EndKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。

  1. 手动设定预分区
    hbase(main):001:0> create 'staff1','info','partition1',SPLITS => ['1000','2000','3000','4000']
    
  2. 生成16进制序列预分区(在插入一条数据时,rowkey必须先采取HexString,转为16进制,再插入到表中)
    hbase(main):001:0> create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
    
  3. 按照文件中设置的规则预分区
    1). 创建splits.txt文件内容如下:
    aaaa
    bbbb
    cccc
    dddd
    2).执行
    hbase(main):001:0> create 'staff3','partition3',SPLITS_FILE => 'splits.txt'
    
  4. 使用JavaAPI创建预分区
    //自定义算法,产生一系列Hash散列值存储在二维数组中
    byte[][] splitKeys = 某个散列值函数
    //创建HBaseAdmin实例
    HBaseAdmin hAdmin = new HBaseAdmin(HBaseConfiguration.create());
    //创建HTableDescriptor实例
    HTableDescriptor tableDesc = new HTableDescriptor(tableName);
    //通过HTableDescriptor实例和散列值二维数组创建带有预分区的HBase表
    hAdmin.createTable(tableDesc, splitKeys);
    

6.3 RowKey设计

一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪一个预分区的区间内,设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。

rowkey设计原则:

  1. 长度原则:rowkey是一个二进制码流,可以为任意字符串,最大长度为64kb,实际应用中一般为10-100bytes,它以byte[]形式保存,一般设定成定长。一般越短越好,不要超过16个字节,主要原因如下:
    a. 目前操作系统都是64位系统,内存8字节对齐,控制在16字节,8字节的整数倍利用了操作系统的最佳特性。
    b. hbase将部分数据加载到内存当中,如果rowkey过长,内存的有效利用率就会下降。
  2. 散列原则:如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位字节采用散列字段处理,由程序随即生成。低位放时间字段,这样将提高数据均衡分布,各个RegionServer负载均衡的几率。如果不进行散列处理,首字段直接使用时间信息,所有该时段的数据都将集中到一个RegionServer当中,这样当检索数据时,负载会集中到个别RegionServer上,造成热点问题,会降低查询效率。
  3. 唯一性原则:必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。但是这里的量不能太大,如果太大需要拆分到多个节点上去。

所以良好的rowkey设计,应当遵循三大原则,既让相同业务的数据具有一定的集中性,便于提高查询效率,也要具有散列性避免热点问题。

以下是一些rowkey常用的设计方案:

  1. 生成随机数、hash、散列值

原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
原本rowKey为2001的,SHA1后变成:9195f873d1715b7575f88118db6dc42a91137874
原本rowKey为3001的,SHA1后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowKey来Hash后作为每个分区的临界值。

  1. 字符串反转

20210404000001转成10000040401202
20210404000002转成20000040401202

  1. 字符串拼接

20210404000001_aa12
20210404000001_bb10

  1. salting(加盐)

1001_20210404000001
1002_20210404000002

6.4 内存优化

HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。

conf/hbase-env.sh中,编写RegionServer进程启动时的JVM参数:

  • -Xms : JVM堆的起始值
  • -Xmx : JVM堆的最大值

或配置环境启动

export HBASE_HEAPSIZE=1G 或
export HBASE_OPTS="-XX:+UseConcMarkSweepGC"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值