hbase数据存储及数据访问原理

行式存储与列式存储

行式存储

上图是一张关于学历的存储模型,采用的是行式存储

从图中可以看出只有张三丰一个人学历比较高,将一行中的所有列都填满了,其他的人都没有将所有的学历填充满。由于行式存储结构固定,没有内容的列也需要占用存储空间,即使没有内容,占据的存储空间也还是在那里。

列式存储

从图中可以看出,行式存储中张三丰的一列数据在列式存储中变成了一行,原来张三丰的六列数据在列式存储中变成了六行。原来在行式存储中张三丰的一行数据共用一个主键“张三丰”,到列式存储中,每一行都对应一个主键“张三丰”,用来标识每一行的数据属于谁的,所以原来的一个主键“张三丰”在列式存储中重复了六次。

行存储与列存储的转换关系见下图

相当于将每一行的每一列拆开,然后通过rowkey(行唯一键)关联起来,rowkey相同的这些数据其实就是原来的一行。由于原来的列变为了现在的行,有需要就加一行,没需要就不加,有效地减少了存储空间的浪费。

压缩效率

对于行式数据库,必然按行压缩,当一行中有多个字段,各个字段对应的数据类型可能不一致,压缩性能压缩比就比较差。

对于列式数据库,必然按列压缩,每一列对应的是相同数据类型的数据,故列式数据库的压缩性能要强于行式数据库。

hbase存储模式

列族式存储

列族,即多列的一个组合,hbase表中的多列都归属于一个列族,列族是表schema的一部分,而列并不是。所以我们在hbase中创建表的时候,需要给出列族,而不需要给出列。在磁盘层面,hbase访问数据是在列族层面来进行的。hbase准确的来说是列族式数据库,而不是列式数据库。一般来说,我们在设计hbase表的时候,会将某些经常需要一起访问的列放在一个列族中,这样就使得数据的存储是最优的。

HBase Table的组成

table = rowkey + family + column + timestamp + value

rowkey:行键,一行数据的唯一标识,通过相同的rowkey去汇总不同列的数据,拿到的数据即一行数据

family:列族,一行数据中包含多个列族,设计表的时候列族一般也不推荐过多

column:列,一个列族中有多个列,但是一个列只对应一个列族

timestamp:版本信息,每一列的一个数据都对应一个版本,也可以理解为一个时间戳。hbase默认建表时一条列数据有三个版本号,当然建表的时也可以指定版本数量

value:数据本身的值

数据存储模式

(table,rowkey,family,column,timestamp)--> value

简单的理解就是一个key-value模式

看下面的图,hbase存储的数据也是行列形式的表,每一行包含很多的列数据。拆分后来看就是红色箭头下面的每一行对应多个列族,每个列族有对应多个列。

列数据的版本

 

数据存储原型

用一种类似于编程语言风格表示如下

或者换成一行来表示:

SortedMap<RowKey,List<SortedMap<Column,List<Value,Timestamp>>>>

HBase的组成

Client

利用 RPC 机制与 HMaster 和HRegionServer通信

Zookeeper

协调,避免 HMaster 单点问题;HMaster没有单点问题,HBase 中可以启动多个HMaster,通过 ZooKeeper 的 Master Election 机制保证总有一个 Master 在运行

Master

负责 Table 和 Region 的管理工作

1.管理用户对 Table 的 CRUD 操作;

2.管理 HRegionServer的负载均衡,调整Region 分布;

3.在 RegionSplit 后,负责新Region分配;

4.在 HRegionServer停机后,负责失效 HRegionServer 上的Region 迁移;

RegionServer

HBase 最核心模块,响应用户IO请求,向 HDFS 中读写数据

HLog:HBase中系统故障恢复以及主从复制都基于HLog实现。默认情况下,所有写入操作(写入、更新以及删除)的数据都先以追加形式写入HLog,再写入MemStore。大多数情况下,HLog并不会被读取,但如果RegionServer在某些异常情况下发生宕机,此时已经写入MemStore中但尚未f lush到磁盘的数据就会丢失,需要回放HLog补救丢失的数据。此外,HBase主从复制需要主集群将HLog日志发送给从集群,从集群在本地执行回放操作,完成集群之间的数据复制。

