HBase的数据结构原理与使用

简介: 关键词:HBase Hadoop 大数据 大数据存储 数据开发 数据库

一、HBase简介

HBase是一个开源的、分布式的、版本化的NoSQL数据库(即非关系型数据库),依托Hadoop分布式文件系统HDFS提供分布式数据存储,利用MapReduce来处理海量数据,用Zookeeper作为其分布式协同服务,一般用于存储海量数据。HDFS和HBase的区别在于,HDFS是文件系统,而HBase是数据库。HBase只是一个NoSQL数据库,把数据存在HDFS上。可以把HBase当做是MySQL,把HDFS当做是硬盘。

二、HBase的数据结构

1、索引结构:LSM树

传统关系型数据普通索引采用B+树。B+树最大的性能问题是会产生大量的随机IO,随着新数据的插入,叶子节点会慢慢分裂,逻辑上连续的叶子节点在磁盘存储上往往不连续,分离得很远,随机读写概率会变大,做范围查询时,会产生大量读随机IO。为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees,直译为日志结构合并树。基于LSM树实现的HBase的写性能相比Mysql放弃部分磁盘读性能,换取写性能的大幅提升。

LSM树严格来说不是一个具体的数据结构,更多是一种数据结构的设计思想。LSM树不是一棵树,而是由至少两个存储结构构成。假设这两颗树分别为C0和C1,C0比较小,全部驻于内存之中,具体可以是任何方便健值查找的数据结构。而C1则驻于机械硬盘。一条新的记录先是从C0中插入,如果这一次的插入造成了C0数据量超出了阀值,那么C0中的部分些数据片段则会直接合并到C1树中。如果有多级树,当C1体量越来越大就向C2合并,低级的树在达到大小阈值后也会在磁盘中进行合并,以此类推,一直往上合并Ck。
195230-f58bf94c28336ce4.png
195230-0ee37f170c29523a.png

LSM树的设计思想:

划分不同等级的树。将对数据的修改增量保持在内存中,数据更新只在内存中操作,没有磁盘访问。达到指定的大小限制后将这些修改操作批量写入磁盘。由于内存的读写速率都比磁盘要快非常多,因此数据写入内存的效率很高。随着小树越来越大,达到指定的阀值限制后将这些修改操作批量写入磁盘,磁盘中的树定期做多路归并操作,合并成一棵大树,以优化读性能。随机读写比顺序读写慢很多,为了提升IO性能,需要将随机操作变为顺序操作。LSM树使用日志文件和一个内存存储结构把随机写转化成顺序写,读写独立,数据从内存刷入磁盘时是预排序的,写性能大幅提升。读取的时候稍微麻烦,需要先看是否命中内存,如果读取的是最近访问过的数据则可以命中,否则需要访问较多的磁盘文件。

使用LSM树的数据库除了HBase,还有nessDB、levelDB、TiDB、RocksDB等。
75.jpg
(图中MongoDB只有WiredTiger(WT)存储引擎既支持B-树,又支持LSM树存储索引。)

2、存储结构

HBase的LSM树中存储的是多个Key-Value结构组成的集合,每一个Key-Value一般都会用一个字节数组来表示。这个字节数组串设计如图所示:
28367945f0bb4bc7af7bcf7f09a03d38.png
(图源:胡争,范欣欣《HBase原理与实践》第二章《基础数据结构与算法》)

字节数组主要分为以下几个字段。其中Rowkey、Family、Qualifier、Timestamp、Type这5个字段组成KeyValue中的key部分。

• keyLen:用来存储KeyValue结构中Key所占用的字节长度。
• valueLen:用来存储KeyValue结构中Value所占用的字节长度。
• rowkeyLen:用来存储rowkey占用的字节长度。
• rowkeyBytes:用来存储rowkey的二进制内容。
• familyLen:用来存储Family占用的字节长度。
• familyBytes:用来存储Family的二进制内容。
• qualif ierBytes:用来存储Qualif ier的二进制内。注意,HBase并没有单独分配字节用来存储qualif ierLen,因为可以通过keyLen和其他字段的长度计算出qualif ierLen。
• timestamp:表示timestamp对应的long值。
• type:表示这个KeyValue操作的类型,HBase内有Put、Delete、Delete Column、DeleteFamily,等等。

HBase的LSM树在内存一般采用跳跃表存储,跳跃表的查找、删除、插入的复杂度都是O(logN)。

