HBase数据结构原理及使用方法详解?
摘要:# 一、HBase简介 HBase是一个开源的、分布式的、版本化的NoSQL数据库(即非关系型数据库),依托Hadoop分布式文件系统HDFS提供分布式数据存储,利用MapReduce来处理海量数据,用Zookeeper作为其分布式协同服务
一、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。
LSM树的设计思想:
划分不同等级的树。将对数据的修改增量保持在内存中,数据更新只在内存中操作,没有磁盘访问。达到指定的大小限制后将这些修改操作批量写入磁盘。由于内存的读写速率都比磁盘要快非常多,因此数据写入内存的效率很高。随着小树越来越大,达到指定的阀值限制后将这些修改操作批量写入磁盘,磁盘中的树定期做多路归并操作,合并成一棵大树,以优化读性能。随机读写比顺序读写慢很多,为了提升IO性能,需要将随机操作变为顺序操作。LSM树使用日志文件和一个内存存储结构把随机写转化成顺序写,读写独立,数据从内存刷入磁盘时是预排序的,写性能大幅提升。读取的时候稍微麻烦,需要先看是否命中内存,如果读取的是最近访问过的数据则可以命中,否则需要访问较多的磁盘文件。
使用LSM树的数据库除了HBase,还有nessDB、levelDB、TiDB、RocksDB等。
(图中MongoDB只有WiredTiger(WT)存储引擎既支持B-树,又支持LSM树存储索引。)
2、存储结构
HBase的LSM树中存储的是多个Key-Value结构组成的集合,每一个Key-Value一般都会用一个字节数组来表示。这个字节数组串设计如图所示:
(图源:胡争,范欣欣《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的稀疏索引。
