Hbase介绍

Hbase介绍

什么是Hbase

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

利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理 Hbase中的海量数据,利用Zookeeper作为其分布式协同服务

主要用来存储非结构化和半结构化的松散数据(列存NoSQL数据库)

Hbase 的特点

Hbase 中的表一般有以下特点。

  1. 大:一个表可以有上亿行,上百万列。
  2. 面向列:面向列(列簇)的存储和权限控制,列(簇)独立检索。
  3. 稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
  4. 数据类型单一:HBase 中的数据类型只有一种String。
  5. 数据多版本:列中的数据可以有多个版本,查询时可以通过指定版本号获取

HBase与传统关系数据库的对比分析

数据类型:关系数据库采用关系模型,具有丰富的数据类型和存储方式,HBase则采用了更加简单的数据模型,它把数据存储为未经解释的字符串;

数据操作:关系数据库中包含了丰富的操作,其中会涉及复杂的多表连接。HBase操作则不存在复杂的表与表之间的关系,只有简单的插入、查询、删除、清空等,因为HBase在设计上就避免了复杂的表和表之间的关系;

查询语言:关系型数据库可使用sql进行查询,Hbase只能使用API(get、put、sacn等)进行操作

存储模式:关系数据库是基于行模式存储的。HBase是基于列存储的,每个列族都由几个文件保存,不同列族的文件是分离的;

数据索引:关系数据库通常可以针对不同列构建复杂的多个索引,以提高数据访问性能。HBase只有一个索引——行键,通过巧妙的设计,HBase中的所有访问方法,或者通过行键访问,或者通过行键扫描,从而使得整个系统不会慢下来;

数据维护:在关系数据库中,更新操作会用最新的当前值去替换记录中原来的旧值,旧值被覆盖后就不会存在。而在HBase中执行更新操作时,并不会删除数据旧的版本,而是生成一个新的版本,旧有的版本仍然保留;

可伸缩性:关系数据库很难实现横向扩展,纵向扩展的空间也比较有限。相反,HBase和BigTable这些分布式数据库就是为了实现灵活的水平扩展而开发的,能够轻易地通过在集群中增加或者减少硬件数量来实现性能的伸缩

整体架构

HBase架构中的主要组件

Hbase介绍_第1张图片

1.ZooKeeper

HBase集群要依赖ZooKeeper才能运行。主要作用是:

  1. Master高可用,协助选举Master

  2. 监听RegionServer状态(心跳),向Master汇报RegionServer上下线信息

  3. 存放与维护集群配置信息,如hbase:meta表的地址。

2.Client

包含访问HBase的接口并维护cache来加快对HBase的访问。Client可不经过Master直接与Region Server通信,发出读或写请求,所以Master挂掉的情况下,集群仍然可以运行一段时间。

3.HMaster

HMaster是集群的主节点,本质上是一个进程。主要作用有

  1. 负责管理元数据,如执行DDL操作、定期更新hbase:meta表

  2. 分配与移动region以保证集群的负载均衡(单个region过大,会拆分、转移)

  3. 管理RegionServer,出现问题时进行故障转移 在分布式集群中,Master通常运行在NameNode上。

4.Region Server

Region Server是Region的管理者,本质上是一个进程。主要作用是:

  1. 负责数据的增删改查,即DML操作

  2. 负责region的拆分与合并

  3. 将MemStore中数据刷写到StoreFiles

  4. 检查RegionServer的HLog文件 在分布式集群中,RegionServer都运行在DataNode上。

5.Region

  1. HBase自动把表水平划分成多个区域(region),每个region会保存一个表 里面某段连续的数据;每个表一开始只有一个region,随着数据不断插 入表,
  2. region不断增大,当增大到一个阀值的时候,region就会等分会 两个新的region(裂变);
  3. 当table中的行不断增多,就会有越来越多的region。这样一张完整的表 被保存在多个Regionserver上。