Region:hbase集群分布数据的最小单位,region是所有数据的部分,它包含完整的行。当表初写数据时,此时表只有一个Region,当随着数据的增多,Region 开始变大,等到它达到限定的阀值大小时,变化把Region 分裂为两个大小基本相同的Region,而这个阀值就是StoreFile 的设定大小,在第一次分裂Region之前,所有加载的数据都放在原始区域的那台服务器上,随着表的变大,Region 的个数也会相应的增加,所以说Region 是HBase集群分布数据的最小单位。

Store:对应表中的一个列族的存储,是hbase的存储核心。

MemStore:Store中的一个内存缓冲,所有写入Region的数据首先都会写入到MemStore中。

StoreFile:hbase数据所在的地方,数据最终的存储格式,StoreFile也就是HFile。可以简单的认为我们存储的所有数据都存储在HFile中。

思考:

在HBase中,大部分的操作都是在RegionServer完成的,Client端想要插入,删除,查询数据都需要先找到相应的RegionServer。什么叫相应的RegionServer?就是管理你要操作的那个Region的RegionServer。Client本身并不知道哪个RegionServer管理哪个Region,那么它是如何找到相应的RegionServer的?

hbase:meta表

hbase内置的一张特殊目录表,用来保存hbase的regions的位置信息(hbase0.9.6以后的版本meta表的位置信息由zookeeper保存)

rowkey:

1.tableName - rowkey的第一个分隔符前面存的是表名

2.startKey - 第二个分隔符前存的是region的第一个rowkey

3.timestamp - 即regionId,代表region的id,通常基于region创建时的时间戳

数据访问流程

0.9.6以前的版本

Client -> zookeeper ->-ROOT-(单region) -> .META. -> 用户表region

0.9.6以后的版本

Client -> zookeeper -> hbase:meta -> 用户表region

Client会从Zookeeper找到hbase:meta的位置,然后扫描该表找到我们需要请求的region信息,直接跟存储该region的regionserver建立连接,请求数据,而不是经由master。这些信息会被客户端缓存,避免多次请求,但是在master进行balance或者regionserver挂掉的话回去重新获取该信息。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

高级用法

1.rowkey的设计

hbase中有两种基本的键结构,行键(row key)和列键(column key)。咱们重点讨论一下行键的设计。

rowkey的特点

1.类似于主键,用来表示hbase中的唯一的一行记录

2.在hbase中被保存为字节数组,按照字典排序存储

3.用户自己指定rowkey是需要保证rowkey的唯一性,不要重复

rowkey在查询时的三种用法

1.get,根据唯一确定的rowkey进行检索

2.like,通过rowkey的rang来进行匹配

3.scan,通过指定一个startRow和一个stopRow来进行范围匹配(如果不指定范围,将进行全表扫描)

rowkey的作用

了解rowkey的作用之前,咱们先回顾一下前面讲到的region server中的数据构成。region server中有多个region,region中又有多个MemStore和StoreFile用来存储数据。region就是一个个的数据分片,而这些数据分片都有对应的startRow和stopRow,HBase表里面的数据是按照RowKey来分散存储到不同的Region里面的。

总结一下rowkey的作用:

1.读写操作时通过rowkey找到对应的region

2.MemStore和StoreFile中的数据按照rowkey的字典序排序

重点来了!!!

设计rowkey时只要牢记一句话 "避免数据热点问题"!!!

什么叫数据热点???

hbase中如何避免数据热点现象,三种套路

套路一:反转(Reversing)

顾名思义就是将固定长度或者固定数字格式的数据反一反,常见的有数据反转和时间戳反转。

适用场景

咱们在设计rowkey的时候发现rowkey数据分布不均匀,但是却发现rowkey的尾部却能实现数据的均匀分布。比如说时间戳

1622876993000
1622876990000

又或者一个系统中的用户id标识

u1000011
u1000023
u1000178
优缺点:

利于get操作,不利于scan操作,因为打乱了数据的自然排序

套路二:加盐(Salting)

在原RowKey的前面添加固定长度的随机数,也就是给RowKey分配一个随机前缀使它和之前的RowKey的开头不同。

适用场景

rowkey的数据类似,随机性比较低,使用反转法也无法使数据形成随机。

缺点:

加了盐就尝不到原来的味道了,因为给rowkey添加了随机的盐值,导致了一种比较尴尬的情况,查询的时候无法准确的知道随机数是什么。个人感觉这种方法很少会用到。

套路三:哈希(Hashing)

基于RowKey的完整或部分数据进行Hash,而后将哈希后的值完整替换或部分替换原RowKey的前缀部分。这里说的hash常用的有MD5、sha1、sha256 或 sha512 等算法。

