一、Apache Hbase基本概述

Apache Hbase是一个基于Hadoop的数据库,它可靠、数据多版本、分布式适合结构化大数据的存储,Apache Hbase是Google BigTable开源实现,基于列储存的菲关系型数据库。

(1)列储存和行储存的区别

列储存和行储存是指数据子存储介质中的额储存方式
**·**关系型数据库(行储存):Oracle、mysql等
**·**非关系型数据库(列储存):Hbase、Redis
在这里插入图片描述

(2)Hbase数据模型及概念

在这里插入图片描述
(1)主键rowkey:获取数据的唯一标识,不能重复,根据字典顺序自动排序,底层储存时byte【】。
(2)列簇column family:多个列的集合,通常一个列簇中存放的是一组功能相近或者业务相近的集合。
(3)单元格cell:rowkey+column family + column定位一个cell,cell有多版本,默认为1个。
(4)多版本:cell允许有读个数据版本。
(5)版本号:系统当前的时间戳,默认会将时间戳最新的cell数据返回给用户。
(6)列column:colume列簇中的一个字段,用来存放某一类别的数据。

(3)Hbase特点:

(1)大:一个表可以有上百亿行上百万列。
(2)面向列:面向列表(列簇)的存储和控制权限
(3)结构稀疏:对于为空(NULL)的列,并不占用储存空间,因此,表可以设计的非常稀疏。
(4)无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中的不同行可以有截然不同的列。
(5)数据多版本:每个单元格中的数据可以有多个版本,默认情况下,版本号自动分配,版本就是单元格插入时的时间戳。
(6)数据类型单一:Hbase中的数据在底层储存是都是byte【】,可以存放任意类型的数据。

(4)Hbase架构详解及完全分布式结构

完全分布式结构:
在这里插入图片描述
zookeeper作为hbase集群的入口(后面java api中从连接hbase的方式就能看出来)

hbase架构详解:
在这里插入图片描述
HBase中的每张表都通过rowKey按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,这个过程由HRegionServer管理,而HRegion的分配由HMaster管理。

HMaster的作用:

(1)为HRegionServer分配HRegion
(2)负责HRegionServer的负载均衡
(3)发现失效的HRegionServer并重新分配
(4)HDFS上的垃圾文件回收
(5)处理Schema更新请求

HRegionServer的作用:

(1)维护HMaster分配给它的HRegion,处理对这些HRegion的IO请求
(2)负责切分正在运行过程中变得过大的HRegion

可以看到,Client访问HBase上的数据并不需要HMaster参与,寻址访问ZooKeeper和HRegionServer,数据读写访问HRegionServer, HMaster仅仅维护Table和Region的元数据信息,Table的元数据信息保存在ZooKeeper上,负载很低。HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列簇创建一个HStore对象,每个HStore都会有一个MemStore和0或多个StoreFile与之对应,每个StoreFile都会对应一个HFile,HFile就是实际的存储文件。因此,一个HRegion有多少列簇就有多少个Store。 一个HRegionServer会有多个HRegion和一个HLog。

HRegion

Table在行的方向上分割为多个HRegion,HRegion是HBase中分布式存储和负载均衡的最小单元,即不同的HRegion可以分别在不同的HRegionServer上,但同一个HRegion是不会拆分到多个HRegionServer上的。HRegion按大小分割,每个表一般只有一个HRegion,随着数据不断插入表,HRegion不断增大,
当HRegion的某个列簇达到一个阀值(默认256M)时就会分成两个新的HRegion。

1、<表名,StartRowKey, 创建时间>
2、由目录表(-ROOT-和.META.)记录该Region的EndRowKey

HRegion定位:HRegion被分配给哪个HRegionServer是完全动态的,所以需要机制来定位HRegion具体在哪个HRegionServer,HBase使用三层结构来定位HRegion:
    1、通过zk里的文件/hbase/rs得到-ROOT-表的位置。-ROOT-表只有一个region。
    2、通过-ROOT-表查找.META.表的第一个表中相应的HRegion位置。其实-ROOT-表是.META.表的第一个region;
         .META.表中的每一个Region在-ROOT-表中都是一行记录。
    3、通过.META.表找到所要的用户表HRegion的位置。用户表的每个HRegion在.META.表中都是一行记录。

    -ROOT-表永远不会被分隔为多个HRegion,保证了最多需要三次跳转,就能定位到任意的region。Client会将查询的位置信息保存缓存起来,缓存不会主动失效,
    因此如果Client上的缓存全部失效,则需要进行6次网络来回,才能定位到正确的HRegion,其中三次用来发现缓存失效,另外三次用来获取位置信息。