LSM树在磁盘中的数据结构也不是树结构,而是Key-Value结构组成的序列,称为SSTable(Sorted String Table)有序字符串表。当SSTable太大时,为了加快SSTable的读取,可以将其划分为多个块,通过记录每个块的起始位置,构建每个SSTable的稀疏索引。这样在读SSTable前,通过索引就知道要读取的数据块磁盘位置了。SSTable索引需要永远加载在内存里。写是写内存,因此随机写十分快。读也是读内存里的 SSTable的索引,并且这里每一个SSTable索引如果用二分法查找,算法复杂度大致在O(lg(n))与O(n)之间,因此随机读也不慢。

3、表结构

与传统的关系型数据库类似,HBase也以表的形式组织数据,表也由行和列组成,不同的是,HBase采用列式存储。
v2-58697c09af0a80f04af4b2231e6a0813.jpg
如上图所示的表,如果采用列式存储,会存成下图的结构:
v2-8b4c896c9decec36b89e6b2fee968c6a.jpg
可以发现,列式存储就是把每列抽出来,然后关联上ID,实际上是用Key-Value结构保存的。这样的优点在于,当表格中有空缺时,可以充分利用存储空间。

对HBase来说,一行数据由一个行键(RowKey)和一个或多个相关的列以及它的值所组成。列的组成都是灵活的,行与行之间的列不需要相同。行键(RowKey)就是SSTable的key。

在HBase里边,先有列族(也叫“列簇”,Column Family),后有列。列族将一列或者多列组织在一起,HBase的每一个列都必须属于某个列族。HBase的列都得归属到列族中,如图所示:
v2-22e7f861a6a079a558a1fb173dbd8b9e.jpg

数据写到HBase的时候都会被记录一个时间戳,这个时间戳被我们当做一个版本。比如说,我们修改或者删除某一条的时候,本质上是往里边新增一条数据,记录的版本加一了而已。如图所示:
v2-a08e56a695e366a580c12514ffe09a21.jpg

被更新和删除的数据不会直接从磁盘上删除,而是为数据添加一个删除标记,查找时会跳过被删除的键,DBA运维会定期删除被标记删除的数据。因此,如果存在频繁覆盖删除需要提前向运维报备以免影响数据库性能。

三、HBase的使用

1、HBase的读写

HBase提供了多种模式、多种语言的访问接口。目前常用的包括Native Java API,Thrift和MapReduce模式。

(1)Java API是HBase提供的原生接口,具备完善的客户端处理逻辑,直接与HBase Server进行通信,效率最高,但受限于语言限制;
(2)Thrift不受语言限制,但会占用额外的网络带宽和处理时间;
(3)HBase还支持了MapReduce,可以通过编写MapReduce任务进行批量数据操作。
使用GoLang和PHP语言搭建的项目显然得用Thrift接口。

常用的HBase的数据操作get、scan和put三种。

(1)get实现随机读取功能,根据指定RowKey获取惟一一条记录。
(2)scan提供批量查询功能,按照指定的条件获取一批记录。通过指定起始和中止的key,即可获取所有包含在内的key对应的数据。可以通过setStartRow与setEndRow来限定范围,也可以通过setFilter方法添加过滤器,这也是分页、多条件查询的基础,用setCaching和setBatch方法能提高速度。
(3)put实现写入,如果要批量导入大规模数据,还可以采用bulkimport的方式。

2、行键(RowKey)设计

Rowkey相当于HBase中数据的主键。HBase中的数据是按照RowKey的ASCII字典顺序进行全局排序。可以使相关行彼此靠近存储。如果Rowkey设计不当会引发热点问题,即客户端大量的读写请求都集中在一个或几个节点上。从而导致性能下降。为防止数据写入时出现热点,数据被写入时应写入集群中的多个区域,而不是一次写入一个区域(Hregion)。

设计原则:

1、唯一原则,要保证Rowkey的唯一性。若HBase中同一表插入相同Rowkey,则原先的数据会被覆盖掉。设计Rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。
2、长度原则。Rowkey长度越短越好,一般不要超过16字节。因为RowKey是一个二进制码流,可以是任意字符串,最大长度64KB,实际应用中一般为10-100字节,以byte[]形式保存。如果RowKey过长比如500个字节,1000万列数据仅RowKey就要占用5GB空间,非常影响HFile的存储效率。
3、散列原则。用时间戳作为Rowkey的前缀会导致大量数据堆积在一个区域进而导致热点问题。如果Rowkey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将Rowkey的高位作为散列字段,低位放时间字段。
2014953-20200509204010907-398203158.png

3、列族(Column Family)的设计

设计原则:

1、列族的名称尽可能短,甚至可以是一个字符。例如,“d”表示数据/默认值。
2、HBase当前不能很好地处理超过两个或三个列族的数据,因此请保持列族的数量较少。最好使用一个列族。仅在数据访问通常是列范围的情况下才引入第二和第三列族。即,一次只查询一个列族,通常不会查询两个列族。
3、将相同IO特性的列放入同一列族。
4、多个列族中的数据(行数)分布大致均匀。
5、对于临时性的列族可以设置失效时间。一旦达到到期时间,HBase将自动删除行。

