HBase简介

2017/09/01 - 数据库 系统架构

简介

HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。

生态

  • 利用Hadoop HDFS作为其文件存储系统。
  • 利用Hadoop MapReduce来处理HBase中的海量数据。
  • 利用Zookeeper作为协同服务

数据模型

Table & Column Family

  • Row Key: 行键,Table的主键,Table中的记录按Row Key排序
  • Timestamp: 时间戳,每次数据操作对应的时间戳,可以看作是数据的version number
  • Column Family:列簇,Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column组成,所有Column均以二进制格式存储,用户需要自行进行类型转换。

row key为表的主键,是必然存在的一列,其次HBase表中的除row key外的列被划分为若干个列族(column family),列族下面是的一些列。

数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。

hbase表中的每个列,都归属与某个列族。必须在使用表之前定义。列名都以列族作为前缀。例如courses:history,courses:math 都属于courses 这个列族。

cell中的数据是没有类型的,全部是字节码形式存贮。

针对一行的读与写操作都是原子的。

通常column family的数量比较少(百数量级),而且一般相对固定,通常在表创建之后就很难甚至不可能修改了。与此相对的column key一般没有数量限制,也可以动态增删。

"com.tx.lottery" : { // row key
    "issues" : { // first column family
        "ssq": {  // column key
            1 : "2014150" // single timestamp
        },      
        "dlt": "15015", // no timestamp
        "k3" : {
            10 : "20150301013", // multiple timestamp
             7  : "20150301010",
             1  : "20150301006"
        }           
    }
    "apps" : { // second column family
        "" : "hsf"  // empty column key
    }
    "fangInfo" : { // third column faimily
        "location":,
        "price":
    } 
}

Table & Region

当Table随着记录数不断增加而变大后,会逐渐分裂成多份splits,成为regions,一个region由[startkey,endkey)表示,不同的region会被Master分配给相应的RegionServer进行管理:

-ROOT- && .META. Table(0.96以前的版本)

HBase中有两张特殊的Table,-ROOT-和.META.

  • .META.:记录了用户表的Region信息,.META.可以有多个regoin

  • -ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region

  • Zookeeper中记录了-ROOT-表的location

Client访问用户数据之前需要首先访问zookeeper,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问,中间需要多次网络操作,不过client端会做cache缓存。

HBase系统架构

Client

HBase Client使用HBase的RPC机制与HMaster和HRegionServer进行通信。对于管理类操作,Client与HMaster进行RPC;对于数据读写类操作,Client与HRegionServer进行RPC。

Zookeeper

Zookeeper Quorum中除了存储了-ROOT-表的地址和HMaster的地址,HRegionServer也会把自己以Ephemeral方式注册到Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点问题。

HMaster

HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行,HMaster在功能上主要负责Table和Region的管理工作:

  • 管理用户对Table的增、删、改、查操作
  • 管理HRegionServer的负载均衡,调整Region分布
  • 在Region Split后,负责新Region的分配
  • 在HRegionServer停机后,负责失效HRegionServer 上的Regions迁移

HRegionServer

Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)

HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region,HRegion中由多个HStore组成。每个HStore对应了Table中的一个Column Family的存储。

HStore存储是HBase存储的核心了,其中由两部分组成,一部分是MemStore,一部分是StoreFiles。MemStore是Sorted Memory Buffer,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile),当StoreFile文件数量增长到一定阈值,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要进入内存中就可以立即返回,保证了HBase I/O的高性能。当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前Region Split成2个Region,父Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。下图描述了Compaction和Split的过程:

在理解了上述HStore的基本原理后,还必须了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没有问题的,但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需要引入HLog了。每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。

HFile

首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。Trailer中有指针指向其他数据块的起始点。File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。Data Index和Meta Index块记录了每个Data块和Meta块的起始点。

Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询。每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏。后面会详细介绍每个KeyValue对的内部构造。

HFile里面的每个KeyValue对就是一个简单的byte数组。但是这个byte数组里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构:

开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。

HBase表特点

  • 大:一个表可以有上亿行,上百万列
  • 面向列:面向列(族)的存储和权限控制,列(族)独立检索。
  • 稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏。

常用API

Configuration config = new Configuration();
config.set(HConstants.ZOOKEEPER_CLIENT_PORT, port);
config.set(HConstants.ZOOKEEPER_QUORUM, quorum);
config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, cluster);
config = HBaseConfiguration.create(config);
HTable hTable = new HTable(config, "lottery_tb_order");


HBaseAdmin hBaseAdmin = new HBaseAdmin(config);
HTableDescriptor[] hTableDescriptors = hBaseAdmin.listTables();


//创建配置
Configuration configuration = HBaseConfiguration.create();
HBaseAdmin admin = new HBaseAdmin(configuration);

// 新建表的基本信息,包含表名,column family信息等
HTableDescriptor tableDescriptor = new TableDescriptor(TableName.valueOf("lottery_info"));
tableDescriptor.addFamily(new HColumnDescriptor("issues"));
tableDescriptor.addFamily(new HColumnDescriptor("apps"));
tableDescriptor.addFamily(new HColumnDescriptor("fangInfo"));

// 执行建表
admin.createTable(tableDescriptor);



HColumnDescriptor columnDescriptor = new HColumnDescriptor("matches");
admin.addColumn("lottery_info", columnDescriptor);
admin.deleteColumn("lottery_info","fangInfo");



Configuration configuration = HBaseConfiguration.create();
HBaseAdmin admin = new HBaseAdmin(configuration);

String tableName = "lottery_info";
if (admin.tableExists(tableName) && !admin.isTableDisabled(tableName)) {
    admin.disableTable(tableName);
    admin.deleteTable(tableName);
}



Configuration configuration = HBaseConfiguration.create();
HTable table = new HTable(configuration, "lottery_info");
String rowKey = "com.tx.lottery";
String columnFamily = "issues";
String columnKey = "ssq";
String data = "2014150";

// 插入或更新一行
Put put = new Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(columnFamily), Bytes.toBytes(columnKey), Bytes.toBytes(data));
table.put(put);

// 查询一行
Get get = new Get(Bytes.toBytes(rowKey));
Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes(columnFamily), Bytes.toBytes(columnFamily));



Configuration configuration = HBaseConfiguration.create();
HTable table = new HTable(configuration, "lottery_info");
String rowKey = "com.tx.lottery";
String columnFamily = "issues";
String columnKey = "ssq";

// 删除一行
Delete delete = new Delete(rowKey.getBytes());
table.delete(delete);

// 删除column下的最新版本数据
Delete specifiedDelete = new Delete(rowKey.getBytes());
specifiedDelete.addColumn(columnFamily.getBytes(), columnKey.getBytes());
table.delete(specifiedDelete);

如果文章对您有帮助,欢迎扫描下方二维码赞助(一分也是爱噢),谢谢

Search

    一分也是爱噢 一分也是爱

    目录