介绍HBase
从功能上讲:HBase主要是解决分布式文件系统HDFS不能随机读写而设计的,HBase是架设在HDFS之上的,所以HBase可以存储海量的数据,HBase又可以支持随机读写,所以HBase是一个支持海量数据随机读写的分布式存储系统。
从架构上讲:HBase中由zookeeper集群、一个HMaster以及若干个HRegionServer组成,zookeeper主要是负责存储HBase集群中的元数据信息(包括但不限于HMaster的位置、HRegionServer的信息、Table等元数据信息),HMaster负责管理HRegionServer,HRegionServer负责管理HRegion,HBase中的table就是由HRegion组成的
从数据模型上讲:HBase中的数据是以表的形式组织起来的,每张表都有自己属于的namespace(你可以把namespace看作Mysql中的数据库),表默认的话是创建在default这个namespace中。HBase中的表其实是一个类似于三层Map结构的模型,一个Table中包含很多的rowKey,每一个rowkey就是对应着一行(row)数据;每一行数据包含了若干个列(column),每一个列由一个column family(列蔟)和column qualifier(列名)组成,当然column qualifier可以为空;每一个列包含了若干个版本的数据值,这个版本默认是数据插入时的服务器时间戳,版本数默认是1个,当然版本以及版本的数量是可以设置的。
HBase的数据模型?
HBase中的数据是以表的形式组织起来的,每张表都有自己属于的namespace(你可以把namespace看作Mysql中的数据库),表默认的话是创建在default这个namespace中。HBase中的表其实是一个类似于三层Map结构的模型,如下图:
一个Table中包含很多的rowKey,每一个rowkey就是对应着一行(row)数据;每一行数据包含了若干个列(column),每一个列由一个column family(列蔟)和column qualifier(列名)组成,当然column qualifier可以为空;每一个列包含了若干个版本的数据值,这个版本默认是数据插入时的服务器时间戳,版本数默认是1个,当然版本以及版本的数量是可以设置的。
HBase数据模型:
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110452412-245201891.png)
(1) 一个Table中包含很多的rowKey,每一个rowkey就是对应着一行(row)数据;每一行数据包含了若干个列(column),每一个列由一个column family(列蔟)和column qualifier(列名)组成,column family不能为空,column qualifier可以为空、
(2) 三层map:第一层map中的每一对Key-value 称之为row
第二层Map中的每一对key-value称之为column
第三层map中的每一对key-value称之为version——> value (版本,以时间戳)
namespace
更改设置version(1-6)
相比传统数据库,列式存储空值不占用空间,存储的更加合理
使用hbase shell创建一张表test,然后对这张表进行增删改查:
先创建一个新的namespace:
create_namespace ‘new_ns’
在new_ns中创建表:
create ‘new_ns:test’,‘f’, ‘e’ //test表中有2个column family
增加数据:
put ‘new_ns:test’, ‘rowkey-1’, ‘f:a’, ‘value1’
put ‘new_ns:test’, ‘rowkey-1’, ‘e’, ‘value2’
查询一行或者一列的数据:
get ‘new_ns:test’, ‘rowkey-1’
get ‘new_ns:test’, ‘rowkey-1’, {COLUMN => ‘f:a’}
更新一列的值:
put ‘new_ns:test’, ‘rowkey-1’, ‘f:a’, ‘value3’
删除一列或者一行的数据:
delete ‘new_ns:test’, ‘rowkey-1’, ‘f:a’
deleteall ‘new_ns:test’, ‘rowkey-1’
查看表中有多少行数据:
count ‘new_ns:test’
清空表的数据:
truncate ‘new_ns:test’
查看表结构:
desc‘new_ns:test’
修改表的结构:
disable ‘new_ns:test’
alter ‘new_ns:test’,{NAME=‘f’,TTL=‘15552000’}
enable ‘new_ns:test’
删除表:
disable ‘new_ns:test’ ----在删除之前需要disable
drop ‘new_ns:test’
描述下HBase的架构?
Hbase数据模型—— VERSION & TTL (Time To Live)
create 'new_ns:test',{NAME => 'f'},{NAME => 'e', VERSIONS => 3},{NAME => 'x'} 搜索是显示最近3列,默认get最近一列
get 'new_ns:test',{COLUMN => 'e',VERSION => 10} 依然显示最近三列
create 'new_ns:test',{NAME => 'f'},{NAME => 'e', VERSIONS => 1},{NAME => 'x', TTL => 5} 搜索时显示最近5秒钟内的所有数据,时间已过,column family对应的值全部清楚
alter new_ns:test',{NAME => 'x', TTL => 15} 更改属性(涉及更改底层数据,更新较慢)
HBase也是遵守主从架构的技术,由一个主HMaster和若干个从HRegionServer组成。HBase中的一张表的数据由若干个HRegion组成,也就是说每一个HRegion负责管理一张表中的一段数据,HRegion都是分布式的存在于HRegionServer中(也就是说一个HRegionServer是管理多个HRegion的进程),所以说HRegion是HBase表中数据分布式存储的单位。那么一个HRegion中又是由若干个column family的数据组成(HRegion对应的表有几个column family,那么HRegion中就管理几个column family的数据);在HRegion中每个column family数据由一个store管理,每个store包含了一个memory store和若干个HFile组成,HFile的数据最终都会落地到HDFS文件中,所以说HBase依赖HDFS,存在HBase中的数据最终都会落地到HDFS中。
在HBase中还有一部分元数据信息,比如HMaster的状态信息、HRegionServer的状态信息以及HRegion的状态信息等,这些信息都是存储在zookeeper集群中,zookeeper集群在HBase中处于一个分布式协调服务的角色,客户端要连接HBase集群的话,也是直接连接zookeeper集群即可,客户端可以在zookeeper中找到需要和HBase的哪一个进程通讯。给出如下的架构图:
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110642265-152040807.png)
单个值是怎样存储在HBase的表中的?
组织结构: 一个table对应多个Region,一个region对应多个Column Family,一张表有多少个Column Family,一个Region则对应多少个Column Family,Column Family里面有一个Store,一个Store里面含有一个MemoryStore和多个HFile。
Table-> Region的关系:
一个Table有多个Region,每一个Region达到指定的数据量后(默认是10G),进行Region切分。Hbase最基本的存储单元是Region,分布式的存储在所有HRegionServer上
每一个Region负责管理和存储一个Table中的某段数据。
每一个table的RowKey是按照字符串的自然顺序升序排列的 可以使数据均匀分布
Region-> Column Family的关系:
一个region对应多个Column Family,一张表有多少个Column Family,一个Region则对应多少个Column Family,Column Family数据存储的基本单元
一个Region负责管理和存储Table中所有的Column Family数据
一个Region负责管理和存储一个或者多个Column Family的数据
3、Column Family -> Store的关系:
一个Column Family对应着一个Store
一个Store中含有一个MemoryStore和若干个HFile 数据先存储在MemoryStore,达到一定的容量后在存储在HFile中
4、HFile -> Block的关系:
一个HFile含有若干个不同类型的Dtata Block 及其他元数据信息 即 Meta data
Block的大小通常为8K到1MB,默认的大小是64KB
Block的类型有:Data Blocks、Index Blocks、Bloom filter Blocks以及Trailer block
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110713317-2115622750.png)
5、Block -> KeyValue的关系:
一个Block包含一个magic数字和若干个KeyValue的数据
若干个 key value合计达到64kb组成一个block
put数据根据Rowkey 的排序放在相应的block中去,一个put操作值是存储在table下面的对应的某一个Region下面的对应的CF下面的Hfile下面的block中的KeyValue,每一个block在HFile中都是有索引信息的。请求获取数据可通过索引信息拿到值所在的block信息,只需要扫描64kb的block就可拿到相应的值,进而达到高校的数据效果,进而支持高效的随机读写。
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110725668-339187448.png)
HFile
答: HBase依赖HDFS,存在HBase中的数据最终都会落地到HDFS中,HBase中的数据在HDFS中就是以HFile这种格式的文件组织起来的。一个HFile包含了若干个不同类型的Block,每一个Block的大小通常为8K到1M(默认是64K);Block的类型有Data Blocks、Index Blocks、Bloom filter Blocks以及Trailer block,一个Data Block包含一个magic数字和若干个KeyValue的数据。Index Blocks是记录每一个Data Block的数据位置(就是索引信息)用于随机读写
查看HFile内容:hbase org.apache.hadoop.hbase.io.hfile.HFile -f hfile_name
Block Encoder与Block Compressors
Prefix RowKey的存储按升序排列,处理相对容易,只存储不容部位,达到空间省略
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110802807-2061040571.png)
diff 相同的数据不在存储,进而达到空间节省
timestamp存储时间差
diff
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110813335-179172830.png)
2、Fast Diff
如果你的rowkey很长,并且有很多的Column的话,则推荐使用Fast Diff,实现比Diff更快
Block Compressors
(1、none
(2、Snappy
(3、LZO
(4、LZ4
(5、GZ
默认 不压缩
1、如果rowkey很长的话或者这个column family含有很多的column的话,则使用Block Encoder
推荐使用Fast Diff
2、如果value很大的话,则需要使用Block Compressors
3、对于访问不频繁的数据(code data),使用GZ压缩器,因为GZ需要更多的cpu资源,但是有很好的压缩率
4、对于访问频繁的数据(hot data),使用Snappy或者LZO压缩器,因为Snappy或者LZO不需要更多的cpu资源,但是压缩率没有GZ高
5、大部分的场景下默认使用Snappy或者LZO就好,因为他们提供了更好的性能Snappy比LZO表现还稍微好点
create 'test_encoder_compress', {NAME => 'c', COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'FAST_DIFF'}, {NAME => 'l'} create 'test_encoder_compress', {NAME => 'c', COMPRESSION => 'GZ', DATA_BLOCK_ENCODING => 'FAST_DIFF'}, {NAME => 'l'}
Bloom Filter
create 'webtable',{NAME => 'c', COMPRESSION => 'GZ', DATA_BLOCK_ENCODING => 'FAST_DIFF'}, {NAME => 'l', BLOOMFILTER => 'ROW'}
Bloom Filter取值: NONE, ROW(默认), ROWCOL
ROW:
根据KeyValue中的row来过滤storefile
举例:假设有2个storefile文件sf1和sf2,
sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v)
sf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v)
如果设置了CF属性中的bloomfilter为ROW,那么get(r1)时就会过滤sf2,get(r3)就会过滤sf1
ROWCOL:
根据KeyValue中的row+qualifier来过滤storefile
举例:假设有2个storefile文件sf1和sf2,
sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v)
sf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v)
如果设置了CF属性中的bloomfilter为ROW,无论get(r1,q1)还是get(r1,q2),都会读取sf1+sf2;而如果设置了CF属性中的bloomfilter为ROWCOL,那么get(r1,q1)就会过滤sf2,get(r1,q2)就会过滤sf1
HBase的读写缓存机制
写缓存机制: Client====>hbase:meta===>RS===>HRegion===>WAL(稳定性和高可用性)===>Store===>MemoryStore===>HFile
所有的数据先写入到Memory Store中,如果Memory Store所占的内存符合下面的规则的话,则会将数据flush到磁盘中:
(1)、当一个Region中所有MemoryStore内存之和大于hbase.hregion.memstore.flush.size(默认大小是:134217728字节(128M))的时候,这个MemoryStore所在的Region中的所有MemoryStore都会写到磁盘
(Hbase中的删除并不是真正的删除,仅仅只是打上标记delecolum,在查询时会过滤掉其标记数据,在数据压实是会真正的删除数据)
(2)、当一个HRegionServer中所有的MemoryStore加在一起的大小大于hbase.regionserver.global.memstore.upperLimit默认大小是堆内存的40%,那么这个HRegionServer中的所有的Region中的内存数据都会flush到磁盘中,当所有的内存使用达到
问题: 防止内存不稳定,提高可靠性
引用WAL机制 (Write Ahead Log 预写日志)
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909110950001-1786485869.png)
数据先写道HDFS中的HLog中去,写完后,在把数据put到MemoryStore中去
LogStner :用于同步缓存在RegionServer上的HLog到HDFS
rollWriter (1)用于滚动日志,使得日志文件数量以及文件大小不会太大,(2)MemoryStore中的文件已经持久化到HFile中去,清理掉HLog中的数据
数据恢复: recovered.edits 在HDFS中每个region中都有一个 recovered.edits 自动回复
(HLog默认开启状态,但消耗性能)
读缓存机制:
Client(获取对应RS所在的机器)===>hbase:meta===>RS===>HRegion===>Store===>MemoryStore(查看是否有相应数据)===>BlockCache(所有的Region共用一个BlockCache)===>HFile(读到的数据快放在BlockCache中)
三种缓存过期策略:(内存容量一定的情况下,内存满了,清理数据算法)
1、FIFO -> 按照“先进先出”的原理来淘汰数据
2、LRU(Least recently used, 最近最少使用) -> 根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
3、LFU(Least frequently used, 最近不常使用) -> 根据数据的历史访问频率来淘汰数据,
其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”
LruBlockCache中的三种Block priority:
1、Single access priority : 当一个Block第一次从HDFS中加载到内存中就是这个级别
2、Multi access priority : 当一个Block再次被访问的时候就会变成这个级别
3、In-memory access priority : 当一个cf被设置为In Memory的时候,则不管这个cf的block被访问了多少次都不会从内存中淘汰,比如hbase:meta表中的cf,数据量小,经常访问
HColumnDescriptor.setInMemory(true); hbase(main):003:0> create 't', {NAME => 'f', IN_MEMORY => 'true'}
读缓存策略包含两种,一种是LruBlockCache,一种是BucketCache,其中默认是使用LruBlockCache
除了缓存需要读取的block之外,还需要缓存如下的数据:
1、hbase:meta
2、HFile Indexes
3、Keys
4、Bloom Filters
LruBlockCache是使用LRU的算法实现的缓存策略
如果要使用BucketCache的话,需要配置开启,BucketCache一般和LruBlockCache配合使用,配合使用的方式有两种:
(1)、LruBlockCache用于缓存Index和Bloom这种META block数据,而BucketCache用于缓存真的数据
(2)、BucketCache作为LruBlockCache的二级缓存使用hbase.regionserver.global.memstore.lowerLimit的时候就不会flush了
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909111022635-1724880947.png)
HBase中的compaction机制的
随着时间的推移,Store中小的HFile越来越多,使得性能下降,这个时候就需要Compaction了。Compaction就是将小的HFile合并压缩成一个大的HFile文件。Compaction分为Minor compactions和Major compactions。
Minor compactions(局部压缩):进行局部的压缩,选择一个Store中的小量的HFile进行合并成一个大的HFile文件
Major compactions(全局压缩):合并一个表的所有Store中的HFiles,使得一个Store只有一个HFile。
1、Major Compaction还会真的删除需要删除的数据
2、Major Compaction还会真的删除多余版本的数据
3、默认是每7天会自动执行一次Major Compaction,将hbase.hregion.majorcompaction(默认是604800000 milliseconds,即7天)设置为0的话,则表示禁止自动压缩
执行Major Compaction的时候会使得HBase整个集群都非常慢,因为Major Compaction需要大量的资源
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909111042633-398092938.png)
防止热点写,在创建表的时候预切分成4个region(3个切分点),进而解决热点问题,根据rowkey的类型设计
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909111054882-2031987668.png)
![](https://img2018.cnblogs.com/blog/1598893/201909/1598893-20190909111059769-1807935822.png)
太多的region
1、snapshots time out
2、compaction storms
3、客户端的操作会超时(比如flush)
4、bulk load会超时(可能会抛出RegionTooBusyException)
造成太多region的原因
1、为每一个Region的file size配置过小
2、不合理的对region进行了切分或者预切分
merge_region 'ENCODED_REGIONNAME','ENCODED_REGIONNAME' merge_region 'ENCODED_REGIONNAME','ENCODED_REGIONNAME',true
Balancing
保持Region可以均匀的分布在每一个RegionServer上
HMaster每隔:
hbase.balancer.period(默认是300000ms即5分钟)
会进行一次Balance
Snapshot
在hbase-site.xml中配置如下参数,然后开启snapshot功能 <property> <name>hbase.snapshot.enabled</name> <value>true</value> </property>