核心概念
Client:是执行查询、写入等对HBase表数据进行增删改查的使用方,可以是使用HBase Client API编写的程序,也可以是其他开发好的HBase客户端应用。
Zookeeper:HBase使用Zookeeper作为集群协调与管理系统。在HBase中其主要的功能与职责为:
-
存储整个集群HMaster与RegionServer的运行状态
-
实现HMaster的故障恢复与自动切换
-
为Client提供元数据表的存储信息
HMaster、RegionServer启动之后将会在Zookeeper上注册并创建节点(/hbasae/master 与 /hbase/rs/*),同时 Zookeeper 通过Heartbeat的心跳机制来维护与监控节点状态,一旦节点丢失心跳,则认为该节点宕机或者下线,将清除该节点在Zookeeper中的注册信息。
当Zookeeper中任一RegionServer节点状态发生变化时,HMaster都会收到通知,并作出相应处理,例如RegionServer宕机,HMaster重新分配Regions至其他RegionServer以保证集群整体可用性。
当HMaster宕机时(Zookeeper监测到心跳超时),Zookeeper中的 /hbasae/master 节点将会消失,同时Zookeeper通知其他备用HMaster节点,重新创建 /hbasae/master 并转化为active master。
除了作为集群中的协调者,Zookeeper还为Client提供了 hbase:meta 表的存储信息。
客户端要访问HBase中的数据,只需要知道Zookeeper集群的连接信息,访问步骤如下:
-
客户端将从Zookeeper(/hbase/meta-region-server)获得 hbase:meta 表存储在哪个RegionServer,缓存该位置信息。
-
查询该RegionServer上的 hbase:meta 表数据,查找要操作的 rowkey所在的Region存储在哪个RegionServer中,缓存该位置信息。
-
在具体的RegionServer上根据rowkey检索该Region数据。
客户端操作数据过程并不需要HMaster的参与,通过Zookeeper间接访问RegionServer来操作数据。第一次请求将会产生3次RPC,之后使用相同的rowkey时客户端将直接使用缓存下来的位置信息,直接访问RegionServer,直至缓存失效(Region失效、迁移等原因)。
hbase:meta表:存储了集群中所有Region的位置信息,从zk路径(/hbase/meta-region-server)获取meta表region,然后读取meta表获取用户数据信息。(旧版本 hbase:-ROOT- 和 hbase:.meta.表:读取流程 client–>Zookeeper(/hbase/root-region-server 获取-ROOT-表region信息)–>-ROOT-表(查询表获取.META.表region信息)–>.META.表(查询表获取用户数据信息)–>RegionServer–>Region )
HMaster:是一个内部管理者,只对客户端提供DDL操作,因而HMaster的负载是比较低的。当集群中存在多个(两个或者三个以上)HMaster,备用的Master会定期与Active Master通信以获取最新的状态信息,以保证故障切换时自身的数据状态是最新的,因而Active Master可能会收到大量来自备用Master的数据请求,有可能导致HMaster负载较高。
-
管理RegionServer,监听其状态,保证集群负载均衡且高可用。
-
管理Region,如新Region的分配、RegionServer宕机时该节点Region的分配与迁移。
-
接收客户端的DDL操作,如创建与删除表、列簇等信息
-
权限控制
通过 Zookeeper 实现对集群中各个 RegionServer 的监控与管理,在 RegionServer 发生故障时可以发现节点宕机并转移 Region 至其他节点,以保证服务的可用性。但是HBase的故障转移并不是无感知的,相反故障转移过程中,可能会直接影响到线上请求的稳定性,造成段时间内的大量延迟。
在分布式系统的 CAP定理 中(Consistency一致性、Availability可用性、Partition tolerance分区容错性),分布式数据库基本特性都会实现P,但是不同的数据库对于A和C各有取舍。如HBase选择了C,而通过Zookeeper这种方式来辅助实现A(虽然会有一定缺陷)。
RegionServer:一个RegionServer中存储并管理者多个Region(不同Table的region会分布在多个RS以实现读写请求的负载均衡),是HBase集群中真正 存储数据、接受读写请求 的地方,是HBase架构中最核心、同时也是最复杂的部分。
-
根据HMaster的region分配请求,存放和管理Region
-
接受客户端的读写请求,检索与写入数据,产生大量IO
Region 概念:
Table -> Region(水平分表,分布在多个RS) -> Store(和列簇一对一) -> StoreFile(HFile)
Table:由一个或者多个Region组成,一个Region中可以看成是Table按行切分且有序的数据块,每个Region都有自身的StartKey、EndKey。
Region:由一个或者多个Store组成,每个Store存储该Region对应table中一个列簇的数据,相同列簇的列存储在同一个Store中。
Store:由一个Memstore(内存)、1~多个StoreFile(磁盘)两部分组成。
Store、Region、Table的关系:多个Store(列簇)组成Region,多个Region(行数据块)组成完整的Table。
BlockCache:RegionServer中的读缓存,一个RegionServer共用一个BlockCache。HBase提供了两种不同的BlockCache实现,用于缓存从HDFS读出的数据。这两种分别为:
-
默认的,存在于堆内存的(on-heap)LruBlockCache
-
存在堆外内存的(off-heap)BucketCache
MemStore: Store的写缓存(同时还具备读缓存的功能),以RowKey、Column Family、Column、Timestamp进行排序(数据在Memstore中为已排序,顺序写入磁盘性能高、速度快)。
WAL(HLog):WAL 全称 Write Ahead Log,也叫HLog ,是 RegionServer 中的预写日志。所有写入数据默认情况下都会先写入WAL中,以保证RegionServer宕机重启之后可以通过WAL来恢复数据,一个RegionServer中共用一个WAL。
LSMT:全称 Log-Structured Merge Tree, HBase采用LSMT 存储。存储引擎
LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。No-SQL数据库一般采用LSM树作为数据结构。
数据读写
写入
RegionServer的写流程如下:
将数据写入WAL中
根据TableName、Rowkey和ColumnFamily将数据写入对应的Memstore中
Memstore通过特定算法将内存中的数据刷写成Storefile写入磁盘(以HFile格式存储HBase的KV数据 于HDFS之上),并标记WAL sequence值。
Storefile定期合小文件
读取
RegionServer处理客户端数据查询请求,只需要从以上三个地方检索到数据即可,在HBase中的检索顺序依次是:BlockCache -> Memstore -> HFiles。
在BlockCache中查询是否命中缓存
缓存未命中则定位到存储该数据的Region
检索Region Memstore中是否有所需要的数据
Memstore中未查得,则检索Hfiles
任一过程查询成功则将数据返回给客户端并缓存至BlockCache
检索HFiles时会产生真实磁盘的IO操作,且Memstore不停刷写的过程中,将会产生大量的HFile
如何在大量的HFile中快速找到所需要的数据呢?
为了提高检索HFiles的性能,HBase支持使用 Bloom Fliter
对HFiles进行快读定位。
压缩与分裂
一个store中storefile数量超过阈值,触发Compact操作,将多个storefile合并成一个storefile。
一个region中storefile的总大小超过阈值,触发split操作,把当前region split成两个region,父region下线,新split的2个子region被master分配到合适的regionserver上。
快照机制
作用
-
数据备份与恢复。
-
利用ExportSnapshot工具实现数据迁移,可以迁移至HDFS或各类主流对象存储。
-
使用MR/Spark直接扫描Snapshot,进行离线分析,避免对实时读写的影响。
原理:HBase基于LSM Tree实现,数据落盘形成HFile之后就不会再改动,所以实现Snapshot的基本思想就是生成某一时刻所有HFile的引用,而不需要做数据拷贝,所以足够轻量。
内存配置
堆内(jvm堆):memstore、LRUBlockCache、rpc队列等。
堆外(director):BucketCache
缓存策略:
-
LruBlockCache 缓存机制:把元数据和列族信息缓存在Java堆内存中。如果 BucketCache 机制没有启动时,默认是启动 LruBlockCache 的;
-
CombinedBlockCache 缓存机制:运用 LruBlockCache 和 BucketCache 两个缓存:当BucketCache启用时,INDEX/BLOOM块会保存于LRUBlockCache的堆内存,数据块(DATA blocks)会一直保存于BucketCache。这时启动 BucketCache 缓存机制后默认的操作;
-
一级和二级缓存机制 (Raw L1+L2):这个机制把元数据和列族信息缓存在LruBlockCache (一级缓存),然后从 LruBlockCache 读取数据缓存在 BucketCache (二级缓存),如果要启动这个缓存机制,要先在 hbase-site.xml 中配置 hbase.bucketcache.combinedcache.enabled=false,这个参数默认是 true;
注意:从HBase 2.0.0 开始,L1与L2的概念便被弃用。第三种缓存方式只能在hbase 2.0.0之前,可以设置。
CombinedBlockCache 是一个LRUBlockCache和BucketCache的混合体。BucketCache是阿里贡献的。LRUBlockCache中主要存储Index Block和Bloom Block,而将Data Block存储在BucketCache中。 因此一次随机读首先在LRUBlockCache中查到对应的Index Block,然后再到BucketCache查找对应数据块。
客户端
HBase客户端需要和多个服务器中的不同服务角色建立连接,所以HBase客户端中的Connection对象并不是简单对应一个socket连接。
HBase clinet要连接三个不同的服务角色:
-
Zookeeper:主要用于获得meta-region位置,集群Id、master等信息。
-
HBase Master:主要用于执行HBaseAdmin接口的一些操作,例如建表等。
-
HBase RegionServer:用于读、写数据。
实践
- 列簇尽量少
- 建表预分区
- rowKey设计原则:
-
HBase的RowKey是按照ASCII有序排序的,要充分利用这点,使得rowKey均匀的分布在各个HBase节点上。
-
rowKey长度定长,越短越好,不要超过16个字节。
-
在设计上保证RowKey的唯一性。
-
常用命令行
建表
'profile:PROFILE_DEVICE', {NAME => 'M', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'SNAPPY', BLOCKCACHE => 'true', BLOCKSIZE => '65536'},{SPLITS=> ['1000','2000','3000','4000','5000','6000','7000','8000','9000','a000','b000','c000','d000','e000','f000']}
表管理
查看表
describe 't1'
禁用表
disable 'test1'
启用表
enable 'test1'
添加数据
语法:put <table>,<rowkey>,<family:column>,<value>,<timestamp>
put 't1','rowkey001','f1:col1','value01'
查询记录
hbase(main)> get 't1','rowkey001', 'f1:col1'
hbase(main)> get 't1','rowkey001', {COLUMN=>'f1:col1'}
扫描表
语法:scan <table>, {COLUMNS => [ <family:column>,.... ], LIMIT => num}
scan 't1',{LIMIT=>5}
参考
https://www.cnblogs.com/CtripDBA/p/14210220.html