HBase官网:https://hbase.Apache.org/。
Apache HBase 是 Hadoop中一个支持分布式的、可扩展的大数据存储的数据库。
当需要对大数据进行随机、实时读/写访问时,可以用 Apache HBase。
列式存储:
HBase是面向列族的非关系型数据库,每行数据列都可以不同,并且列可以按照需求进行动态增加。
因此在开始创建HBase表时,可以只创建列族,等需要时再创建相应的列。
数据压缩:
列式存储意味着数据往往类型相同,可以采用某种压缩算法进行统一的压缩存储。
海量存储:
HDFS支持的海量存储,存储PB级数据仍能有百毫秒内的响应速度。
进入HBase客户端命令操作界面:
hbase shell
查看帮助命令:
hbase(mAIn):001:0> help
查看当前数据库中有哪些表:
hbase(main):006:0> list
创建一张表:
创建user表, 包含base_info、extra_info两个列族。
hbase(main):007:0> create 'user', 'base_info', 'extra_info'
create 'user', {NAME => 'base_info', VERSIONS => '3'},{NAME => 'extra_info'}
添加数据操作:
向user表中插入信息,row key为 rk0001,列族base_info中添加name列标示符,值为zhangsan。
hbase(main):008:0> put 'user', 'rk0001', 'base_info:name', 'zhangsan'
向user表中插入信息,row key为rk0001,列族base_info中添加age列标示符,值为20。
hbase(main):010:0> put 'user', 'rk0001', 'base_info:age', 20
查询数据:
通过rowkey进行查询:
hbase(main):006:0> get 'user', 'rk0001'
查看rowkey下面的某个列族的信息:
hbase(main):007:0> get 'user', 'rk0001', 'base_info'
查看rowkey指定列族指定字段的值:
hbase(main):008:0> get 'user', 'rk0001', 'base_info:name', 'base_info:age'
查看rowkey指定多个列族的信息
hbase(main):010:0> get 'user', 'rk0001', 'base_info', 'extra_info'
hbase(main):011:0> get 'user', 'rk0001', {COLUMN => ['base_info', 'extra_info']}
hbase(main):012:0> get 'user', 'rk0001', {COLUMN => ['base_info:name', 'extra_info:address']}
指定rowkey与列值查询:
hbase(main):013:0> get 'user', 'rk0001', {FILTER => "ValueFilter(=, 'binary:zhangsan')"}
指定rowkey与列值模糊查询:
hbase(main):015:0> get 'user', 'rk0001', {FILTER => "(QualifierFilter(=,'substring:a'))"}
插入一批数据:
hbase(main):016:0> put 'user', 'rk0002', 'base_info:name', 'fanbingbing'
hbase(main):017:0> put 'user', 'rk0002', 'base_info:gender', 'female'
hbase(main):018:0> put 'user', 'rk0002', 'base_info:birthday', '2000-06-06'
hbase(main):019:0> put 'user', 'rk0002', 'extra_info:address', 'Shanghai'
查询所有数据:
hbase(main):020:0> scan 'user'
列族查询:
Scan:
hbase(main):021:0> scan 'user', {COLUMNS => 'base_info'}
hbase(main):022:0> scan 'user', {COLUMNS => 'base_info', RAW => true, VERSIONS => 5}
多列族查询:
hbase(main):023:0> scan 'user', {COLUMNS => ['base_info', 'extra_info']}
hbase(main):024:0> scan 'user', {COLUMNS => ['base_info:name', 'extra_info:address']}
指定列族与某个列名查询:
hbase(main):025:0> scan 'user', {COLUMNS => 'base_info:name'}
指定列族与列名以及限定版本查询:
hbase(main):026:0> scan 'user', {COLUMNS => 'base_info:name', VERSIONS => 5}
指定多个列族与按照数据值模糊查询:
hbase(main):027:0> scan 'user', {COLUMNS => ['base_info', 'extra_info'], FILTER => "(QualifierFilter(=,'substring:a'))"}
rowkey的范围值查询:
hbase(main):028:0> scan 'user', {COLUMNS => 'base_info', STARTROW => 'rk0001', ENDROW => 'rk0003'}
指定rowkey模糊查询:
hbase(main):029:0> scan 'user',{FILTER=>"PrefixFilter('rk')"}
更新数据值:
hbase(main):030:0> put 'user', 'rk0001', 'base_info:name', 'zhangsansan'
指定rowkey以及列名进行删除:
hbase(main):032:0> delete 'user', 'rk0001', 'base_info:name'
指定rowkey,列名以及字段值进行删除:
hbase(main):033:0> delete 'user', 'rk0001', 'base_info:age', 1564745324798
删除 base_info 列族。
hbase(main):034:0> alter 'user', NAME => 'base_info', METHOD => 'delete'
hbase(main):035:0> alter 'user', 'delete' => 'base_info'
删除user表数据:
hbase(main):036:0> truncate 'user'
删除user表:
#先disable 再drop
hbase(main):036:0> disable 'user'
hbase(main):037:0> drop 'user'
#如果不进行disable,直接drop会报错
ERROR: Table user is enabled. Disable it first.
Rowkey(行键):
Column Family(列族):
Timestamp(时间戳):
Column(列):
Cell(单元格):
利用了HDFS的分布式存储和Hadoop的分布式计算能力:
将数据按照行和列族的方式存储在HDFS上:
利用了LSM(Log-Structured Merge-Tree)算法:
支持数据的自动分片和负载均衡:
HMaster:
HRegionServer:
Zookeeper:
HRegion:
Store:
MemStore:
StoreFile(HFile):
HDFS:
一个列族就划分成一个 Store,如果一个表中只有 1 个列族,那么每一个 Region 中只有一个 Store。
一个 Store 里面只有一个 MemStore。
一个 Store 里面有很多个 StoreFile, 最后数据是以很多个 HFile 文件保存在 HDFS 上。
HBase 官方目前支持两种负载均衡策略:
SimpleLoadBalancer 策略:
这种策略能够保证每个 RegionServer 的 Region 个数基本相等。
假设集群中一共有 n 个 RegionServer,m 个 Region ,那么集群的平均负载就是 average = m/n。
因此, SimpleLoadBalancer 策略中负载就是 Region 个数,集群负载迁移计划就是 Region 从个数较多的 RegionServer 上迁移到个数较少的 RegionServer 上。
虽然集群中每个 RegionServer 的 Region 个数都基本相同,但如果某台 RegionServer 上的 Region 全部都是热点数据,导致 90 %的读写请求还是落在了这台 RegionServer 上,这样没有达到负载均衡的目的。
StochasticLoadBalancer 策略:
它对于负载的定义不再是 Region 个数这么简单,而是由多种独立负载加权计算的复合值,这些独立负载包括:
这些独立负载经过加权计算会得到一个代价值,系统使用这个代价值来评估当前 Region 分布是否均衡,越均衡代价值越低。
MemStore的大小超过某个值的时候,会Flush到磁盘,默认为128M。
MemStore中的数据时间超过1小时,会Flush到磁盘。
HRegionServer的全局MemStore的大小超过某大小会触发Flush到磁盘,默认是堆大小的40%。
HBase需要在必要的时候将小的Store File合并成相对较大的Store File,这个过程为Compaction。
在HBase中主要存在两种类型的Compaction合并。
Minor Compaction 小合并:
Major Compaction 大合并:
Region 中存储的是大量的 Rowkey 数据,当 Region 中的数据条数过多的时候,直接影响查询效率。
HBase 的 Region Split 策略一共有以下几种。
ConstantSizeRegionSplitPolicy:
0.94版本前默认切分策略。
当Region大小大于某个阈值之后就会触发切分,一个Region等分为2个Region。
阈值设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个。
如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的Region,这对于集群的管理、资源使用、Failover不好。
IncreasingToUpperBoundRegionSplitPolicy:
0.94版本~2.0版本默认切分策略。
总体看和ConstantSizeRegionSplitPolicy思路相同,一个Region大小大于设置阈值就会触发切分。
Region Split的计算公式是:
例如:
后面每次split的size都是10GB了。
SteppingSplitPolicy:
KeyPrefixRegionSplitPolicy:
DelimitedKeyPrefixRegionSplitPolicy:
DisabledRegionSplitPolicy:
不启用自动拆分,需要指定手动拆分。
当一个table刚被创建的时候,HBase默认的分配一个Region给table。
解决办法:
如何预分区?
手动指定预分区:
create 'person','info1','info2',SPLITS => ['1000','2000','3000','4000']
HBase 支持 put , get , delete 和 scan 等基础操作,所有这些操作的基础是 region 定位。
region 定位基本步骤:
客户端与 ZooKeeper 交互,查找 hbase:meta 系统表所在的 Regionserver。
hbase:meta 表维护了每个用户表中 rowkey 区间与 Region 存放位置的映射关系,具体如下:
客户端与 hbase:meta 系统表所在 RegionServer 交互,获取 rowkey 所在的 RegionServer。
客户端与 rowkey 所在的 RegionServer 交互,执行该 rowkey 相关操作。
需要注意:
读操作:
首先从 ZooKeeper 找到 meta 表的 region 位置,然后读取hbase:meta 表中的数据,hbase:meta 表中存储了用户表的 region 信息。
根据要查询的 namespace 、表名和 rowkey 信息,找到写入数据对应的 Region 信息。
找到这个 Region 对应的 RegionServer ,然后发送请求。
查找对应的 Region。
先从 MemStore 查找数据,如果没有,再从 BlockCache 上读取。
如果 BlockCache 中也没有找到,再到 StoreFile(HFile) 上进行读取。
写操作:
首先从 ZooKeeper 找到 hbase:meta 表的 Region 位置,然后读取 hbase:meta表中的数据,hbase:meta 表中存储了用户表的 Region 信息。
根据 namespace 、表名和 rowkey 信息找到写入数据对应的 Region 信息。
找到这个 Region 对应的 RegionServer ,然后发送请求。
把数据分别写到 HLog (WriteAheadLog)和 MemStore 各一份。
MemStore 达到阈值后把数据刷到磁盘,生成 StoreFile 文件。
删除 HLog 中的历史数据。
用户数据位于 HDFS 中,业务需要定期将这部分海量数据导入 HBase 系统,以执行随机查询更新操作。
这种场景如果调用写入 API 进行处理,极有可能会给 RegionServer 带来较大的写人压力。
所以HBase提供了另一种将数据写入HBase集群的方法:BulkLoad。
BulkLoad 首先使用 MapReduce 将待写入集群数据转换为 HFile 文件,再直接将这些 HFile 文件加载到在线集群中。
BulkLoad 没有将写请求发送给 RegionServer 处理,可以有效避免上述一系列问题。
什么是热点?
检索 HBase 的记录首先要通过Row Key来定位数据行。
当大量的 Client 访问 HBase 集群的一个或少数几个节点,造成少数 Region Server 的读/写请求过多、负载过大,而其他Region Server 负载却很小,就造成了 热点 现象。
解决方案:
预分区:
- 目的让表的数据可以均衡的分散在集群中,而不是默认只有一个Region分布在集群的一个节点上。
加盐:
- 在Rowkey的前面增加随机数,具体就是给Rowkey分配一个随机前缀以使得它和之前的Rowkey的开头不同。
哈希:
- 哈希会使同一行永远用一个前缀加盐。
- 也可以使负载分散到整个集群,但是读是可以预测的。
- 使用确定的哈希可以让客户端重构完整的Rowkey,可以使用get操作准确获取某一个行数据。
反转:
- 反转固定长度或者数字格式的Rowkey。
- 这样可以使得Rowkey中经常改变的部分放在前面。