HBase 深度历险 | 京东物流技术团队
简介
HBase 的全称是 Hadoop Database,是一个分布式的,可扩展,面向列簇的数据库,是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。本文会像剥洋葱一样,层层剥开她的心。
特点
首先我们看一下 hbase 有哪些特点:
•高性能
基于 LSM 树的数据结构设计,保证了顺序写,并且通过布隆过滤器,compaction 等内部优化手段来优化读性能,使得 hbase 具有很高的读写性能。
•高可靠
hbase 在数据写入之前,会先写入 WAL 预写日志,用来防止机器宕机时,内存中数据的丢失问题。
•易扩展
底层依赖 hdfs,当磁盘空间不足时,可以直接水平扩展。
•稀疏性
稀疏性是 HBase 中的一个突出的特点,在其他数据库中,对于空值的处理一般都会填充 null,对于成百上千万列的表来说,通常会存在大量的空值,如果使用填充 null 的策略,势必会造成大量空间的浪费。而对于 HBase 空值不需要任何填充,因此稀疏性是 HBase 的列可以无限扩展的一个重要的条件。
•列簇存储
使用了列簇存储,可以使用户自由选择哪些列来放在同一个列簇中
•多版本
hbase 支持数据多版本的保存,通过时间戳来排序。用户可以根据需要选择最新版本或者某个历史版本。
什么是列簇存储
前边提到了 hbase 是按照列簇存储来存储数据的,我们先来对比一下行式存储,列式存储以及列簇存储的区别。
•行式存储:
传统的关系型数据库都是按行存储的,也就是每行数据都是存储在一起的。
•列式存储:
数据按列存储,就是每列数据是存储在一起的。按列存储有好处呢?
1.可以节约存储空间,只存储有用的内容,按行存储需要为 null 值进行处理,而按列存储就不需要了。
2.每一列的类型是一致的,在数据落到磁盘时,可以获得更好的数据压缩效率。
3.大多数情况下,我们再查询的时候是不需要查询出整张表的所有列的,只会用到部分列,但是因为按行存储的方式,每次都必须先将所有字段查询出来,在过滤出我们需要的字段。但是按列存储就不一样了,就像是吃自助餐,按需查询,需要什么就只查询什么,很明显也是可以减少磁盘 IO。
•列簇存储:
那什么是列簇存储呢?按列存储是每个列单独存储在一起,这种方式对于字段很多的表来说,如果一次查询设计的列数量太多的话,势必会造成大量的磁盘 IO,从而影响查询性能。而列簇存储的意思就是,一个列簇下边可以存储多个列,每个列簇存储在一个文件里,这样就能减少一些磁盘 IO,从而提升查询性能。
每个列簇的数据存储在一起,每个列簇可以自由选择储存哪些列。所以列簇存储实际上是给用户提供了一种自由选择的权利,如果所有的列都放到一个列簇里,那实际就相当于按行存储一样,每次查询需要把所有的列全部都查出来,而如果每个列单独存一个列簇的话,就像按列存储一样了。
在当前体系中不建议设置太多列簇,后面也会提到,是因为 MemStore 在进行 flush 时候的最小单元不是 MemStore,而是整个 region,因此设置太多的列簇会是 flush 十分耗能,但是这种架构为 HBase 将来演变成 HTAP(Hybrid Transactional and Analytical Processing)系统提供了最核心的基础。
架构原理
HBase 架构整体上分为 3 个部分 Zookeepper 集群,HMaster 以及 Region server。架构图看上去还挺复杂的,但是最核心的是 region server,后边会从 region server -> region -> store -> hfile-> data block,一点点的拨开它的外衣。
1.Zookeeper
Zookeepper 是一个分布式的无中心的元数据存储服务,用来探测和记录 HBase 集群中服务器的状态信息,hmaster 和 region server 通过定时发送心跳和 zk 维持关系。
Zookeepper 中存储了 hbase:meta 表,这张表维护了整个集群所有的 Region 信息,client 首先会访问 zk,查询 hbase:meta 表信息,为了查询性能会将 hbase:meta 表信息缓存在客户端。
2.HMaster
HMaster 在 hbase 中是属于一个 leader 的角色,不参与具体表数据的管理,只负责宏观把控,主要工作内容有两方面,一个是管理 Region server,另一个就执行一些高危操作,比如执行 DDL(建表,删除表等)。
HBase Master 的功能:
•监控所有 RegionServer 的状态,在集群处于数据恢复或者动态调整负载时,分配 Region 到某一个 Region Server 中
•提供 DDL 相关的 API, 新建(create),删除(delete)和更新(update)表结构
3.Region server
Region server 是整个 habse 架构最为核心的模块,负责实际数据的读写,当访问数据时, 客户端与 HBase 的 Region server 直接通信。
HBase 的表根据 Row Key 的区域分成多个 Region, 一个 Region 包含这这个区域内所有数据。而 Region server 负责管理多个 Region, 负责在这个 Region server 上的所有 region 的读写操作,一个 Region server 最多可以管理 1000 个 region。
每个 Region server 都把自己的数据存储在 HDFS 中,如果一个服务器既是 Region server 又是 HDFS 的 Datanode. 那么这个 Region server 的数据会在把其中一个副本存储在本地的 HDFS 中, 加速访问速度。
但是, 如果是一个新迁移来的 Region server, 这个 region server 的数据并没有本地副本. 直到 HBase 运行 compaction, 才会把一个副本迁移到本地的 Datanode 上面。
3.1 Region server 架构图
RegionServer 主要用来响应用户的 IO 请求,是 HBase 中最核心的模块,由 WAL(HLog)、BlockCache 以及多个 Region 构成。
3.2 HLog(WAL)
HLog 其实就是 WAL (Write-Ahead-Log) 预写日志,就是在写入 memstore 之前,会先写到 HLog,给数据来个备份,HLog 是保存在磁盘上的,从而保证高可靠性。HLog 是 region server 级别的,也就是说整个 region server 共用一个 HLog。
HLog 在 hbase 中有两个作用:
•当 region server 宕机的时候,还在内存中未进行写入磁盘的数据不会丢失,仍然可以通过 HLog 日志恢复。
•用于实现 HBase 集群间主从复制,通过回放主集群推送过来的 HLog 日志实现主从复制。
HLog 的日志文件存放在 HDFS 中,hbase 集群默认会在 hdfs 上创建 hbase 文件夹,在该文件夹下有一个 WAL 目录,其中存放着所有相关的 HLog,HLog 并不会永久存在,在整个 HBase 总 HLog 会经历如下过程:
1.发生写入操作的时候会先构建 HLog。
2.因为 HLog 会不断追加,所以整个文件会越来越大,因此需要支持滚动日志文件存储,所以 HBase 后台每间隔一段时间(默认一小时)会产生一个新的 HLog 文件,历史 HLog 标记为历史文件。
3.一旦数据进入到磁盘,形成 HFile 后,HLog 中的数据就没有存在必要了,因为 HFile 存储在 HDFS 中,HDFS 文件系统保障了其可靠性,因此当该 HLog 中的数据都落地成磁盘后,该 HLog 会变为失效状态,对应的操作是将该文件从 WAL 移动到 oldWAl 目录,此时文件依旧存在,并未进行删除。
4.hbase 有一个后台进程,默认每间隔一分钟会对失效日志文件进行判断,如果没有任何引用操作,那么此时的文件会被彻底的从物理删除。
3.3 BlockCache
HBase 会将从 HFile 查询出的数据缓存到 BlockCache,以便后续可以直接从内存读取,减少磁盘 IO。
BlockCache 是 region 级别的,每个 region 只有一个 BlockCache。
HBase 的数据仅仅独立存在于 MemStore 和 HFile 中,BlockCache 中缓存的只是 HFile 中的部分热点数据。
3.4 region
每个 region 由一个或多个 store 组成,region 是集群负载均衡的基本单位,是 MemStore 进行 flush 的基本单元。整个 region 其实就是一个 LSM 树,memstore 对应 C0 树,存储在内存中,作为写缓存,HFile 对应 Cn 树,存储在磁盘上。通过保证顺序写,在牺牲部分读性能的前提下,来大幅度提升写入性能,并且通过布隆过滤器和 compaction 等内部的优化来弥补读性能,从而达到读写性能都很高。
3.4.1 store
每个 store 由 MemStore 和多个 StoreFile 组成,StoreFile 底层实际是 HFile,StoreFile 是 hbase 对 HFile 的封装。每个列簇存储在一个 store,所以有几个列簇,就会有几个 store,列簇太多就会有过多的 MemStore,就会占用过多的内存,并且在进行 flush 的时候,也会造成更大的耗能。
3.4.1.1 MemStore
MemStore 是 hbase 的写缓存,在写入请求时,当写入 HLog 成功之后,变先写入 MemStore,并且会按照 rowkey 字典序进行排序,当达到一定阈值的时候,才会 flush 数据到 hdfs 形成 hfile 文件。hbase 的 MemStore 是采用了跳表这一数据结构,在这里就不过多介绍了。
MemStore 的作用:
•写缓存:攒批数据,批量落盘,减少磁盘 IO,提升性能
•排序:维持数据按照 RowKey 的字典序排列
MemStore 在读写时都起到了很大的作用,最大的耗能操作时在 flush 操作,下边我们详细介绍一下。
Memstore Flush 触发条件
我们已经知道,当 MemStore 的大小达到一定的阈值时,就会 flush 数据到 HDFS 上,形成 hfile 文件,但是需要注意的是,MemStore 进行 flush 的最小操作单元不是 MemStore,而是整个 HRegion。也就是说,有一个 MemStore 需要进行 flush,整个 HRegion 都会受影响,所以一个 HRegion 如果有过多的 Memstore,每次 flush 的开销必然会很大,所以每个表不应该设置过多的列簇。下边介绍一下触发 flush 操作的具体条件:
1.当一个 Region 中某个 MemStore 的大小达到hbase.hregion.memstore.flush.size
(默认值 128MB)时,会触发该 Region 中所有 MemStore Flush,不会阻塞写操作。
2.当一个 Region 中所有 MemStore 的大小总和达到hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size
(默认值 2 * 128 = 256MB)时,会触发该 Region 中所有 MemStore Flush,期间阻塞该 Region 的写操作。
3.当一个 Region 准备下线时的 MemStore 大小总和达到hbase.hregion.preclose.flush.size
(默认值 5MB)时,会触发该 Region 中所有 MemStore Flush,然后 Region 才能关闭。
4.当一个 RegionServer 中所有 MemStore 的大小总和达到hbase.regionserver.global.memstore.size * HBASE_HEAPSIZE
(默认值 0.4 * 堆空间大小)时,会从 MemStore 最大的 Region 开始,触发该 RegionServer 中所有 Region 的 Flush,并阻塞整个 RegionServer 的写操作。直到 MemStore 大小回落到上一个参数值的hbase.regionserver.global.memstore.size.lower.limit
(默认值 0.95)倍,才解除阻塞。
5.当一个 RegionServer 中的 WAL(即 HLog)数量达到hbase.regionserver.maxlogs
(默认值 32)时,HBase 就选取最早的一个 WAL 对应的那些 Region 进行 MemStore Flush,期间也会阻塞对应 Region 的写操作。
6.RegionServer 会定期 Flush MemStore,周期为hbase.regionserver.optionalcacheflushinterval
(默认值 1 小时)。为了避免所有 Region 同时 Flush,定期刷新会有随机的延时。
7.用户可以通过执行flush [table]
或flush [region]
命令来手动 Flush 一张表或一个 Region 的 MemStore。
3.4.1.2 HFile
HFile 是 HBase 存储数据的文件组织形式,参考 BigTable 的 SSTable 和 Hadoop 的 TFile 实现。下图是 HFile 扥物理结构示意图,如图所示,HFile 会被切分为多个大小相等的 block 块,HFile 内部结构还是比较复杂的,有兴趣的同学可以看下(http://hbasefly.com/2016/03/25/hbase-hfile/),本文我们主要看下存储实际数据的 data block。
3.4.1.2.1 DataBlock
DataBlock 是 HBase 中数据存储的最小单元。DataBlock 中主要存储用户的 KeyValue 数据(KeyValue 后面一般会跟一个 timestamp,图中未标出),而 KeyValue 结构是 HBase 存储的核心,每个数据都是以 KeyValue 结构在 HBase 中进行存储。KeyValue 结构磁盘中可以表示为:
每个 KeyValue 都由 4 个部分构成,分别为 key length,value length,key 和 value。其中 key length 和 value length 是两个固定长度的数值,而 key 是一个复杂的结构,首先是 rowkey 的长度,接着是 rowkey,然后是 ColumnFamily 的长度,再是 ColumnFamily,之后是 ColumnQualifier,最后是时间戳和 KeyType(keytype 有四种类型,分别是 Put、Delete、 DeleteColumn 和 DeleteFamily),value 就没有那么复杂,就是一串纯粹的二进制数据。
3.4.1.2.2 基本概念
从上图可以看出一些长度是固定的值,所以把 key 做一些简化,然后重点看下这些内容的具体含义。Key 由 RowKey(行键) + ColumnFamily(列族)+ Column Qualifier(列修饰符)+ TimeStamp(时间戳--版本)+ KeyType(类型)组成,而 Value 就是实际上的值。
•Rowkey:每行数据的主键,是 hbase 的唯一的查询条件,因此 Rowkey 的设计至关重要。
•Column family:列簇,每个列簇的数据存储在一起,每个列簇用户可以自由选择存储哪些列,同一个列簇的所有成员具有相同的列簇前缀,通常将同一类型的列存放在一个列簇下,但是要注意不要设置过多的列簇,后边会讲到。
•Qualifier:列,就是具体的列,以列簇作为前缀,格式为 Column family:Qualifier。
•Timestamp:时间戳,插入单元格时的时间戳,默认作为单元格的版本号。不同版本的数据按照时间戳倒序排序,即最新的数据排在最前面。当数据需要更新的时候,不会向 mysql 一样,直接对源数据就行更新,而是会新增一条数据,新数据的时间戳更大,因此会排在前面。可以根据时间戳来设置 TTL,以及保存的版本个数。
•Type:数据类型,用来区别是不是 delete 的数据,删除数据和更新数据一样,也不会直接进行数据的物理删除,也会新插入一条数据,只是这条数据的 type 会被标记为 delete,在查询是默认会进行过滤。只有在执行 Major Compaction 操作时,才会进行 delete 数据的清理。
•Cell:单元格,在 HBase 中,值作为一个单元保存在单元格中。要定位一个单元,需要满足 “rowkey + column family + qualifier + timestamp+KeyType” 五个要素。每个 cell 保存着同一份数据的多个版本。cell 中没有数据类型,完全是字节存储。
4.读写流程
在介绍读写流程之前,先介绍一下 Meta table,为什么要先介绍它,因为它是读写操作的必经之路,是 rowkey 的引路人。
Meta table
Meta table 存储了所有的 region 信息,Meta table 储存在 zk 中,客户端在第一次读写访问或者 region 失效时,都会先访问 zk,根据 rowkey 从 Meta table 获取 region server 的信息,然后客户端再去访问对应的 region server,进行对应的读写操作。
写流程
1.Client 先访问 zookeeper,获取 Meta table 表位于哪个 Region Server。
2.访问对应的 Region Server,获取 Meta table 表,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 Region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3.客户端直接与目标 Region Server 进行通信。
4.将数据顺序写入(追加)到 HLog(WAL),防止机器宕机而丢失内存中的数据。
5.将数据写入对应的 MemStore,数据会在 MemStore 进行排序。
6.向客户端发送 ack。
7.等达到 MemStore 的触发条件后,将数据 flush 到 HFile。
读流程
1.Client 先访问 zookeeper,获取 Meta table 表位于哪个 Region Server。
2.访问对应的 Region Server,获取 Meta table 表,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 Region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3.客户端直接与目标 Region Server 进行通信。
4.分别在内存中还未落盘的 MemStore 和已经落入磁盘的 HFile 中(对于 HFile 数据优先从缓存在内存中的 BlockCache 读取,读缓存没有再从 HFile 中加载)读取数据,并将查到的所有数据进行合并。注意 hbase 的数据仅仅独立的存在于内存和磁盘中,就是说 MemStore 和 HFile 中的数据一定是全量的数据,而 BlockCache (读缓存)只是 HFile 缓存在内存中的部分热点数据,BlockCache 的作用是在读取 HFile 的数据时,可以直接从内存中读取,减少磁盘 IO 次数。
5.将从文件 HFile 中查询到的数据块 (Block,HFile 数据存储单元,默认大小为 64KB) 缓存到 Block Cache。
6.将合并后的最终结果,然后返回时间最新的数据返回给客户端。
读流程详解
hbase 的写操作十分方便,更新实际上只是新加了一条最新时间戳的数据,删除数据也只是添加了一个 delete 的标记,只有再 Major Compaction 的时候才会进行物理删除。而因为 hbase 中同一个 rowkey 是保存了多版本的数据,以及不同的 keytype 的数据,所以同一个 rowkey 会对应多条数据,因此这里不像我们想的那样,如果 rowkey 命中 BlockCache 或者 MemStore 就会直接返回,远没有想象中的简单,因此也不存在先从 BlockCache 读还是先从 MemStore 中读的概念,这种说法本身就是不对的。
在读取数据的时候,内存 MemStore 和磁盘 HFile 中的数据都要读取,会创建两种类型的 Scanner(StoreFileScanner 和 MemstoreScanner),分别用来探查 HFile 和 MemStore 中的数据,然后会根据用户选择的时间范围以及 rowkey 的范围过滤掉一些 Scanner,最后对于 HFile 的数据会先查找对应的 Block,查找 Block 时会优先从 Blockcache 中查找,找不到再从 HFile 中加载。而 MemstoreScanner 会从 Memstore 中查询数据,最后将内存和磁盘中的数据进行合并,返回最新的数据给到客户端。简化的流程如下:
1.构建 Scanner
每个 StoreScanner 会为当前该 Store 中每个 HFile 构造一个 StoreFileScanner,用于实际执行对应文件的检索。同时会为对应 Memstore 构造一个 MemstoreScanner,用于执行该 Store 中 Memstore 的数据检索。
1.过滤 Scanner
根据 Time Range 以及 RowKey Range 对 StoreFileScanner 以及 MemstoreScanner 进行过滤,淘汰肯定不存在待检索结果的 Scanner。
1.Seek rowkey
所有 StoreFileScanner 开始做准备工作,在负责的 HFile 中定位到满足条件的起始 Row。Seek 过程(此处略过 Lazy Seek 优化)也是一个很核心的步骤,它主要包含下面三步:
•定位 Block Offset:在 Blockcache 中读取该 HFile 的索引树结构,根据索引树检索对应 RowKey 所在的 Block Offset 和 Block Size
•Load Block:根据 BlockOffset 首先在 BlockCache 中查找 Data Block,如果不在缓存,再在 HFile 中加载
•Seek Key:在 Data Block 内部通过二分查找的方式定位具体的 RowKey。
6.HBase Compaction
HBase 的 MemStore 在达到触发条件的时候,会把 MemStore 中的数据 flush 到 HDFS 上,每次都会形成一个新的 HFile 文件,所以随着时间的不断累积,同一个 Store 下的 HFile 会越来越多,进而会降低 HBase 查询性能,主要体现在查询数据的 IO 次数增加。为了优化查询性能,HBase 会合并小的 HFile 以减少文件数量,这种合并 HFile 的操作称为 Compaction。
•Minor Compaction:会将邻近的若干个 HFile 合并,在合并过程中会清理 TTL 的数据,但不会清理被删除的数据。Minor Compaction 消耗的资源较少,通过少量的 IO 减少文件的个数,提升读取操作的性能,适合高频率地跑。
•Major Compaction:会将一个 Store 下的所有 HFile 进行合并,并且会清理掉过期的和被删除的数据,即在 Major Compaction 会删除全部需要删除的数据。一般情况下,Major Compaction 时间会持续比较长,整个过程会消耗大量系统资源,对上层业务有比较大的影响。因此,生产环境下通常关闭自动触发 Major Compaction 功能,改为手动在业务低峰期触发。
HBase 与传统关系型数据库的对比
•数据类型:
没有数据类型,都是字节数组(有一个工具类 Bytes,将 java 对象序列化为字节数组),而传统的关系型数据库有丰富的数据类型。
•数据操作:
HBase 只有很基本的插入、查询、删除等操作,表和表之间是没有关系的,而传统数据库一般都会有很多的函数,并且表之间有关联的特点。
•存储模式:
Hbase 基于列簇存储,而传统关系型数据库是基于行存储。
•数据更新:
habse 中对于数据 update 情况,不会向传统数据库一样,直接修改这条记录,而是新插入了一条状态为 delete 的数据,这么设计的原因是为了保证顺序写,提高数据 IO 效率,进而提高性能。并且旧数据也不会马上删除,会在 compaction 时才会进行删除。
•时间版本:
Hbase 数据写入 cell 时,还会附带时间戳,默认为数据写入时 RegionServer 的时间,但是也可以指定一个不同的时间。数据可以有多个版本,并且可以设置 TTL,以及保留的版本个数。
•索引:mysql 支持多种类型的索引,而 hbase 只有 rowkey
•易扩展性
搞过 mysql 分库分表的同学一定体会过它的魅力,十分的麻烦,但是 hbase 可以动态水平扩展,十分便捷
Rowkey 的设计
访问方式
HBase 访问只有 3 种方式
1.通过单个 rowkey 访问
2.通过 rowkey 的 range
3.全表扫描
RowKey 设计原则
•长度原则
RowKey 是一个二进制码流,可以是任意字符串,最大长度为 64kb,实际应用中一般为 10-100byte,以 byte[]形式保存,一般设计成定长。建议越短越好,不要超过 16 个字节
•唯一原则
必须在设计上保证 RowKey 的唯一性。由于在 HBase 中数据存储是 Key-Value 形式,若向 HBase 中同一张表插入相同 RowKey 的数据,则原先存在的数据会被新的数据覆盖。
•排序原则
HBase 的 RowKey 是按照 ASCII 有序排序的
•散列原则
RowKey 应均匀的分布在各个 HBase 节点上,防止热点数据造成数据倾斜。
避免数据热点的方法
•Reversing
顾名思义,直接反转的意思。比如用户 id,手机号等,rowkey 的头部不具有随机性,但是尾部具有良好的随机性,那这时我们可以将 rowkey 直接翻转过来。Reversing 可以有效的使 RowKey 随机分布,但是牺牲了 RowKey 的有序性。利于 Get 操作,但不利于 Scan 操作,因为数据在原 RowKey 上的自然顺序已经被打乱。
•Salting
Salting(加盐)的原理是在原 RowKey 的前面添加固定长度的随机数,从而保障数据在所有 Regions 间的负载均衡。
•Hashing
Hashing 和加盐是类似的,只是 Hashing 要求前缀不能是随机的,需要使用一些 hash 算法,这样客户端可以重构出 Hashing 后的 rowkey。
推荐方案:
一般情况,可以选择业务主键的后 3 位取余 hbase 分区数的余数作为加盐的前缀,然后在拼接上业务主键作为 rowkey。
HBase 的缺点
•数据分析能力弱:数据分析是 HBase 的弱项,比如聚合运算、多维度复杂查询、多表关联查询等。所以,我们一般在 HBase 之上架设 Phoenix 或 Spark 等组件,增强 HBase 数据分析处理的能力。
•原生不支持二级索引:默认 HBase 只对 rowkey 做了单列索引,因此正常情况下对非 rowkey 列做查询比较慢。所以,我们一般会选择一个 HBase 二级索引解决方案,目前比较成熟的解决方案是 Phoenix,此外还可以选择 Elasticsearch/Solr 等搜索引擎自己设计实现。
•原生不支持 SQL:SQL 查询也是 HBase 的一个弱项,好在这块可以通过引入 Phoenix 解决,Phoenix 是专为 HBase 设计的 SQL 层。
•HBase 原生不支持全局跨行事务,只支持单行事务模型。同样,可以使用 Phoenix 提供的全局事务模型组件来弥补 HBase 的这个缺陷。
•故障恢复时间长
注意点总结
1.设计表结构的时候,一定要注意列簇的个数不要设置的过多,不要超过 3 个,最好就只有一个
2.rowkey 的设计一定要注意加盐或 hash 处理,避免出现热点数据的问题
3.Major Compaction 默认开启需要关闭,因为十分耗费资源,影响写入,可以在业务低峰期手动执行
4.MemStore 的 flush 的触发条件,一定要关注 Region Server 中所有 Memstore 是否达到阈值这个条件,因为这个会影响整个 Region Server 的写入
5.BlockCache 一定不要理解偏了,hbase 中的数据,要么还在写缓存 MemStore 中未写入磁盘,要么就是已经写入磁盘的数据,这两部分一定包含了全量的数据,BlockCache 只是 HFile 缓存在内存中的部分热点数据。
Q&A
1. 常说 HBase 数据读取要读 Memstore、HFile 和 Blockcache,为什么上面 Scanner 只有 StoreFileScanner 和 MemstoreScanner 两种?没有 BlockcacheScanner?
HBase 中数据仅仅独立地存在于 Memstore 和 StoreFile 中,Blockcache 中的数据只是 StoreFile 中的部分数据(热点数据),即所有存在于 Blockcache 的数据必然存在于 StoreFile 中。因此 MemstoreScanner 和 StoreFileScanner 就可以覆盖到所有数据。实际读取时 StoreFileScanner 通过索引定位到待查找 key 所在的 block 之后,首先检查该 block 是否存在于 Blockcache 中,如果存在直接取出,如果不存在再到对应的 StoreFile 中读取。
2. 数据更新操作先将数据写入 Memstore,再落盘。落盘之后需不需要更新 Blockcache 中对应的 kv?如果不更新,会不会读到脏数据?
如果理清楚了第一个问题,相信很容易得出这个答案:不需要更新 Blockcache 中对应的 kv,而且不会读到脏数据。数据写入 Memstore 落盘会形成新的文件,和 Blockcache 里面的数据是相互独立的,以多版本的方式存在。
参考资料:
https://my.oschina.net/u/4511602/blog/4916109
https://www.jianshu.com/p/f911cb9e42de
https://www.jianshu.com/p/0e178bce8f63
http://hbasefly.com/2016/12/21/hbase-getorscan/
http://hbasefly.com/2017/06/11/hbase-scan-2/
https://zhuanlan.zhihu.com/p/145551967
https://blog.csdn.net/u012151684/article/details/109040581
https://xie.infoq.cn/article/76f8caba743f8be2beb81441d
https://zhuanlan.zhihu.com/p/159052841?utm_id=0
http://hbasefly.com/2016/04/03/hbase_hfile_index/
http://hbasefly.com/2016/03/25/hbase-hfile/
作者:京东物流 于建飞
来源:京东云开发者社区 自猿其说 Tech 转载请注明来源
评论