HBase的读写底层原理
简单阐述以下HBase读写高性能的底层原理
HBase会将实时数据保存到内存(MemStore–Region维度的)中,在内存中的数据是有序的,如果内存空间满了,会刷写到HFile中,而在HFile中保存的内容也是有序的。当数据写入HFile后,内存中的数据会被丢弃。
hbase的数据存储方式和elasticsearch非常相似:除非hbase系统发起的合并文件命令,HFile一旦创建,不能修改,其原理基于hadoop的hdfs,这里不阐述了,如果是update命令就会拆解为delete+insert命令,在memstore中新增一条新数据,标记原始数据的删除标志。
随着数据的增多HFile页文件也会多,其中会包括一些update造成的冗余数据。当到达一定条件,后台线程会合并小文件组成大文件,这样磁盘查找会限制在少数几个数据存储文件中。HBase的写入速度快是因为它其实并不是真的立即写入文件中,而是先写入内存,随后异步刷入HFile(其实现在的数据存储中间件都是利用的这种方式,比如mysql数据库的redoLog+bufferPool,es的内核缓冲区缓冲segmentFile和transLog,像kafka,rocketmq也是利用这种方式)。所以在客户端看来,写入速度很快。另外,写入时候将随机写入转换成顺序写,数据写入速度也很稳定。
而读取速度快是因为它使用了LSM树型结构,而不是B或B+树。磁盘的顺序读取速度很快,但是相比而言,寻找磁道的速度就要慢很多。HBase的存储结构导致它需要磁盘寻道时间在可预测范围内,并且读取与所要查询的rowkey连续的任意数量的记录都不会引发额外的寻道开销。比如有5个存储文件,那么最多需要5次磁盘寻道就可以。而关系型数据库,即使有索引,也无法确定磁盘寻道次数。而且,HBase读取首先会在缓存(BlockCache)中查找,它采用了LRU(最近最少使用算法),如果缓存中没找到,会从内存中的MemStore中查找,只有这两个地方都找不到时,才会加载HFile中的内容,而上文也提到了读取HFile速度也会很快,因为节省了寻道开销。
举例:
A:如果快速查询(从磁盘读数据),hbase是根据rowkey查询的,只要能快速的定位rowkey, 就能实现快速的查询,主要是以下因素:
1、hbase是可划分成多个region,你可以简单的理解为关系型数据库的多个分区。
2、键是排好序了的
3、按列存储的
B:实时查询
HBase的机制是数据先写入到内存中,当数据量达到一定的量(如128M),再写入磁盘中, 在内存中,是不进行数据的更新或合并操作的,只增加数据,这使得用户的写操作只要进入内存中就可以立即返回,保证了HBase I/O的高性能,用户刚新增的数据可以马上查到,联系hbase的数据存储原理,新增的数据在内存中,还未被刷进磁盘,自然能快速找到。
hbase的读写过程
(1)读流程:
- 客户端通过zookeeper以及-root-表和.mate.表找到目标数据所在的regionserver(寻址)
- 联系regionserver查询目标数据
- Region先在memstore中查找,命中则返回
- 如果memstore找不到,则在storefile中扫描 , 为了能快速的判断要查询的数据在不在这个 StoreFile 中,应用了 BloomFilter(布隆过滤),如果不存在则开始下个storeFile的搜索,如果布隆过滤结果存在(布隆过滤是有概率错误的),则使用二分法寻找所需列簇。
(2)写流程:
- Client先根据rowkey找到对应的region所在的regionserver(寻址)
- Client向regionserver提交请求
- Regionserver找到目标region
- Regionserver检查数据是否与 Schema 一致
- 如果客户端没有指定版本,则获取当前系统时间作为数据版本
- 将更新写入Hlog **
- 将数据写入memstore**
- 判断memstore的是否需要flush为storefile
LSM优化方式:
Bloom filter: 就是个带随机概率的bitmap,可以快速的告诉你,某一个小的有序结构里有没有指定的那个数据的。于是就可以不用二分查找,而只需简单的计算几次就能知道数据是否在某个小集合里啦。效率得到了提升,但付出的是空间代价。
compact:小树合并为大树:因为小树性能有问题,所以要有个进程不断地将小树合并到大树上,这样大部分的老数据查询也可以直接使用log2N的方式找到,不需要再进行(N/m)*log2n的查询了
总结:
用到HBase基本都是insert操作,像日志这种基本不会去改,所以在内存中直接新增当内存达到一定数量的时候,刷入磁盘,这边应该是直接创建一个storeFile,有点类似elasticsearch的segementfile,这个storefile是根据rowkey排过序的。当读取的时候,如果要查找的是范围内的,那就要遍历所有的storefile了,但是这里有用到二分查询和布隆过滤,效率还是可以的,后期当storefile数量到达阈值时合并成多个大的storefile可以减少磁盘寻道,都在一个文件里了。(如果确实有delete和update操作,应该也是类似es,将对应rowkey的之前的数据标记为删除,读取的时候全部取出根据是否删除过滤)
HBase使用blockStore来将读取的数据缓存在内存中(类似mysql的buffer pool),以便后续查找同样或者相邻的数据,blockStore是regionServer级别的。
还有Hbase的实时查询,说的是刚插入的数据能马上查到,因为刚插入的数据在内存种,一般都能直接查到,并不是说对很早以前的数据也能实现实时查询。