Memstore 与 storefile

  1. 一个region由多个store组成,一个store对应一个CF(列族)
  2. store包括位于内存中的memstore和位于磁盘的storefile写操作先写入 memstore,当memstore中的数据达到某个阈值,
  3. regionserver会启动 flash cache进程写入storefile,每次写入形成单独的一个storefile
  4. 当storefile文件的数量增长到一定阈值后,系统会进行合并(minor、 major compaction),在合并过程中会进行版本合并和删除工作 (majar),形成更大的storefile。
  5. 当一个region所有storefile的大小和超过一定阈值后,会把当前的region 分割为两个,并由hmaster分配到相应的regionserver服务器,实现负载均衡。
  6. 客户端检索数据,先在memstore找,找不到再找storefile
  7. Region是HBase中分布式存储和负载均衡的最小单元。最小单元就表 示不同的HRegion可以分布在不同的Region server上。
  8. Region由一个或者多个Store组成,每个store保存一个columns family。
  9. 每个Strore又由一个memStore和0至多个StoreFile组成。
  10. 如图:StoreFile 以HFile格式保存在HDFS上。

Hbase介绍_第2张图片
Hbase介绍_第3张图片

5.HLog(WAL log):

每个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件中,HLog文件定期会滚动出新,并删除旧的文件(已持久化到Storefile中的数据),当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的HLog文件,将不同region的log数据拆分,分别放在相应region目录下,然后再将失效的region(带有刚刚拆分的log)重新分配,领取到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到Memstore中,然后flush到StoreFile,完成数据恢复。

7.hbase:meta

hbase:meta(以前叫.META.)是之前说过的命名空间 hbase中的一张表。记录了全部表的所有region相关信息,如region位于哪个Region Server上。 重点:hbase:meta表的位置信息存储在Zookeeper中!!!

hbase:meta(.meta.)表结构

Hbase介绍_第4张图片

Key:

Region key的格式是:[table],[region start key],[region id]

Values:

info:regioninfo: 序列化的当前region的HRegionInfo实例。

info:server:存储这个region的regionserver的server:port

info:serverstartcode:该Regionserver拥用该region的起始时间

数据模型

hbase在使用之前无需定义一个固定的表结构,同一个表中不同行数据可以包含不同的列。Hbase很适合存储不确定列、不确定大小的半结构化数据。

逻辑模型

逻辑视图

RowKey:是Byte array,是表中每条记录的“主键”,方便快速查找,Rowkey的设计非常重要;

Column Family:列族,拥有一个名称(string),包含一个或者多个相关列;

Column:属于某一个columnfamily,

familyName:columnName,每条记录可动态添加;

Version Number:HBase中通过row和columns确定的为一个存贮单元称为cell。每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。

Value(Cell):由*{row key, column(* = + ), version} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。

Hbase介绍_第5张图片

以关系型数据的思维下会感觉,上面的表格是一个5列4行的数据表格,但是在HBase中这种理解是错误的,其实在HBase中上面的表格只是一行数据;

物理模型

每个column family存储在HDFS上的一个单独文件中,空值不会被保存。

HBase是一个列式存储数据库,数据按列族聚簇存储在存储文件(StoreFile)中,在逻辑视图上面有些列是空白的,空白的列单元格不会被存储,当请求这些空白的单元格时,会返回null值。如果在查询的时候不提供时间戳,那么会返回距离现在最近的那一个版本的数据,因为在存储的时候,数据会按照时间戳来排序。

(1)HBase中表按照行键的范围被划分为不同的分区(Region),各个分区由分区服务器负责管理并提供数据读写服务,HBase主节点进程(HMaster)负责分区的分配以及在集群中的迁移。

(2)一个分区同时有且仅由一个分区服务器提供服务。当分区增长到配置的大小后,如果开启了自动拆分(也可以手动拆分或者建表时预先拆分),则分区服务器会负责将这个分区拆分成两个。

每个分区都有一个唯一的分区名,格式是“<表名,startRowKey,创建时间>”。一个分区下的每个列族都会有一个存储仓库(Store),因此一个表有几个列族,那么每个分区就会有几个存储仓库。

(3)每个Store(存储仓库)有且仅有一个MemStore(内存仓库),但是可以有多个存储文件。当分区服务器处理写入请求时,数据的变更操作在写入WAL后,会先写入MemStore,同时在内存中按行键排序。

当MemStore到达配置的大小或者集群中所有MemStore使用的总内存达到配置的阈值百分比时,MemStore会刷新为一个StoreFile(存储文件)到磁盘,存储文件只会顺序写入,不支持修改。

