HBase作为BigTable的开源实现(之一,但也是应用最广的),其架构应该很多人即使完全没看过HBase的代码也都很清楚,毕竟干这个的几乎没人没读过BigTable的论文。但一个系统除了最基础的架构外还需要有一些细节的优化和实用的功能,很多功能大家就不见得了解了。因此感觉有必要介绍下最近两年HBase新增的一些比较重要的功能。以0.98为基准,在此假设0.98所包含的功能是大家所了解的,毕竟0.98.0是2014年2月发布了的。
HBase 1.0.0 在2015年2月发布后,首先最直观的改变是版本号采用了semantic versioning的语义,a.b.c的版本号,a major version变了不保证兼容性(尽量保证可以rolling update),b minor version变了可能会加feature并且对用户公开的API可能会加method(但不会减少,也就是调用API的升级后还能编译,但实现interface的class可能无法再编译),c patch version只负责bugfix。因此现在已经发布了1.0.x、1.1.x、1.2.x三个minor version版本,1.1和1.2分别增加了一些新功能(稍后再说)。而1.0目前已经不再维护,1.1最新版为1.1.7,1.2的最新版为1.2.4,每个分支大概每个月会发一个patch version,等稳定了可能频率会低。
从开发的角度讲,现在1.0之后一共有如下git branch:master、branch-1、branch-1.3、branch-1.2、branch-1.1、branch-1.0,除了最后一个外剩下的5个分支都还在维护。master是为2.x.x准备的,branch-1.3是为了1.3准备的,1.3.0应该也快发布了,发布后branch-1会fork出来一个branch-1.4作为1.4的分支。开出独立的分支类似于其他项目的feature freeze但又不完全是,一般来说不再添加新的大feature,但小feature可能还是会加。这也意味着1.3发布后,1.4会有哪些功能已经基本确定了,假设每个minor分支的发布周期是X个月,那就意味着某个新feature提交后最少X个月才能在正式发布的版本中出现,极端情况是2X个月……HBase的开发是一律先提交给master然后根据更低版本的version是否需要而backport;相对应的Cassandra是先改需要提交的最低版本然后逐渐往上提交直到trunk。不过master因为是为2.0准备的,并且现在所有feature都一定会提交到master,所以如果开个branch-2、branch-2.0并且发布2.0.0,那么理论上有可能同样的feature在1.x中还没发布……
接下来说具体的新特性。
1.0最大的变化是master变成了一个特殊的region server。在之前master并不能接收客户端的读写请求,只负责作为一个协调者做一些集群状态相关的操作,即使是meta表也是放在RS上的,由master指定放在何处。1.0开始HMaster类直接继承自HRegionServer,理论上可以往上面放region了,实际上一般来说放普通region没啥意义,如果放也是放meta表的region。因为master操作meta表比较频繁,放一起理论上可以减少延迟。当然也可以不放,默认就没放。一旦meta表放在master上,重启master就不那么的无痛了,并且很多个集群找两台机器搭master可能就不合适了因为master的load会变高。
1.0还把client的主要操作接口从HTable类、HTableInterface接口变成了一个Table接口。对应的API上有一些细微修改以更符合语义。另外新增了multi-WAL的特性,一个RS不一定还写单个WAL文件,不同的region可以写在不同的文件上,提高写性能(主要还是单个WAL暂时无法把系统压榨满)。
还有个比较大的变化是把mvcc和sequence id合二为一了。以前这俩一个表示请求顺序一个表示写入WAL的顺序,合二为一意味着WAL的写入顺序和请求的先后顺序一致了,很多事情会方便做。
1.0还有一些功能是0.98.0没有但0.98后续版本中的某一个开始支持,因为0.9x的每个分支是可以加feature的。比如cell级别的ttl、提供原子性的checkAndMutate等等。
1.1是2015年5月发布。主要新增的功能是scan的协议改进,之前扫描如果一次性扫的太多很容易超时,新版允许快超时之前把当前已经扫完的部分先传给client,甚至可以在无法返回任何有效数据的时候返回一个空的response作为心跳。同时除了time之外还考虑size,如果一行数据特别大可能会超时甚至OOM,改进后允许返回一行的部分cell给client从而避免server一次攒太多OOM,client把完整的一行拼接好后一起给用户从而保证scan一次返回一行的语义,或者client如果setAllowPartial(true)后可以每次返回几列这样也避免client端OOM。
1.1还支持请求限流,可以根据用户或表来限流,超过之后把请求干掉防止个别人把系统搞挂。Flush也不再是表级别可以针对单个CF进行flush。基于HDFS2.6的新功能允许某些文件单独放ssd另一些放hdd上,1.1开始也允许把WAL写在ssd。
1.2是2016年2月发布。最大的改动可能是region replica的功能基本“完善”了。虽然这个功能在1.0就有了,但很多细节是1.1和1.2实现的。这个东西其实就是让一个region在主从俩(甚至更多)region server上服务。其中只有主RS可以写,从RS可以最终一致性的读。读写分离的同时也可以用来作为备份降低一个RS挂掉后对应region的恢复时间,当然代价是整个集群占用的MemStore变成二倍了。当然这里的“完善”加了引号,是因为这只代表着社区想搞的主要功能都加上了,我个人比较怀疑有多少人在生产环境用,对应的意味着一些bug可能并没有被发现,不知道这个功能到底是否稳定。
还有个改动是row lock改成读写锁了,因为HBase支持一些cas的原子操作,需要先读在写,再此期间同一个row的请求必须等着。此前这个lock就是普通的lock,导致非cas的写请求比如put也无法在row内并发执行。改成读写锁后,cas的操作拿写锁,普通的写操作拿读锁,这样如果没有cas操作的话并发的put可以直接各自执行以时间戳为版本。读请求和这个锁没关系,靠mvcc拿一致性的数据。
1.3现在还没发,已经算比较滞后了,直接导致未来的1.4在branch-1上憋了很长时间因此1.4估计是会加非常多的功能……1.3在感觉比较重要的新功能应该是支持DateTieredCompaction,在compaction的时候按时间戳分区,新老数据在不同的文件,这样读数据的时候如果设了时间范围就可以跳过不相干的文件了(现在也支持设时间范围并且也支持在读取时过滤不需要的HFile,但compact没有根据时间戳拆分,所以很多时候可能还是读大多数文件)。这个功能的设计借鉴了Cassandra,Cassandra 堆feature的能力还是需要HBase学习的……
1.0后的主要版本的新feature暂时就收集到这些,不知道有没有重要的东西遗漏,有些也谈不上feature可能只是底层的改进,用户都不一定知道。某种程度上也因为用户可见的feature堆的不够快影响了推广,相对来说Cassandra在国外更火和Datastax堆feature与商业推广的能力密不可分。HBase最近两年的commit频率是高于以往的,而Cassandra并没有增加甚至可能小幅度下降。感觉这和中国的公司更多参与到HBase的社区开发有比较大的关系。而不同公司贡献内容的方向也很好的体现了不同业务看重的不一样的点。像阿里贡献的patch更多地在压榨极限性能比如qps,而小米对单机qps极限要求不高,更多的是提高集群的可用性以及业务所需要的一些功能。因为阿里对HBase社区贡献最大的部门(整个阿里用HBase的部门很多,用的版本也不一样,各自维护各自的,毕竟大公司)是把HBase用来做搜索的,天猫淘宝的搜索量比较高从而需要HBase有很好的性能,性能比容量更先成为瓶颈。而小米的HBase最大的用户是MiCloud,在线服务存用户的数据,虽然访问量也很大但单机的qps并没到瓶颈,或者说总是磁盘的容量/IO能力先到瓶颈,因此更关注可用性、故障恢复时间等等,还有就是业务遇到的各种问题,比如delete语义等等,即使关注latency可能也并不是最重要的考量。当然,阿里和小米侧重点不一样是好事,这样HBase可以在两方面同时改进。
而最近一两年很多代码只发到master上,这就意味着很多功能只有2.0才有。2.0我个人觉得最重要的是仨东西:一个是异步的client,各种接口返回的都是CompletableFuture(当然也意味着必须用jdk8了,毕竟7都EOL了);一个是各种offheap的工作,降低gc压力从而降低.999的延迟;一个是各种优化从而提高性能,比如异步的FanOutWAL(异步本身可以用更少的线程提高吞吐,并且在HBase内部重写了HDFS的Output从而变成同时往三个datanode写数据而非pipeline,也降低了写延迟)、Netty的RPCServer等等。总体来说2.0还是比较值得期待的,虽然不知道啥时候能发2.0.0,也不知道比较稳定要等多久。