Hbase的基本简介及安装、配置、使用(一)

KinglyJn      2017-08-26

Hbase简介

Hbase是一个构建在HDFS之上的、高可靠、高性能、面向列、可伸缩、实时读写的开源分布式数据库,是google的bigtable的开源实现,适用于海量数据的存储和实时查询,是hadoop生态系统中重要的一员。

以下是hbase处理数据的物理架构图:


1. master-slave主从架构。
2. table从竖直方向进行切割,分成若干个区域region,由各个区域服务器regionserver进行管理。
3. region
   a.一个region就相当于大表的一个分片
   b.region存储机制包含两部分,即内存存储(MemStore)和文件存储(StoreFile)
4. masterserver
   a.负责指派resion给regionserver,通过zk获得task帮助
   b.处理跨regionserver的的负载均衡问题
   c.从繁忙服务器到空闲服务器之间的数据转载
   d.通过负载均衡来判断集群的状态
5. regionserver
   a.和client通信
   b.处理数据操作 和 它下面的所有region的读写请求
   c.通过阈值决定region size


hbase的优点:

  • 成熟:社区成熟、理论充分经过实践、丰富的工具支持
  • 高效:高并发写入、均衡效果好、null数据的列不占存储空间
  • 分布式:基于hdfs(数据安全、普通商用服务器廉价、易扩展)、zookeeper
  • 表的特点:
    • 大:存储量大,一张表可以有数百亿行、上百万列;检索速度快,准实时,[毫]秒级别;
    • 数据安全:基于hdfs存储,一条记录至少有3个备份
    • 面向列存储:面向列(族)的存储和权限访问,列(族)独立索引;
    • 稀疏:对于为空的列,并不占用存储空间,因此表可以设计的非常稀疏;
    • 数据类型单一:hbase中数据类型都是字符串类型(string);
    • 无模式:表只定义列族,而不定义列,这样数据存储更加灵活(结构化和半结构化的数据)


hbase数据存储的逻辑模型和物理模型

数据存储的逻辑模型

0. 存储方式上,面向行和面向列的设计方式,主要的考虑因素是磁盘的寻址效率问题。
1. 面向列族的数据库(面向行适合小量数据在线面向事务处理(OLTP),适合巨量数据在线分析处理(OLAP))
2. table中只定义了列族,并按照row存储,rowkey决定按照字典排序、且不超过64k字节数据。
3. 术语
	table:表,是row的集合
	row:行,是列族的集合
	column family:列族,是列的集合
	column:列,是kv对的集合
4. Hbase每个列都归属于列族,列族必须作为表模式(schema)定义的一部分预先给出。如 create 't1','cf1'。
   权限控制、存储以及调优都是在列族层面上进行的。
   Hbase把同一列族里面的数据存储在同一个目录下,通常由多个文件保存。

主键(Row Key):用来检索记录的主键,访问Hbase表中的行,只有三种方式,即通过单个Row key访问、通过Row Key的range 和 全表扫描。主键为任意字符串,最大长度为64kb,按照字典顺序存储,在hbase内部保存为字节数组。

列族(Column Family):列族在创建表的时候声明,它是一些列的集合,一个列族的所有的列都有相同的前缀,如courses:hisotry和courses:month都是列族courses的成员,冒号(:)是列族的分隔符,用来区分前缀和列名。一个列族可以包含多个列。列中的数据都是以二进制的形式存在,没有数据类型。

时间戳和存储单元(Timestamp and Cell):Hbase中通过row和columns确定的为一个存储单元称为Cell。每个cell保存着一份数据的多个版本。在写入数据的时候,时间戳可以由hbase自动赋值(当前系统时间精确到毫秒),也可以显式赋值。