(4)数据块(block)是HBase中数据读取的最小单元,StoreFile由数据块组成,可以在建表时按列族指定表数据的数据块大小。如果开启了HBase的数据压缩功能,数据在写入StoreFile之前会按数据块进行压缩,读取时同样对数据块解压后再放入缓存。理想情况下,每次读取数据的大小都是指定的数据块大小的倍数,这样可以避免一些无效的IO,效率最高。

Hbase介绍_第6张图片
Hbase介绍_第7张图片

Hbase基本操作

  1. 启动 shell 界面命令:hbase shell
  2. 查看所有表的命令(这个命令不会列出Hbase:meta和Hbase:namespace这两个表)
list

Hbase介绍_第8张图片

  1. 查看表结构信息命令
describe 'tableName'desc 'tableName'

Hbase介绍_第9张图片

  1. 插入数据
put ‘tableName’,‘rowKey’,‘列族:列名’,‘value

在这里插入图片描述

  1. 查询数据(单行)
get 'tableName','rowKey'

在这里插入图片描述

  1. 按指定条件获取一批数据
scan 'tableName' 

不指定条件得情况下查询全表。一般可设定起始和结束的rowKey、查询的条数、是否排序等。

  1. 删除数据
delete ‘表’,‘行’,‘列’,时间戳

Hbase介绍_第10张图片

hbase生存期TTL的设置

即生存期。TTL是作用于列族的,它设置了一个基于时间戳的临界值, 内部的管理会自动检查TTL值是否达到上限,在major合并过程中时间戳被判定为超过TTL的数据会被自动删除

1.建表时直接指定TTL

create 'hbase_test',{NAME => 'cf', TTL=>'86400'},{NAME => 'data'}

2.在已存在表上指定

alter "hbase_test",NAME=>'data',TTL=>'86400' #设置TTL值,作用于列族data

zookeeper中Hbase节点含义

在这里插入图片描述

[meta-region-server, rs, splitWAL, backup-masters, flush-table-proc, master-maintenance, online-snapshot, switch, master, running, draining, namespace, hbaseid, table]

序号 zookeeper节点名 节点含义
1 meta-region-server 存储HBase集群hbase:meta元数据表所在的RegionServer访问地址。客户端读写数据首先会从此节点读取hbase:meta元数据的访问地址,将部分元数据加载到本地,根据元数据进行数据路由。
2 rs/[host-name] 每个子znode包含各RegionServer的信息
3 master 其中包含当前活动(即赢得选举)的HMaster信息
4 hbaseid 集群id
5 table 集群中各个表信息
6 splitWAL replay过程中等待切分的日志路径
7 namespace habse的namespace
8 online-snapshot 用来实现在线snapshot操作。表级别在线snapshot同样是一个分布式操作,需要对目标表的每个Region都执行snapshot,全部成功之后才能返回成功。Master作为控制节点给各个相关RegionServer下达snapshot命令,对应RegionServer对目标Region执行snapshot,成功后通知Master。Master下达snapshot命令、RegionServer反馈snapshot结果都是通过ZooKeeper完成的。
9 backup-masters/[host-name] 每个子znode包含当前作为热备的HMaster信息
10 draining 当若干个regionserver同时下线时,将要下线的regionserver以临时节点的方式存储在这里,避免要下线的region转移到另个一要下线的regionserver上面
8 master-maintenance 暂未找到明确含义
12 running 同上
13 switch 同上
14 flush-table-proc 同上

hdfs路径含义

