HBase入坑须知(一)

96
冯宇Ops
0.4 2018.04.25 21:45* 字数 1766

使用HBase时间也不短了,看到周围也有很多人逐步在应用HBase,于是打算总结一下HBase常见的坑,给新入坑的小伙伴的一些参考。

本篇为入门篇,以HBase的基础概念为主,后期可能会引入项目中的实际应用。

入坑前请细读官方文档官方文档文档……

重要的事情说三遍,哪怕只是快速扫一眼,也请快速扫完官方文档,再来详读本篇内容。本篇内容不会对HBase的各种好处以及详细设计鼓吹一通,只是快速总结下需要注意的点与可能遇到的坑。因此无论何时请务必以官方文档为主。以下只是总结初次接触时的一些要点,读者默认为是已经了解HBase的场景及用途,如果还在纠结是否要用HBase的朋友,请先阅读官方文档了解设计,再决定是否切换到HBase吧。

HBase上手并不那么容易,而且hbase shell命令行工具可能也不像其他RDBMS的cli界面那么好用,很多你习惯的功能可能没有,甚至可能要非常复杂,这些请务必提前做好准备。

另外,国内中文资料大多版本停留在古老的0.94版本,大多数已经不具有参考价值,请务必以官方文档为准。

发行版的选择

可能有些小伙伴已经听说过了Hadoop有各种发行版了,而HBase直接跑在Hadoop之上,所以也会有各种选择。比如官方开源版本,Cloudera版本,MapR等等诸多发行版,一些选择恐惧症的小伙伴可能已经要疯了,入门都要纠结半天。

这里我不会费力的去比对各种版本的异同,仅给出我个人的上手意见。初学以及本地快速搭建环境,推荐使用官方版本,因为相对获取容易,本地开发启动容易,文档资料较多。

如果是产品环境部署,我个人推荐使用Cloudera的CDH,因为它带有一个图形化的集群部署以及管理工具Cloudera Manager,产品环境部署升级维护都极其方便,以及相对更新比较平稳,性能比较稳定。个人推荐使用Cloudera Manager部署三节点集群做产品环境。

NOTE: 可能有些人会问,我在本地使用官方版本的Hadoop,产品环境使用CDH,那么能兼容吗?以我多年的实际使用经验来看,是可以的,绝大多数使用不会遇到问题。某些yarn的api可能会遇到CDH版本和官方版本不同,那么只需要加入Cloudera的maven仓库,使用cloudera版本的jar包替换项目依赖即可。

HBase的存储

这个坑是很多入门的朋友最容易迷惑的,详细描述官方文档都有,这里只总结一些要点:

  • HBase中的存储一切皆是字节
  • HBase的RowKey会按照字节顺序排序,并且添加索引
  • HBase会按照row数量自动切割成Region,保持负载均衡与冗余(策略可改,如需调整默认行为请详细阅读官方文档)

在HBase,你存的任何内容都必须转为byte[]字节流进行存储,在Java代码中可以调用hbase-client jar包中的org.apache.hadoop.hbase.util.Bytes.toBytes()方法将各种基本类型的数据转为byte[]字节流。所以解析数据的压力就放在了客户端。客户端从HBase拿到了字节流,进行相应的解码算法还原为原始数据(比如通过org.apache.hadoop.hbase.util.Bytes.toInt()byte[]还原回int)。因此要求存入数据之前用户必须设计好存储内容——HBase根本不管你存入的是什么玩意,对它来说就是一个byte[],用户必须自己负责解析内容,因此存储数据之前必须想好你要存什么,以及如何解析。这点和很多常见的RDB有很大的不同。

HBase的RowKey天生自带索引,并且按字节顺序排列,而且天然分布式。因此设计RowKey就成了用好HBase的关键。你可以把RowKey想象中SQL语句中SELECT col FROM htable WHERE (condition);其中的condition,用HBase之前务必先设计好你的查询条件,将你的WHERE条件的语句作为RowKey存储。

HBase的存储结构

每个HBase的table存储的结果大致如下表所示:

image.png

qualifier之上还有个层级column family,相比传统的RDB多了一个层级,初次使用可能会不适应。一些概念解释如下:

Term Description
Row HBase中的"行",包含一个row key(行键)和若干个column(列)
Column Family "列族",相当于列的分组,由多个列构成一个列族。同一列族下的列具有相同的属性
Column Qualifier 就是列族下某个具体的"列",Qualifier的属性受到Column Family限制
Column "列",表示存储中的某一具体的列,包含列族和Qualifier,用:连接: cf:qualifier
Cell 就是由行列所唯一确定的单元格。一个单元格可能包含多个version
Timestamp 每个Cell中都会包含一个元数据Timestamp,主要用于区分存储的版本

特别注意HBase中每个单元格可以存储多个Version,但是通常不建议存储多个VersionVersion是通过Timestamp属性区分的。每个table中也不建议使用过多的Column Family

关于HBase Shell

首先要有一个了解,就是hbase shell使用JRuby开发的,这个命令行实际跑的是ruby代码,理论上你可以在这个Shell上跑任何Ruby代码,而不要把自己局限在help输出的那几个固定的函数。

比如一个常用的例子,就是将hbase shell中输出的各种hex string还原为对应的数据类型,比如这样scan 'table'之后,经常能看到类似于这样的内容:

 \x00\x00\x00\x00\x00\x00\x00\x0 column=cf:\x00\x02\x98\x10, timestamp=1458266571188, value=xxxxxx
 1\x7F\xFF\xFE\xACx\x8C\x9A\xFF
1 row(s) in 0.1930 seconds

我们已知我们存储的结构是RowKey存储结构是long-long共16个字节, column的存储结构是cf:int,int占4个字节,那么我们可以在终端下使用ruby代码直接还原出这个hex string:

hbase(main):003:0> "\x00\x00\x00\x00\x00\x00\x00\x01".unpack("N")
=> [0]
hbase(main):004:0> "\x7F\xFF\xFE\xACx\x8C\x9A\xFF".unpack("N")
=> [2147483308]
hbase(main):005:0> "\x00\x02\x98\x10".unpack("N")
=> [170000]

直接使用ruby的unpack方法,将hex string还原为对应的longint,得知我们到底存储的是什么玩意。如果熟悉一点ruby代码的话,用hbase shell会得心应手。即使不了解,也可以通过搜索引擎帮我们找到合适的ruby代码放到hbase shell中使用。

一些常见的表结构设计套路

官方文档中已经罗列了一些常见的HBase场景中结构设计的套路,可以参考。尤其是做时序数据库的时候,那么OpenTSDB表结构值得参考。

后续会逐步总结下我们在实战中的一些筛选查询范例以供参考。

技术随笔