黑马程序员技术交流社区

标题: 【上海校区】HBase内部核心原理剖析 [打印本页]

作者: 玩转曼哈顿    时间: 2019-2-25 11:39
标题: 【上海校区】HBase内部核心原理剖析
本帖最后由 玩转曼哈顿 于 2019-2-25 16:03 编辑

一、数据模型
1.重要概念回顾
2.几个小问题HBase是否支持表关联?
官方给出的答案是干脆的,那就是“不支持”。如果想实现数据之间的关联,就必须自己去实现了,这是挑选 NoSQL 数据库必须付出的代价。
HBase 是否支持 ACID?
ACID 就是 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)的首字母缩写,ACID 是事务正确执行的保证,HBase 部分支持 了 ACID。
表命名空间有什么用?
表命名空间主要是用来对表分组,那么对表分组有什么用?命名空间可以填补 HBase 无法在一个实例上分库的缺憾,通过命名空间我们可以像关系型数据库一样将表分组,对于不同的组进行不同的环境设定,比如配额管理、安全管理等。
HBase 中有两个保留表空间是预先定义好的:
二、HBase 的存储数据方式
1.架构回顾
一个 HBase 集群由一个 Master(也可以把两个 Master 做成 HighAvailable)和多个 RegionServer 组成。
2.RegionServer 内部架构
一个 RegionServer 包含有:
3.Region 内部架构
每一个 Region 内都包含有多个 Store 实例,一个 Store 对应一个列族的数据,如果一个表有两个列族,那么在一个 Region 里面就有两个 Store,Store 内部有 MemStore 和 HFile 这两个组成部分。

4.预写日志(WAL)
预写日志(Write-ahead log,WAL)就是设计来解决宕机之后的操作恢复问题的,数据到达 Region 的时候是先写入 WAL,然后再被加载到 Memstore,就算 Region 的机器宕掉了,由于 WAL 的数据是存储在 HDFS 上的,所以数据并不会丢失。
WAL 是默认开启的,可以通过下面的代码关闭 WAL。
Mutation.setDurability(Durability.SKIP_WAL);
Put、Append、Increment、Delete 都是 Mutation 的子类,所以他们都有 setDurability 方法,这样可以让该数据操作快一点,但是最好不要这样做,因为当服务器宕机时,数据就会丢失。
如果你实在想不惜通过关闭 WAL 来提高性能,可以选择异步写入 WAL。
Mutation.setDurability(Durability.ASYNC WAL);这样设定后 Region 会等到条件满足的时候才把操作写入 WAL,这里提到的条件主要指的是时间间隔 hbase.regionserver.optionallogflushinterval,这个时间间隔的意思是 HBase 间隔多久会把操作从内存写入 WAL,默认值是 1s。
如果你的系统对性能要求极高、对数据一致性要求不高,并且系统的性能瓶颈出现在 WAL 上的时候,你可以考虑使用异步写入 WAL,否则,使用默认的配置即可。

5.WAL 滚动
WAL 是一个环状的滚动日志结构,因为这种结构写入效果最高,而且可以保证空间不会持续变大。
WAL 的检查间隔由 hbase.regionserver.logroll.period 定义,默认值为 1h。检查的内容是把当前 WAL 中的操作跟实际持久化到 HDFS 上的操作比较,看哪些操作已经被持久化了,被持久化的操作就会被移动到 .oldlogs 文件夹内(这个文件夹也是在 HDFS 上的)。
一个 WAL 实例包含有多个 WAL 文件,WAL 文件的最大数量通过 hbase.regionserver.maxlogs(默认是 32)参数来定义。

其他的触发滚动的条件是
WAL 文件被创建出来后会放在 /hbase/.log 下(这里说的路径都是基于 HDFS),一旦 WAL 文件被判定为要归档,则会被移动到 /hbase/.oldlogs 文件夹。Master 会负责定期地去清理 .oldlogs 文件夹,判断的条件是“没有任何引用指向这个 WAL 文件”。目前有两种服务可能会引用 WAL 文件:
6.Store 内部结构WAL 是存储在 HDFS 上的,Memstore 是存储在内存中的,HFile 又是存储在 HDFS 上的;数据是先写入 WAL,再被放入 Memstore,最后被持久化到 HFile 中。数据在进入 HFile 之前已经被存储到 HDFS 一次了,为什么还需要被放入 Memstore?
这是因为 HDFS 上的文件只能创建、追加、删除,但是不能修改。对于一个数据库来说,按顺序地存放数据是非常重要的,这是性能的保障,所以我们不能按照数据到来的顺序来写入硬盘。
可以使用内存先把数据整理成顺序存放,然后再一起写入硬盘,这就是 Memstore 存在的意义。虽然 Memstore 是存储在内存中的,HFile 和 WAL 是存储在 HDFS 上的,但由于数据在写入 Memstore 之前,要先被写入 WAL,所以增加 Memstore 的大小并不能加速写入速度。Memstore 存在的意义是维持数据按照 rowkey 顺序排列,而不是做一个缓存。
7.MemStore
设计 MemStore 的原因有以下几点:
不过不要想当然地认为读取也是先读取 Memstore 再读取磁盘哟!读取的时候是有专门的缓存叫 BlockCache,这个 BlockCache 如果开启了,就是先读 BlockCache,读不到才是读 HFile+Memstore。
8.HFile(StoreFile)
HFile 是数据存储的实际载体,我们创建的所有表、列等数据都存储在 HFile 里面。HFile 是由一个一个的块组成的,在 HBase 中一个块的大小默认为 64KB,由列族上的 BLOCKSIZE 属性定义。这些块区分了不同的角色:
其实叫 HFile 或者 StoreFile 都没错,在物理存储上我们管 MemStore 刷写而成的文件叫 HFile,StoreFile 就是 HFile 的抽象类而已。
9.Data 数据块
Data 数据块的第一位存储的是块的类型,后面存储的是多个 KeyValue 键值对,也就是单元格(Cell)的实现类,Cell 是一个接口,KeyValue 是它的实现类。