数据存储的物理模型

  1. table中的所有行都按照rowkey的字典序进行排列;

  2. table在行的方向上分割为多个region;

  3. region按大小进行分割,每张表开始只有一个region,随着数据的增多,region不断增大,当增大到一定阈值的时候,region就会分裂为两个新的region,随后会有越来越多的region;

    开始创建表
    --
    ...      分裂
    --    --------->   --          分裂        
                       ...     ----------->   ...
                       300001
                 
                 
                       300002
                       ...
                       --
    
  4. Region是hbase中分布式存储和负载均衡的最小单元,不同的region分布到不同的regionserver上;

  5. Region([startkey, stopkey))虽是分布式存储的最小单元,但并不是存储的最小单元。

    region由一个或者多个store,每个store保存一个columns family。

    每个store又由一个memStore和0个或多个storeFile组成。

    memStore存储在内存中,storeFile存储在HDFS上。

    hbase数据写入的流程:

    put --> cell
    	0) 将操作记录写入HDFS上的WAL预写日志
    	1) 将数据先写入内存memstore中,一直到memstore满
    	2) -->flush成一个storefile,直至storefile增长到一定的阈值
    	3) -->触发compact合并操作,多个storefile合并成一个storefile,同时进行版本合并和数据删除
    	4) -->compact动作使得storefile越来越大,单个的storefile超过一定的阈值之后,触发split操作
    	5) -->split操作将当前region split成两个region,旧region会下线,
    	      新的两个孩子region会被hmaster分配到相应的hregionserver上,使得原先一个region的压力
    	      分流到两个region上
       	     
    注意:hbase只是增加数据,所有更新和删除操作都是在compact阶段做的,所以用户写操作只需要进入内存即可立即返回,从而保证IO的高性能。
    


搭建hbase本地模式

参考

# 1.下载
wget http://archive.apache.org/dist/hbase/1.2.3/hbase-1.2.3-bin.tar.gz
# 2.tar开、配置环境变量
tar -zxvf hbase-1.2.3-bin.tar.gz -C xxx/xxx
sudo vim /etc/profile
# 3.conf/hbase-env.sh:
export JAVA_HOME=/opt/modules/jdk1.8.0_144
export HBASE_MANAGES_ZK=true   #HBase是否管理它自己的ZooKeeper的实例

# 4.conf/hbase.conf
# 参考 lib/hbase-common-x.x.x.jar!/hbase-default.xml
<property>
    <name>hbase.rootdir</name>
    <value>file:///opt/module/data/hbasedata/hbase</value>
    <description>默认值为:${hbase.tmp.dir}/hbase</description>
</property>
<!--
<property>
    <name>hbase.tmp.dir</name>
    <value>${java.io.tmpdir}/hbase-${user.name}</value>
    <description>通常情况下,目录的结构为 /tmp/hbase-ubuntu,可以自定义</description>
</property>
<property>
    <name>hbase.zookeeper.property.dataDir</name>
    <value>${hbase.tmp.dir}/zookeeper</value>
    <description>通常不配置,默认为${hbase.tmp.dir}/zookeeper</description>
</property>
-->

# 5. 快捷启动hbase
bin/start-hbase.sh
或
bin/hbase-daemon.sh start master
bin/hbase-daemon.sh start regionserver
# 6. 查看hbase相关进程
2424 Jps
2173 HMaster
# 7. web查看hbase集群
curl http://nimbusz:60010/master-status
# 8. 关闭hbase
bin/stop-hbase.sh
或
bin/hbase-daemon.sh stop regionserver
bin/hbase-daemon.sh stop master


搭建hbase分布式环境

参考

# 1. 关闭防火墙(卸载防火墙apt-get remove iptables),关闭selinux
ufw disable

# 2.1 下载解压(或者编译源码)、删除docs文件(没用,而且占空间,故删除)
# 可以参考 http://archive.cloudera.com/cdh5/cdh/5/ 来确定cdh生态对应的版本
# 这里我选用1.2.3版本
wget http://archive.apache.org/dist/hbase/hbase-0.98.6/hbase-0.98.6-hadoop2-bin.tar.gz
或者
wget http://archive.apache.org/dist/hbase/1.2.3/hbase-1.2.3-bin.tar.gz
rm -rf docs

# 2.2 配置HBASE_HOME(可以省略)
sudo vim /etc/profile
	export HBASE_HOME=/opt/module/hbase-x.x.x
	export PATH=$PATH:...:$HBASE_HOME/bin