适用场景

哈希和加盐的适用场景类似,但是由于加盐方法的前缀是随机数,用原rowkey查询时不方便,因此出现了哈希方法,由于哈希是使用各种常见的算法来计算出的前缀,因此哈希既可以使负载分散到整个集群,又可以轻松读取数据。

优缺点:

跟反转一样

总结

rowkey的设计原则
长度原则:

rowkey本质上一个二进制的流,可以是任意字符。官方建议rowkey的长度越短越好,最好不要超过16个字节。

(16字节是64位操作系统的最佳选择:64位系统,内存8字节对齐,控制在16字节,8字节的整数倍利用了操作系统的最佳特性)

rowkey过长带来的影响

1.影响HFile的存储效率:HBase里的数据在持久化文件HFile中其实是按照Key-Value对形式存储的。这时候如果RowKey很长,比如达到了200byte,那么仅仅1000w行的记录,只考虑RowKey就需占用近2GB的空间,极大的影响了HFile的存储效率。 2.降低检索效率:由于MemStore会缓存部分数据到内存中,如果rowkey比较长,就会导致内存的有效利用率降低,也就不能缓存更多的数据,从而降低检索效率。

唯一原则:

在hbase中,数据采用的是key-value对形式进行存储的,如果向hbase中插入两条相同的rowkey的记录,在你版本数设置为1的情况下,两条数据中先插入的数据会被后插入的数据覆盖掉,可以用mysql的主键或者redis的key来理解。

排序原则:

HBase会把rowkey按照ASCII进行自然有序排序,所以反过来我们在设计rowkey的时候可以根据这个特点来设计完美的rowkey,好好的利用这个特性就是排序原则。

散列原则:

就是上面讲到的rowkey的优化设计,保证数据均匀的分布到region上。

 

2.布隆过滤器

hbase中的高级用法,它能减少特定情况(get/scan)下的查询时间,不过由于这种模式增加了内存和存储的负担,所以被默认为关闭状态。

hbase支持的几种布隆过滤器:

1.行键布隆过滤器

2.列键布隆过滤器

块索引机制

在hbase中,数据最终都会由一个个的HFile文件落在hdfs上,这里说的的块索引机制就是指HFile的块索引。HFIle是由一个个数据块与索引块组成,他们通常默认为64KB。hbase是通过块索引来访问这些数据块的。而索引是由每个数据块的第一行数据的rowkey组成的。当hbase打开一个HFile时,块索引信息会优先加载到内存当中,然后hbase会通过这些块索引来查询数据。

如果用户随机查找一个行键,则这个行键很可能位于两个开始键(即索引)之间的位置。对于hbase来说,它判断这个行键是否真实存在的唯一方法就是加载这个数据块,并且扫描它是否包含这个键。

同时,还存在很多情况使得这种情况更加复杂。

对于一个应用来说,用户通常会以一定的速率进行更新数据,这就将导致内存中的数据被刷写到磁盘中,并且之后系统会将他们合并成更大的存储文件。在hbase的合并存储文件的时候,它仅仅会合并最近几个存储文件,直至合并的存储文件到达配置的最大大小。最终系统中会有很多的存储文件,所有的存储文件都是候选文件,其可能包含用户请求行键的单元格。

我们可以看到,这些不同的文件都来着同一个列族,所以他们的行键分布类似。所以,虽然我们要查询更新的特定行只在某个或者某几个文件中,但是采用块索引方式,还是会覆盖整个行键范围。当块索引确定那些块可能含有某个行键后,regionServer需要加载每一个块来检查该块中是否真的包含该行的单元格。

布隆过滤器的存储位置

当我们选择采用布隆过滤器之后,HBase会在生成StoreFile(HFile)时包含一份布隆过滤器结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。但是在大多数情况下,这些负担相对于布隆过滤器带来的好处是可以接受的。

使用布隆过滤器后数据的查询流程

布隆过滤器 --> MemStore --> HFile

行级布隆过滤器和列级布隆过滤器的选择

如果用户只做行扫描,使用更加细粒度的行加列布隆过滤器不会有任何的帮助,这种场景就应该使用行级布隆过滤器。当用户不能批量更新特定的一行,并且最后的使用存储文件都含有该部分时,行加列级的布隆过滤器更加有用。

 

 

posted @ 2021-05-22 14:54  电影公众号  阅读(371)  评论(0编辑  收藏  举报