HStore
每一个HRegion由一个或多个HStore组成,至少是一个HStore,HBase会把一起访问的数据放在一个HStore里面,即为每个ColumnFamily建一个HStore,如果有几个ColumnFamily,也就有几个HStore。一个Store由一个MemStore和0或者多个StoreFile组成。 HBase以Store的大小来判断是否需要切分HRegion。

MemStore
MemStore 是放在内存里的,保存修改的数据即keyValues。当MemStore的大小达到一个阀值(默认64MB)时,MemStore会被Flush到文件, 即生成一个快照。目前HBase会有一个线程来负责MemStore的Flush操作。

StoreFile
  MemStore内存中的数据写到文件后就是StoreFile,StoreFile底层是以HFile的格式保存

HLog
  HLog(WAL log):WAL意为write ahead log,用来做灾难恢复使用,HLog记录数据的所有变更,一旦region server 宕机,就可以从log中进行恢复。

简单描述一下Hbase故障恢复的过程:
Hmaster发现某些HregionServer不可用,开始故障恢复,获取故障的HregionServer的Hlog,分离每一个HRegion写指令,重新分配Hregion,在配到HregionServer中恢复HRegion数据,执行分离的HRegion写指令恢复MemStore,持久化完成的数据可以直接从HDFS中获取。

(二)Hbase Java API

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-common</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-protocol</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-common</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-protocol</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
package com.learn;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

public class HbaseAPI {
    private Admin admin;
    private Connection connection;

    @Before
    public void doBefore() throws IOException {
        Configuration configuration = HBaseConfiguration.create();
        configuration.set(HConstants.ZOOKEEPER_QUORUM,"192.168.139.156:2181");
        connection = ConnectionFactory.createConnection(configuration);
        admin = connection.getAdmin();
    }

    @Test
    public void testCreateNameSpace() throws IOException {
        NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create("gaoj").addConfiguration("author", "gaojian").build();
        admin.createNamespace(namespaceDescriptor);
    }

    @Test
    public void testCreateTable() throws IOException {
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("gaoj:t_order"));
        HColumnDescriptor cf1 = new HColumnDescriptor("cf1");
        cf1.setMaxVersions(3);
        HColumnDescriptor cf2 = new HColumnDescriptor("cf2");
        cf2.setTimeToLive(1800);
        hTableDescriptor.addFamily(cf1);
        hTableDescriptor.addFamily(cf2);
        admin.createTable(hTableDescriptor);
    }

    @Test
    public void testInsert() throws IOException {
        Table table = connection.getTable(TableName.valueOf("gaoj:t_order"));
        Put put = new Put(Bytes.toBytes("order101"));
        put.addColumn(Bytes.toBytes("cf1"),Bytes.toBytes("count"),Bytes.toBytes(123));
        table.put(put);
    }

    @Test
    public void testSelect() throws IOException {
        Table table = connection.getTable(TableName.valueOf("gaoj:t_order"));
        Get get = new Get(Bytes.toBytes("order101"));
        Result result = table.get(get);
        String name = Bytes.toString(result.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("name")));
        System.out.println(name);
    }

    @Test
    public void testDelete() throws IOException {
        Table table = connection.getTable(TableName.valueOf("gaoj:t_order"));
        Delete delete = new Delete(Bytes.toBytes("order101"));
        ArrayList<Delete> list = new ArrayList<Delete>();
        list.add(delete);
        table.delete(list);
    }

    @Test
    public void testScan() throws IOException {
        Table table = connection.getTable(TableName.valueOf("gaoj:t_order"));
        Scan scan = new Scan();
        ResultScanner scanner = table.getScanner(scan);
        Iterator<Result> iterator = scanner.iterator();
        while (iterator.hasNext()){
            Result next = iterator.next();
            String row = Bytes.toString(next.getRow());
            String name = Bytes.toString(next.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("name")));
            System.out.println(row + "*" + name);
        }
    }
    @After
    public void doAfter() throws IOException {
        if(admin != null){
            admin.close();
        }
        if(connection != null){
            connection.close();
        }
    }
}

以上api实现了数据的增删改查,关于Hbase,更多的掌握的是API的使用,在学习的过程中,我们可以使用help指令帮助学习。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