source /etc/profile

# 3. 官方默认的是以hadoop2.2.0编译的,这里我们需要手动替换habsehome/lib文件夹中hadoop相关的jar成我们需要的hadoop的版本(2.5.0)
需要替换的jar包如下:
hadoop-annotations-2.2.0.jar
hadoop-auth-2.2.0.jar
hadoop-client-2.2.0.jar
hadoop-common-2.2.0.jar
hadoop-hdfs-2.2.0.jar
hadoop-mapreduce-client-app-2.2.0.jar
hadoop-mapreduce-client-common-2.2.0.jar
hadoop-mapreduce-client-core-2.2.0.jar
hadoop-mapreduce-client-jobclient-2.2.0.jar
hadoop-mapreduce-client-shuffle-2.2.0.jar
hadoop-yarn-api-2.2.0.jar
hadoop-yarn-client-2.2.0.jar
hadoop-yarn-common-2.2.0.jar
hadoop-yarn-server-common-2.2.0.jar
hadoop-yarn-server-nodemanager-2.2.0.jar

# 4. 修改HBase的配置文件 conf/hbase-env.sh 修改内容如下:
export JAVA_HOME=/opt/modules/jdk1.8.0_144
export HBASE_MANAGES_ZK=false   #HBase是否管理它自己的ZooKeeper的实例

# 5. 配置文件
# 5.1 conf/hbase-site.xml
# 参考 lib/hbase-common-x.x.x.jar!/hbase-default.xml
# [注] 如果在 HDFS HA 上部署HBase需要做的更改如下:
# 在hbase-site.xml中,rootdir改为和hadoop的dfs.nameservices一样(e.g. hdfs://ns1/hbase)
# 将hdfs的配置文件core-site.xml和hdfs-site.xml拷贝到hbase的conf目录下,然后重启hbase
-------------------------
<property>
	<!--指定是完全分布式环境还是伪分布式环境-->
  	<name>hbase.cluster.distributed</name>
  	<value>true</value>
</property>
<property>
  	<name>hbase.rootdir</name>
  	<value>hdfs://nimbusz:8020/hbase</value>
</property>
<property>
  	<name>hbase.zookeeper.quorum</name>
  	<value>nimbusz,supervisor01z,supervisor02z</value>
</property>
<property>
  	<name>hbase.zookeeper.property.clientPort</name>
  	<value>2181</value>
</property>
<!--这个值默认是60s,这意味着一个serve宕机了,master至少需要60s才能察觉到宕机,并开始恢复。
在调节这个参数之前,你需要确认你的JVM的GC参数,否则一个很长的GC操作就可能导致超时。建议开始的时候,
将这个值调的高一点,因为每当我们在执行一个大规模数据导入的时候regionserver死掉,通常这样的问题是由于长时间的GC造成的-->
<property>
	<name>zookeeper.session.timeout</name>
	<value>60000</value>
</property>
<!--这个参数决定了处理用户请求的线程数量,默认是30-->
<property>
	<name>hbase.regionserver.handler.count</name>
	<value>30</value>
</property>


# 5.2 conf/regionservers
------------------------
nimbusz
supervisor01z
supervisor02z

# 5.3 分发hbase及其配置文件 到各个节点
scp -r /opt/module/hbase-1.2.3 ubuntu@supervisor01z:/opt/module/hbase-1.2.3
scp -r /opt/module/hbase-1.2.3 ubuntu@supervisor02z:/opt/module/hbase-1.2.3

# 6. 在master节点(使用nimbusz主机充当master节点)快捷启动hbase
bin/start-hbase.sh
或
bin/hbase-daemon.sh start master
bin/hbase-daemon.sh start regionserver

# 7. 查看hbase相关进程
35216 HMaster
11009 QuorumPeerMain
35362 HRegionServer

# 8. web查看hbase集群
# web ui端口参数为:hbase.master.info.port
curl http://nimbusz:60010/master-status	#1.0版本之前
curl http://nimbusz:16010/master-status #1.0版本之后