4、HBase Shell的安装和使用

HBase自带的操作工具只有HBase Shell这一命令行终端。通过HBase Shell工具,可以交互式地进行数据管理,包括插入数据、删除数据等。虽然也有一些第三方图形界面客户端支持HBase,如DBeaver、BigInsights、HbaseGUI,但系统部的HBase只支持HBase Shell。

安装HBase Shell需要先挑选一台用于安装的虚拟机,为该虚拟机安装Java环境。之后在系统部奇麟大数据的客户端管理页面选择“添加客户端账号”,申请为该虚拟机添加项目账号。申请通过后勾选机器,单击“部署Hadoop环境”在该机器上安装HBase Shell。

安装成功后,到虚拟机上使用sudo -iu命令先切换到项目账号。然后切换到目录cd $HBASE_HOME/bin,运行hbase shell,即可进入HBase Shell程序。

这里列出几个常用的HBase Shell命令:

名称 命令表达式
查看存在哪些表 list
添加数据 put '表名称', '行键', '列族 : 列名', '值'
查看一行数据 get '表名称', '行键'
查看指定列族的一行数据 get '表名称', '行键', '列族'
查看指定列族及列名的数据 get '表名称', '行键', '列族 : 列名',
查看表中的数据总量 count '表名'
删除一个单元格的数据 delete '表名' ,'行键' , '列族 : 列名'
删除一行所有数据 delete '表名' ,'行键'
查看表的所有数据 scan '表名'。注意,一般不应直接使用scan扫描整个表的海量数据。
查看一列数据 scan '表名' , ['列族 : 列名']
查看帮助信息 help

5、MongoDB数据迁移HBase

使用kettle等工具可以把MongoDB数据库迁移到HBase。也可以使用MapReduce处理,速度远快于Java API和Thrift。

参考文献:

胡争,范欣欣.HBase原理与实践[M].北京:机械工业出版社,2019

O’Neil, P., Cheng, E., Gawlick, D., & O’Neil, E. (1996). The log-structured merge-tree (LSM-tree). Acta Informatica, 33(4), 351-385.

相关实践学习
云数据库HBase版使用教程
  相关的阿里云产品:云数据库 HBase 版 面向大数据领域的一站式NoSQL服务,100%兼容开源HBase并深度扩展,支持海量数据下的实时存储、高并发吞吐、轻SQL分析、全文检索、时序时空查询等能力,是风控、推荐、广告、物联网、车联网、Feeds流、数据大屏等场景首选数据库,是为淘宝、支付宝、菜鸟等众多阿里核心业务提供关键支撑的数据库。 了解产品详情: https://cn.aliyun.com/product/hbase   ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
存储 编译器 数据库
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
55 0
|
2月前
|
算法 调度 C++
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
41 0
|
4月前
|
存储 NoSQL 安全
Redis相关命令详解及其原理:Redis基本操作、数据结构以及应用
Redis相关命令详解及其原理:Redis基本操作、数据结构以及应用
96 0
|
3天前
|
存储 算法 Java
22个常用数据结构实现与原理分析
前两天V哥跟一个老学员吃饭,聊起面试大厂的事,说为啥大厂面试第一看基本条件,第二就是考数据结构算法,其他高阶的内容会比较少,最近V哥也在跟大厂对接这一块业务,了解得多一些,这是因为考察基本功能力被放到了重要位置,大厂认为硬性条件,比如学历过关,基本功够扎实,那对于实际工作用的上层技能,内部培养就好,也就是相比你掌握了多少多少牛逼的高阶技术,他们更在乎你的基本功,所以,进大厂,基本功必须要搞稳,否则白扯,今天 V 哥把总结好的22个常用的数据结构实现原理,和示例分析分享给大家,希望对你有帮助,觉得内容有收获,请帮忙转发给更多需求的朋友,共同进步。
|
29天前
|
算法 调度 C++
[数据结构与算法]贪心算法(原理+代码)
[数据结构与算法]贪心算法(原理+代码)
|
29天前
|
存储 算法 编译器
【数据结构】栈算法(算法原理+源码)
【数据结构】栈算法(算法原理+源码)
【数据结构】栈算法(算法原理+源码)
|
29天前
|
算法 搜索推荐 数据库
【数据结构】二叉树算法讲解(定义+算法原理+源码)
【数据结构】二叉树算法讲解(定义+算法原理+源码)
|
29天前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
2月前
|
存储 NoSQL Redis
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)(三)
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)
36 0
|
2月前
|
存储 NoSQL 安全
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)(二)
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)
26 0