10.KeyValue 类
一个 KeyValue 类里面最后一个部分是存储数据的 Value,而前面的部分都是存储跟该单元格相关的元数据信息。如果你存储的 value 很小,那么这个单元格的绝大部分空间就都是 rowkey、column family、column 等的元数据,所以大家的列族和列的名字如果很长,大部分的空间就都被拿来存储这些数据了。
不过如果采用适当的压缩算法就可以极大地节省存储列族、列等信息的空间了,所以在实际的使用中,可以通过指定压缩算法来压缩这些元数据。不过压缩和解压必然带来性能损耗,所以使用压缩也需要根据实际情况来取舍。如果你的数据主要是归档数据,不太要求读写性能,那么压缩算法就比较适合你。
三、增删查改的真正面目
HBase 是一个可以随机读写的数据库,而它所基于的持久化层 HDFS 却是要么新增,要么整个删除,不能修改的系统。那 HBase 怎么实现我们的增删查改的?真实的情况是这样的:HBase 几乎总是在做新增操作。
由于数据库在使用过程中积累了很多增删查改操作,数据的连续性和顺序性必然会被破坏。为了提升性能,HBase 每间隔一段时间都会进行一次合并(Compaction),合并的对象为 HFile 文件。
合并分为 minor compaction 和 major compaction,在 HBase 进行 major compaction 的时候,它会把多个 HFile 合并成 1 个 HFile,在这个过程中,一旦检测到有被打上墓碑标记的记录,在合并的过程中就忽略这条记录,这样在新产生的 HFile 中,就没有这条记录了,自然也就相当于被真正地删除了。
四.HBase 数据结构总结
HBase 数据的内部结构大体如下:
五.KeyValue 的写入和读出
1.写入
一个 KeyValue 被持久化到 HDFS 的过程的如下:

2.读出
由于有 MemStore(基于内存)和 HFile(基于HDFS)这两个机制,你一定会立马想到先读取 MemStore,如果找不到,再去 HFile 中查询。这是显而易见的机制,可惜 HBase 在处理读取的时候并不是这样的。实际的读取顺序是先从 BlockCache 中找数据,找不到了再去 Memstore 和 HFile 中查询数据。

墓碑标记和数据不在一个地方,读取数据的时候怎么知道这个数据要删除呢?如果这个数据比它的墓碑标记更早被读到,那在这个时间点真是不知道这个数据会被删 除,只有当扫描器接着往下读,读到墓碑标记的时候才知道这个数据是被标记为删除的,不需要返回给用户。

所以 HBase 的 Scan 操作在取到所需要的所有行键对应的信息之后还会继续扫描下去,直到被扫描的数据大于给出的限定条件为止,这样它才能知道哪些数据应该被返回给用户,而哪些应该被舍弃。所以你增加过滤条件也无法减少 Scan 遍历的行数,只有缩小 STARTROW 和 ENDROW 之间的行键范围才可以明显地加快扫描的速度

在 Scan 扫描的时候 store 会创建 StoreScanner 实例,StoreScanner 会把 MemStore 和 HFile 结合起来扫描,所以具体从 MemStore 还是 HFile 中读取数据,外部的调用者都不需要知道具体的细节。当 StoreScanner 打开的时候,会先定位到起始行键(STARTROW)上,然后开始往下扫描。
其中红色块部分都是属于指定 row 的数据,Scan 要把所有符合条件的 StoreScanner 都扫描过一遍之后才会返回数据给用户。
六.Region 的定位
Region 的查找,早期的设计(0.96.0)之前是被称为三层查询架构:

-ROOT- 表记录在 ZooKeeper 上,路径为:/hbase/root-region-server;Client 查找数据的流程从宏观角度来看是这样的:

新版 Region 查找流程:












作者: 不二晨    时间: 2019-3-4 11:25
感谢分享




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2