# 9. 关闭hbase
bin/stop-hbase.sh
或
bin/hbase-daemon.sh stop regionserver
bin/hbase-daemon.sh stop master

注:Apache提供的hadoop本地库是32位的,而在64位的服务器上就会有问题,因此需要自己编译64位的版本。

hbase(main):001:0> get 'user', '1002'
2017-08-28 11:28:47,988 WARN  [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable


hbase master HA

Hbase的master节点也可以做高可用配置,设置master的一个或者多个备份节点(backup master)。

只需要在其他hbase的节点上开启master进程即可:

$ hbase-daemon.sh start master

查看web ui(可以在nimbusz的web ui界面开到hbase集群现在有两个 backup master):

$ curl http://nimbusz:16010
$ curl http://supervisor01z:16010   #备份节点
$ curl http://supervisor02z:16010   #备份节点
#也可以在zookeeper的/hbase/master 和 /hbase/backup-masters 节点查看主节点和备份节点信息

当nimbusz的master宕机之后,zookeeper会从 [supervisor01z, supervisor02z] 按【字典顺序】选举出一个节点作为master主节点。


hbase.rootdir 对应HDFS文件简介


Hbase shell

# 查看hbase脚本提供的一些功能
$ bin/hbase
Usage: hbase [<options>] <command> [<args>]
Options:
  --config DIR    Configuration direction to use. Default: ./conf
  --hosts HOSTS   Override the list in 'regionservers' file
Commands:
Some commands take arguments. Pass no args or -h for usage.
  shell           Run the HBase shell
  hbck            Run the hbase 'fsck' tool
  hlog            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
  version         Print the version
  CLASSNAME       Run the class named CLASSNAME
  
  
# 使用hbase shell
$ bin/habse shell

# 使用help查看帮助文档
hbase(main):001:0> help
hbase(main):001:0> help "list"


Hbase shell 常见命令

-- 查看数据库中有哪些表
list

-- 创建表(单引号和双引号都可以)
create 'user', 'info'

-- 查看表的描述信息
describe 'user'

-- 插入数据
put 'user', '1001', 'info:name', 'zhangsan'
put 'user', '1001', 'info:age', '25'
put 'user', '1001', 'info:gender', '1'
put 'user', '1001', 'info:address', 'beijing'


-- 将memStore中的数据刷到storeFile中(一个)
flush 'TABLENAME'
flush 'REGIONNAME'
flush 'ENCODED_REGIONNAME'

-- 将store数据进行合并(一个个小的storeFile可能会变成大一个打的storeFile)
compact 'user'


-- 查询表的数据
-- 在hbase中的查询只有三种方式
-- 1. 依据rowkey进行查询,最快
get 'user', '1001'
get 'user', '1001', 'info:name'

-- 2. 范围查询,用的最多
scan 'user', {COLUMNS => ['info:name','info:age'], LIMIT => 2, STARTROW => '1001'}

-- 3. 全表扫描,最慢
scan 'user'


-- 删除表的数据
-- 1. 删除某条数据的一列
delete 'user', '1001', 'info:age'

-- 2. 删除某条数据的全部列
deleteadll 'user', '1001'


hbase检索和操作数据的过程

定位的过程

  1. user表的数据存储在很多region上,而region的元数据信息(包括每个region被哪一个regionserver管理)存储在hbase:meta表中。
  2. 客户端首先会根据配置文件中zookeeper地址连接zookeeper,并读取//meta-region-server节点信息,该节点信息存储HBase元数据(hbase:meta)表所在的RegionServer地址以及访问端口等信息。用户可以通过zookeeper命令(get //meta-region-server)查看该节点信息。
  3. 根据hbase:meta所在RegionServer的访问信息,客户端会将该元数据表加载到本地并进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息。
  4. 根据数据所在RegionServer的访问信息,客户端会向该RegionServer发送真正的数据读取请求。服务器端接收到该请求之后需要进行复杂的处理,后面做介绍。

通过上述对客户端以及HBase系统的交互分析,可以基本明确两点:

  • 客户端只需要配置zookeeper的访问地址以及根目录,就可以进行正常的读写请求。不需要配置集群的RegionServer地址列表。
  • 客户端会将hbase:meta元数据表缓存在本地,因此上述步骤中前两步只会在客户端第一次请求的时候发生,之后所有请求都直接从缓存中加载元数据。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常,此时客户端需要重新加载一份最新的元数据表到本地。RegionServer接收到客户端的get/scan请求之后,先后做了两件事情:构建scanner体系(实际上就是做一些scan前的准备工作),在此体系基础上一行一行检索。举个不太合适但易于理解的例子,scan数据就和开发商盖房一样,也是分成两步:组建施工队体系,明确每个工人的职责;一层一层盖楼。

“盖楼”的过程

构建scanner体系-组建施工队

scanner体系的核心在于三层scanner:RegionScanner、StoreScanner以及StoreFileScanner。三者是层级的关系,一个RegionScanner由多个StoreScanner构成,一张表由多个列族组成,就有多少个StoreScanner负责该列族的数据扫描。一个StoreScanner又是由多个StoreFileScanner组成。每个Store的数据由内存中的MemStore和磁盘上的StoreFile文件组成,相对应的,StoreScanner对象会雇佣一个MemStoreScanner和N个StoreFileScanner来进行实际的数据读取,每个StoreFile文件对应一个StoreFileScanner,注意:StoreFileScanner和MemstoreScanner是整个scan的最终执行者。

对应于建楼项目,一栋楼通常由好几个单元楼构成(每个单元楼对应于一个Store),每个单元楼会请一个监工(StoreScanner)负责该单元楼的建造。而监工一般不做具体的事情,他负责招募很多工人(StoreFileScanner),这些工人才是建楼的主体。下图是整个构建流程图:

  1. RegionScanner会根据列族构建StoreScanner,有多少列族就构建多少StoreScanner,用于负责该列族的数据检索

    1. 构建StoreFileScanner:每个StoreScanner会为当前该Store中每个HFile构造一个StoreFileScanner,用于实际执行对应文件的检索。同时会为对应Memstore构造一个MemstoreScanner,用于执行该Store中Memstore的数据检索。该步骤对应于监工在人才市场招募建楼所需的各种类型工匠。

    2. 过滤淘汰StoreFileScanner:根据Time Range以及RowKey Range对StoreFileScanner以及MemstoreScanner进行过滤,淘汰肯定不存在待检索结果的Scanner。上图中StoreFile3因为检查RowKeyRange不存在待检索Rowkey所以被淘汰。该步骤针对具体的建楼方案,裁撤掉部分不需要的工匠,比如这栋楼不需要地暖安装,对应的工匠就可以撤掉。

    3. Seek rowkey:所有StoreFileScanner开始做准备工作,在负责的HFile中定位到满足条件的起始Row。工匠也开始准备自己的建造工具,建造材料,找到自己的工作地点,等待一声命下。就像所有重要项目的准备工作都很核心一样,Seek过程(此处略过Lazy Seek优化)也是一个很核心的步骤,它主要包含下面三步:

      1. 定位Block Offset:在Blockcache中读取该HFile的索引树结构,根据索引树检索对应RowKey所在的Block Offset和Block Size
      2. Load Block:根据BlockOffset首先在BlockCache中查找Data Block,如果不在缓存,再在HFile中加载
      3. Seek Key:在Data Block内部通过二分查找的方式定位具体的RowKey 整体流程细节参见《HBase原理-探索HFile索引机制》,文中详细说明了HFile索引结构以及如何通过索引结构定位具体的Block以及RowKey
    4. StoreFileScanner合并构建最小堆:将该Store中所有StoreFileScanner和MemstoreScanner合并形成一个heap(最小堆),所谓heap是一个优先级队列,队列中元素是所有scanner,排序规则按照scanner seek到的keyvalue大小由小到大进行排序。这里需要重点关注三个问题,首先为什么这些Scanner需要由小到大排序,其次keyvalue是什么样的结构,最后,keyvalue谁大谁小是如何确定的。

      为什么这些Scanner需要由小到大排序?最直接的解释是scan的结果需要由小到大输出给用户,当然,这并不全面,最合理的解释是只有由小到大排序才能使得scan效率最高。举个简单的例子,HBase支持数据多版本,假设用户只想获取最新版本,那只需要将这些数据由最新到最旧进行排序,然后取队首元素返回就可以。那么,如果不排序,就只能遍历所有元素,查看符不符合用户查询条件。这就是排队的意义。

      工匠们也需要排序,先做地板的排前面,做墙体的次之,最后是做门窗户的。做墙体的内部还需要再排序,做内墙的排前面,做外墙的排后面,这样,假如设计师临时决定不做外墙的话,就可以直接跳过外墙部分工作。很显然,如果不排序的话,是没办法临时做决定的,因为这部分工作已经可能做掉了。

      HBase中KeyValue是什么样的结构?HBase中KeyValue并不是简单的KV数据对,而是一个具有复杂元素的结构体,其中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等多部分组成,Value是一个简单的二进制数据。Key中元素KeyType表示该KeyValue的类型,取值分别为Put/Delete/Delete Column/Delete Family等。KeyValue可以表示为如下图所示:

      了解了KeyValue的逻辑结构后,我们不妨再进一步从原理的角度想想HBase的开发者们为什么如此对其设计。这个就得从HBase所支持的数据操作说起了,HBase支持四种主要的数据操作,分别是Get/Scan/Put/Delete,其中Get和Scan代表数据查询,Put操作代表数据插入或更新(如果Put的RowKey不存在则为插入操作、否则为更新操作),特别需要注意的是HBase中更新操作并不是直接覆盖修改原数据,而是生成新的数据,新数据和原数据具有不同的版本(时间戳);Delete操作执行数据删除,和数据更新操作相同,HBase执行数据删除并不会马上将数据从数据库中永久删除,而只是生成一条删除记录,最后在系统执行文件合并的时候再统一删除。

      HBase中更新删除操作并不直接操作原数据,而是生成一个新纪录,那问题来了,如何知道一条记录到底是插入操作还是更新操作亦或是删除操作呢?这正是KeyType和Timestamp的用武之地。上文中提到KeyType取值为分别为Put/Delete/Delete Column/Delete Family四种,如果KeyType取值为Put,表示该条记录为插入或者更新操作,而无论是插入或者更新,都可以使用版本号(Timestamp)对记录进行选择;如果KeyType为Delete,表示该条记录为整行删除操作;相应的KeyType为Delete Column和Delete Family分别表示删除某行某列以及某行某列族操作;

      不同KeyValue之间如何进行大小比较? 上文提到KeyValue中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等5部分组成,HBase设定Key大小首先比较RowKey,RowKey越小Key就越小;RowKey如果相同就看CF,CF越小Key越小;CF如果相同看Qualifier,Qualifier越小Key越小;Qualifier如果相同再看Timestamp,Timestamp越大表示时间越新,对应的Key越小。如果Timestamp还相同,就看KeyType,KeyType按照DeleteFamily -> DeleteColumn -> Delete -> Put 顺序依次对应的Key越来越大。

  2. StoreScanner合并构建最小堆:上文讨论的是一个监工如何构建自己的工匠师团队以及工匠师如何做准备工作、排序工作。实际上,监工也需要进行排序,比如一单元的监工排前面,二单元的监工排之后… StoreScanner一样,列族小的StoreScanner排前面,列族大的StoreScanner排后面。

    scan查询-层层建楼:构建Scanner体系是为了更好地执行scan查询,就像组建工匠师团队就是为了盖房子一样。scan查询总是一行一行查询的,先查第一行的所有数据,再查第二行的所有数据,但每一行的查询流程却没有什么本质区别。盖房子也一样,无论是盖8层还是盖18层,都需要一层一层往上盖,而且每一层的盖法并没有什么区别。所以实际上我们只需要关注其中一行数据是如何查询的就可以。

    对于一行数据的查询,又可以分解为多个列族的查询,比如RowKey=row1的一行数据查询,首先查询列族1上该行的数据集合,再查询列族2里该行的数据集合。同样是盖第一层房子,先盖一单元的一层,再改二单元的一层,盖完之后才算一层盖完,接着开始盖第二层。所以我们也只需要关注某一行某个列族的数据是如何查询的就可以。

    还记得Scanner体系构建的最终结果是一个由StoreFileScanner和MemstoreScanner组成的heap(最小堆)么,这里就派上用场了。下图是一张表的逻辑视图,该表有两个列族cf1和cf2(我们只关注cf1),cf1只有一个列name,表中有5行数据,其中每个cell基本都有多个版本。cf1的数据假如实际存储在三个区域,memstore中有r2和r4的最新数据,hfile1中是最早的数据。现在需要查询RowKey=r2的数据,按照上文的理论对应的Scanner指向就如图所示:

    这三个Scanner组成的heap为<MemstoreScanner,StoreFileScanner2, StoreFileScanner1>,Scanner由小到大排列。查询的时候首先pop出heap的堆顶元素,即MemstoreScanner,得到keyvalue = r2:cf1:name:v3:name23的数据,拿到这个keyvalue之后,需要进行如下判定:

    检查该KeyValue的KeyType是否是Deleted/DeletedCol等,如果是就直接忽略该列所有其他版本,跳到下列(列族) 检查该KeyValue的Timestamp是否在用户设定的Timestamp Range范围,如果不在该范围,忽略 检查该KeyValue是否满足用户设置的各种filter过滤器,如果不满足,忽略 检查该KeyValue是否满足用户查询中设定的版本数,比如用户只查询最新版本,则忽略该cell的其他版本;反正如果用户查询所有版本,则还需要查询该cell的其他版本。

    现在假设用户查询所有版本而且该keyvalue检查通过,此时当前的堆顶元素需要执行next方法去检索下一个值,并重新组织最小堆。即图中MemstoreScanner将会指向r4,重新组织最小堆之后最小堆将会变为<StoreFileScanner2, StoreFileScanner1, MemstoreScanner>,堆顶元素变为StoreFileScanner2,得到keyvalue=r2:cf1:name:v2:name22,进行一系列判定,再next,再重新组织最小堆…

    不断重复这个过程,直至一行数据全部被检索得到。继续下一行…


Hbase切割风暴

#切割风暴
#hbase默认自动切割达到一定阈值大小的表,由于hbase的负载均衡机制,所有regionserver的管理的所有region容易同时达到这个阈值,从而会引起切割风暴,导致hbase性能下降。一般为避免Hbase的切割风暴,生产环境会将切割阈值设置的非常大(不让hbase自动切割),由运维人员手动完成大表的切割工作 或 合并工作。

masterserver
----------------------------------------------------------
regionserver01        regionserver02        regionserver03
----------------------------------------------------------
region01 阈值          region06              region09 阈值
region04              region03 阈值          region07
resion02              region05              region08  阈值


Hbase自带压测工具

命令:

$ hbase org.apache.Hadoop.hbase.PerformanceEvaluation sequentialWrite 1
或者
$ hbase pe

命令形式:
Java org.apache.hadoop.hbase.PerformanceEvaluation <OPTIONS> [-D<property=value>]* <command> <nclients>

Options:
nomapred        Run multiple clients using threads (rather than use mapreduce)
rows            Rows each client runs. Default: One million
size            Total size in GiB. Mutually exclusive with --rows. Default: 1.0.
sampleRate      Execute test on a sample of total rows. Only supported by randomRead. Default: 1.0
traceRate       Enable HTrace spans. Initiate tracing every N rows. Default: 0
table           Alternate table name. Default: 'TestTable'
multiGet        If >0, when doing RandomRead, perform multiple gets instead of single gets. Default: 0
compress        Compression type to use (GZ, LZO, ...). Default: 'NONE'
flushCommits    Used to determine if the test should flush the table. Default: false
writeToWAL      Set writeToWAL on puts. Default: True
autoFlush       Set autoFlush on htable. Default: False
oneCon          all the threads share the same connection. Default: False
presplit        Create presplit table. Recommended for accurate perf analysis (see guide).  Default: disabled
inmemory        Tries to keep the HFiles of the CF inmemory as far as possible. Not guaranteed that reads are always served from memory.  Default: false
usetags         Writes tags along with KVs. Use with HFile V3. Default: false
numoftags       Specify the no of tags that would be needed. This works only if usetags is true.
filterAll       Helps to filter out all the rows on the server side there by not returning any thing back to the client.  Helps to check the server side performance.  Uses FilterAllFilter internally. 
latency         Set to report operation latencies. Default: False
bloomFilter      Bloom filter type, one of [NONE, ROW, ROWCOL
valueSize       Pass value size to use: Default: 1024
valueRandom     Set if we should vary value size between 0 and 'valueSize'; set on read for stats on size:Default: Not set.
valueZipf       Set if we should vary value size between 0 and 'valueSize' in zipf form: Default: Not set.
period          Report every 'period' rows: Default: opts.perClientRunRows / 10
multiGet        Batch gets together into groups of N. Only supported by randomRead. Default: disabled
addColumns      Adds columns to scans/gets explicitly. Default: true
replicas        Enable region replica testing. Defaults: 1.
splitPolicy     Specify a custom RegionSplitPolicy for the table.
randomSleep     Do a random sleep before each get between 0 and entered value. Defaults: 0
columns         Columns to write per row. Default: 1
caching         Scan caching to use. Default: 30
Note: -D properties will be applied to the conf used. 
  For example: 
   -Dmapreduce.output.fileoutputformat.compress=true
   -Dmapreduce.task.timeout=60000

Command:
filterScan      Run scan test using a filter to find a specific row based on it's 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

默认pe是使用MapReduce作业来执行的。其中参数:nClients可以当做创建多少线程执行测试.

顺序写命令: hbase pe sequentialWrite 1 :

结果:
HBase Performance Evaluation
  Elapsed time in milliseconds=171522
  Row count=1048570

说明: 
命令行创建一个单独客户端,并且执行持续的写入测试。命令行将一直显示完成的进度直到打印最后的结果,当用户确定客户端服务器负载并不大时,可增加一定数量的客户端(也就是说线程或者MapReduce任务)
hbase pe sequentialWrite 4 :  
	HBase Performance Evaluation
      Elapsed time in milliseconds=232334
      Row count=1048560

随机写命令:hbase pe randomWrite 1

结果:  
HBase Performance Evaluation
      Elapsed time in milliseconds=163566
      Row count=1048570

顺序读命令:hbase pe sequentialRead 1

结果:  
HBase Performance Evaluation
	Elapsed time in milliseconds=493891
	Row count=1048570

随机读命令:hbase pe randomRead 1

HBase Performance Evaluation
      Elapsed time in milliseconds=586889
      Row count=1048570


测试环境使用hbase自带压测工具的一些测试数据

集群配置(3个slave)
	CPU: 12core
	Mem: 64G
	HD: 普通商用机械硬盘 2T
	
测试数据:
顺序写	hbase pe sequentialWrite nClients
1:5000/s	2:3000/s	3:1700/s	4:1440/s	5:1140/s	6:850/s		10:670/s	20:1060/s	50:1160/s

随机写	hbase pe randomWrite nClients
1:6000/s	2:2800/s	3:1800/s	4:1700/s	5:1300/s	6:870/s		10:700/s
20:1340/s	50:1280/s

顺序读 hbase pe sequentialRead nClients
1:3800/s	2:3100/s	3:2760/s	4:1900/s	5:2010/s	6:1702/s	10:1090/s
20:1330/s	50:780/s

随机读 hbase pe randomRead nClients
1:3180/s	2:2850/s	3:2480/s	4:2130/s	5:1740/s	6:1540/s	10:840/s
20:930/s	50:722/s


Tags:


Share: