学习笔记(15-16章)

  • 1.HBase 2.x核心技术
  • 1.1.Procedure
  • 1.1.1.Procedure定义
  • 1.1.2.Procedure Yield
  • 1.2.In Memory Compaction
  • 1.2.1.Segment概念
  • 1.2.2.开启方式
  • 2.高级话题
  • 2.1.二级索引
  • 2.1.1.优劣比较
  • 2.2.HBase开发与测试
  • 2.2.1.HBase社区运作机制
  • 2.3.HBase相关网站
  • 3.总结


本博客内容基本整理自《Hbase原理与实践》一书。仅用于个人学习和积累。

1.HBase 2.x核心技术

HBase 2.x版本是迄今为止改动最大的一个版本,主要包含的核心功能如下:

  • 基于Procedure v2重新设计了HBase的Assignment Manager和核心管理流程。通过Procedure v2,HBase能保证各核心步骤的原子性,从设计上解决了分布式场景下多状态不一致的问题。
  • 实现了In Memory Compaction功能。该功能将MemStore分成若干小数据块,将多个数据块在MemStore内部做Compaction,一方面缓解了写放大的问题,另一方面降低了写路径的GC压力。
  • 存储MOB数据。2.0.0版本之前对大于1MB的数据支持并不友好,因为大value场景下Compaction会加剧写放大问题,同时容易挤占HBase的BucketCache。而新版本通过把大value存储到独立的HFile中来解决这个问题,更好地满足了多样化的存储需求。
  • 读写路径全链路Offheap化。在2.0版本之前,HBase只有读路径上的BucketCache可以存放Offheap,而在2.0版本中,社区实现了从RPC读请求到完成处理,最后到返回数据至客户端的全链路内存的Offheap化,从而进一步控制了GC的影响。
  • 异步化设计。异步的好处是在相同线程数的情况下,提升系统的吞吐量。2.0版本中做了大量的异步化设计,例如提供了异步的客户端,采用Netty实现异步RPC,实现asyncFsWAL等。

1.1.Procedure

在HBase 2.0版本之前,系统存在一个潜在的问题:HBase的元信息分布在ZooKeeper、HBase Meta表以及HDFS文件系统中,而HBase的分布式管理流程并没法保证操作流程的原子性,因此,容易导致这三者之间的不一致。
HBase 2.0引入了Procedure v2的设计。本质上是通过设计一个分布式任务流框架,来保证这个任务流的多个步骤全部成功,或者全部失败,即保证分布式任务流的原子性。

1.1.1.Procedure定义

一个Procedure一般由多个subtask组成,每个subtask是一些执行步骤的集合,这些执行步骤中又会依赖部分Procedure。
Procedure提供的两个接口:execute()和rollback(),其中execute()接口用于实现Procedure的执行逻辑,rollback()接口用于实现Procedure的回滚逻辑。这两个接口的实现需要保证幂等性。

1.1.2.Procedure Yield

Procedure v2框架还提供了另一种处理重试的方式——把当前异常的Procedure直接从调度队列中移走,并将Procedure添加到调度队列队尾。等待前面所有的Procedure都执行完成之后,再执行上次有异常的Procedure,从而达到重试的目的。

1.2.In Memory Compaction

为了实现更高的写入吞吐和更低的延迟,社区团队对MemStore做了更细粒度的设计。这里,主要指的就是In Memory Compaction。

1.2.1.Segment概念

Segment本质上是维护一个有序的cell列表。根据cell列表是否可更改,Segment可以分为两种类型。

  • MutableSegment :该类型的Segment支持添加cell、删除cell、扫描cell、读取某个cell等操作。因此一般使用一个ConcurrentSkipListMap来维护列表。
  • ImmutableSegment :该类型的Segment只支持扫描cell和读取某个cell这种查找类操作,不支持添加、删除等写入操作。因此简单来说,只需要一个数组维护即可。

注意:无论是何种类型的Segment,都需要实时保证cell列表的有序性。

1.2.2.开启方式

有两种方式可以开启In Memory Compaction功能。第一种是在服务端的hbase-site.xml添加如下配置,此配置对集群中所有的表有效:

hbase.hregion.compacting.memstore.type=BASIC   #可选择NONE/BASIC/EAGER三种

当然,也可以针对某个表的给定Column Family打开In Memory Compaction,代码如下:

create 'test', {NAME=> 'cf', IN_MEMORY_COMPACTION=> 'BASIC'} #NONE/BASIC/EAGER

注意,这里IN_MEMORY_COMPACTION三个取值的含义如下:

  • NONE,系统默认值,表示不开启In-Memory Compaction,仍使用之前默认的DefaultMemstore
  • BASIC,表示开启In-Memory Compaction,但是在ImmutableSegment做Compaction的时候,并不会走ScanQueryMatcher过滤无效数据,同时cell指向的内存数据不会发生任何移动。可以认为是一种轻量级的内存Compaction。
  • EAGER,表示开启In-Memory Compaction。在ImmutableSegment做Compaction时,会通过ScanQueryMatcher过滤无效数据,并重新整理cell指向的内存数据,将其拷贝到一个全新的内存区域。可以认为是一种开销比BASIC的更大,但是更彻底的Compaction。所谓无效数据包括TTL过期的数据、超过Family指定版本的cell,以及被用户删除的cell。

从原理上说,如果表中存在大量特定行的数据更新,则使用EAGER能获得更高的收益,否则使用BASIC

2.高级话题

2.1.二级索引

二级索引是开发者常用的一个数据库功能,但是社区版HBase并不支持二级索引功能。书中介绍了如下两种设计二级索引额思路。

  • 局部二级索引:所谓局部二级索引是指在Region内部建立二级索引,同时保证主表和索引数据的一致性。
  • 全局二级索引:全局二级索引,是为整个表的某个字段建立一个全表级别的索引。
2.1.1.优劣比较

局部二级索引的写入性能很高,因为直接通过Region内部的原子Batch操作即可完成,但是读取的时候需要考虑每个Region是否包含对应的数据,适合写多读少的场景。全局二级索引的写入需要通过分布式事务协议来保证跨Region跨行事务的原子性,实现比局部二级索引复杂很多,写入的性能要比局部二级索引差很多,但是读取性能好很多,适合写少读多的场景。

2.2.HBase开发与测试

HBase项目是由Apache软件基金会负责发行维护的,HBase社区的核心成员分布在世界各地,HBase通过如下的社区运作机制保证了项目的正常运作,也保证了相对高质量的代码。现在很多开源项目模式基本也是如此。

2.2.1.HBase社区运作机制

HBase社区根据每位工程师对HBase项目的贡献大小,将工程师分成三个级别:PMC、Committer、Contributor。各级别工程师的职责和权限如下:

  • HBase Contributor :为HBase项目贡献feature、修复bug、编写文档、添加或修改测试代码、在hbase-user邮件列表中回答用户问题、推广HBase和扩大社区影响力等。一般来说,只要是为HBase项目做了贡献的,无论贡献大小,都称之为Contributor。而且,社区非常欢迎各路人才成为HBase社区的Contributor。但一般来说,Contributor没有HBase项目的git写权限,需要在Committer或PMC的帮助下,修改才能合并到git仓库。
  • HBase Committer :当某位Contributor的贡献量到达一定程度,并被社区PMC成员认可,该Contributor就会被社区成员提名为Committer。Committer拥有HBase项目git仓库的写权限,他(她)可以为其他Contributor做Code Review,待review通过后执行+1操作,并帮助提交代码到git仓库中。在社区决策投票(例如Release版本发布、依赖升级、功能合并等)中,Committer的投票具有较大的影响力。
  • HBase PMC :包含Committer所有的权利。此外,还负责HBase项目核心功能的设计和审查、发布Release版本、提名活跃Contributor晋升等。他们是社区权限最高的成员。

HBase代码合并流程:

HBase PMC 成员 hbase procedure_HBase PMC 成员

3.总结

今天阅读了《HBase原理与实践》的最后两章,其中HBase 2.x核心技术中的Procedure相关的内容对我来说帮助很大。因为目前工作上用到的HBase版本就已经是2.x了,而关于Procedure功能的相关资料目前相对缺乏,之前在整理HBase空洞问题的时候就了解到了HBase 2.x版本之后数据不一致问题在实现了Procedure之后就很少会出现了,但是并不清楚这个东西到底是何方神圣,今天终于在书中找到了答案。Procedure功能其实就是保证了HBase任务的原子性,在这个功能实现之前,HBase元数据分布在ZK,meta表和HDFS文件中,某一方出现问题就会导致数据出现不一致的情况,而Procedure就保证了执行的任务在操作某一方出现问题时,这个任务就全部失败,不会再出现之前部分成功,部分失败的情况,严格地保证了任务流的原子性。今天还了解到了HBase社区的运行机制,也意识到了测试在一个项目中的重要性,一个没有自动测试代码的项目,随着代码库的日渐增长,未来开发新功能所付出的人工测试成本会是极其巨大的。所以之后自己不仅要注重自己的代码质量,也要多注意为自己写的代码编写相应的测试代码,养成良好的习惯,向大佬们看齐。