序号 路径名称 路径含义
1 /hbase/data hbase 的核心目录,0.98版本里支持 namespace 的概念模型,系统会预置两个 namespace 即:hbase和default
2 /hbase/data/hbase/namespace 中存储了 HBase 中的所有 namespace 信息,包括预置的hbase 和 default。
3 /hbase/data/hbase/meta hbase的元数据表,存储了region的位置信息
4 hbase/data/default/ 存储所有用户数据表/hbase/data/default/表名
5 /hbase/.tmp 这个目录用来存储临时文件,当对表进行操作的时候,首先会将表移动到该目录下,然后再进行操作。比如,表创建的时候首先会在tmp目录下执行,执行成功后再将tmp目录下的表信息移动到实际表目录下。表删除操作会将表目录移动到tmp目录下,一定时间过后再将tmp目录下的文件真正删除
6 /hbase/hbase.id 存储集群唯一的 cluster id 号,是一个 uuid。
7 /hbase/WALs 存储集群中所有RegionServer的HLog日志文件
8 /hbase/oldWALs 当/hbase/WALs 中的HLog文件被持久化到存储文件中,不再需要日志文件时,它们会被移动到/hbase/oldWALs目录。
9 /hbase/hbase.version HBase软件版本文件,代码静态版本
10 /hbase/corrupt 存储损坏的HLog文件或者HFile文件
11 /hbase/archive 存储表的归档和快照,HBase 在做 Split或者 compact 操作完成之后,会将之前的HFile 移到archive 目录中,该目录由 HMaster 上的一个定时任务定期去清理。存储表的归档和快照具体目录:/hbase/archive/data/default/表名/region名/列族名/fd2221d8d1ae4e579c21882f0ec4c5a5,这个目录中的文件会有定时任务定时进行清理。
12 .tabledesc 表描述文件,记录对应表的基本schema信息。
13 .tmp: 表临时目录,主要用来存储Flush和Compaction过程中的中间结果。以f lush为例,MemStore中的KV数据落盘形成HFile首先会生成在.tmp目录下,一旦完成再从.tmp目录移动到对应的实际文件目录。
14 .regioninfo Region描述文件。
15 recovered.edits 存储故障恢复时该Region需要回放的WAL日志数据。RegionServer宕机之后,该节点上还没有来得及flush到磁盘的数据需要通过WAL回放恢复,WAL文件首先需要按照Region进行切分,每个Region拥有对应的WAL数据片段,回放时只需要回放自己的WAL数据片段即可。

HBase数据的读取流程

1.Client访问zookeeper,获取元数据存储所在的regionserver

2.通过刚刚获取的地址访问对应的regionserver,拿到对应的表存储的regionserver

3.去表所在的regionserver进行数据的读取

4.查找对应的region,在region中寻找列族,先找到memstore,找不到去blockcache中寻找,再找不到就进行storefile的遍历

5.找到数据之后会先缓存到blockcache中,再将结果返回

blockcache逐渐满了之后,会采用LRU的淘汰策略。

HBase数据的写入过程

1、Client访问zookeeper,获取元数据存储所在的regionserver

2、通过刚刚获取的地址访问对应的regionserver,拿到对应的表存储的regionserver

3、去表所在的regionserver进行数据的添加

4、查找对应的region,在region中寻找列族,先向memstore中写入数据

5、当memstore写入的值变多,触发溢写操作(flush),进行文件的溢写,成为一个StoreFile

6、当溢写的文件过多时,会触发文件的合并(Compact)操作,合并有两种方式(major,minor)(多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除)

  • minor compaction:小范围合并,默认是3-10个文件进行合并,不会删除其他版本的数据。
  • major compaction:将当前目录下的所有文件全部合并,一般手动触发,会删除其他版本的数据(不同时间戳的)

7、当region中的数据逐渐变大之后,达到某一个阈值,会进行裂变(一个region等分为两个region,并分配到不同的regionserver),原本的Region会下线,新Split出来的两个Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。

Hbase启动时Zookeeper作用

1.Hbase启动时需要读取zookeeper中meta-region-server,以获取Hbase的meta信息(位置信息)。

2.拿到meta的位置信息后,到指定的regionserver读取元数据信息。

3.加载元数据信息中的namespace,加载表。

线上数据迁移问题复现

复现步骤

1.将Hbase集群服务停止。

2.将zookeeper的meta-region-server节点删除。

3.启动Hbase集群,在Hbase shell客户端执行查询操作。
Hbase介绍_第11张图片

4.查看日志,发现HMaster启动失败。
Hbase介绍_第12张图片

5.复现步骤结束,得到与线上一致的现象与报错。

6.对比删除zookeeper中meta-region-server节点前后的数据

前:

[zk: localhost:2181(CONNECTED) 3] get /hbase/meta-region-server
�master:16000�Ciq�(PBUF

bigdata06�}�Ϯ��/ 

cZxid = 0x10000035d
ctime = Thu Mar 18 20:34:46 CST 2021
mZxid = 0x2400001ff9
mtime = Wed Nov 03 17:45:32 CST 2021
pZxid = 0x10000035d
cversion = 0
dataVersion = 54
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 56
numChildren = 0

后:

[zk: localhost:2181(CONNECTED) 7] get /hbase/meta-region-server   
?master:16000?~7??1?PBUF

	bigdata07?}????/ 

cZxid = 0x240000206e
ctime = Wed Nov 03 17:54:55 CST 2021
mZxid = 0x2400002070
mtime = Wed Nov 03 17:54:56 CST 2021
pZxid = 0x240000206e
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 56
numChildren = 0

得到结论

由于/hbase/meta-region-server不存在导致获取不到hbase:meta的位置信息(首次启动时可能因为region发生位置变化导致找不到meta的region)。即在加载namespcace时,无法获取namespace信息,导致启动Hbase失败。

修复元数据

利用hbase-client中操作zookeeper的方法,构造相同的参数,使用hbase-client去读写zookeeper节点。

	/**
     * 构造访问zookeeper的Watcher
     */
    public static ZKWatcher getZooKeeperWatcher(String quorum) throws IOException {

        //conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, "2181");
        //conf.set(HConstants.CLIENT_ZOOKEEPER_QUORUM,"bigdata01");
        conf.set(HConstants.ZOOKEEPER_QUORUM,quorum);
        if (zooKeeperWatcher == null) {
            zooKeeperWatcher = new ZKWatcher(conf, "testing utility", new Abortable() {
                @Override
                public void abort(String why, Throwable e) {
                    throw new RuntimeException("Unexpected abort in HBaseZKTestingUtility:" + why, e);
                }

                @Override
                public boolean isAborted() {
                    return false;
                }
            });
        }
        return zooKeeperWatcher;
    }
	/**
     * 参数一 zookeeper连接信息
     * 参数二 元数据位置ServerName
     * 参数三 ENCODED
     */
    public static void main(String[] args) throws IOException, InterruptedException, KeeperException, DeserializationException {
        System.out.println("开始>>>>>>>>>>>>>>>>>>>>>");
        RegionState regionState;

        ZKWatcher zooKeeperWatcher = getZooKeeperWatcher(args[0]);

        // 写入
        ServerName serverName = ServerName.valueOf(args[1]);
        RegionState.State state = RegionState.State.valueOf("OPEN");
        int replicaId = Integer.parseInt(args[2]);

        if (serverName == null) {
            log.warn("Tried to set null ServerName in hbase:meta; skipping -- ServerName required");
            return;
        }
        log.info("Setting hbase:meta replicaId={} location in ZooKeeper as {}, state={}", replicaId,
                serverName, state);
        // Make the MetaRegionServer pb and then get its bytes and save this as
        // the znode content.
        MetaRegionServer pbrsr = MetaRegionServer.newBuilder()
                .setServer(ProtobufUtil.toServerName(serverName))
                .setRpcVersion(HConstants.RPC_CURRENT_VERSION)
                .setState(state.convert()).build();
        byte[] setData = ProtobufUtil.prependPBMagic(pbrsr.toByteArray());
        try {
            ZKUtil.setData(zooKeeperWatcher,
                    "/hbase/meta-region-server", setData);
        } catch(KeeperException.NoNodeException nne) {
            if (replicaId == RegionInfo.DEFAULT_REPLICA_ID) {
                log.info("hbase:meta region location doesn't exist, create it");
            } else {
                log.info("hbase:meta region location doesn't exist for replicaId=" + replicaId +
                        ", create it");
            }
            ZKUtil.createAndWatch(zooKeeperWatcher, "/hbase/meta-region-server",
                    setData);
        }
        System.out.println(">>>>>>>>>>>>元数据写入完成");

         // 读取
        System.out.println(">>>>>>>>>>>>元数据读取");
        byte[] data = ZKUtil.getData(zooKeeperWatcher, "/hbase/meta-region-server");
        regionState = ProtobufUtil.parseMetaRegionStateFrom(data, 0);
        System.out.println(">>>>>>>>>>>>>>>>" + regionState);
    }

你可能感兴趣的:(hbase,hbase,数据库,zookeeper)