Hbase

目录

1 概述

1.1 HBase 数据模型

1.1.1 HBase 逻辑结构

1.1.2 HBase 物理存储结构

1.1.3 数据模型

1.2 HBase 基本架构

2 HBase 快速入门

2.1 HBase 安装部署

hadoop3.X和Hbase2.X不兼容问题:

2.2 基本操作

3 HBase API

3.1 环境准备

3.2 创建连接

3.2.1 单线程创建连接

3.2.2 多线程创建连接(推荐)

3.3.3 DDL

3.3.4 DML

4 深层

4.1 Master

4.2 RegionServer 架构

4.3 写流程

4.4 刷写机制

4.5 读流程

4.5.1 HFile 结构

4.5.2 读流程

4.5.3 合并读取数据优化

4.6 StoreFile Compaction

4.7 Region Split

5 优化(调参)

5.1 RowKey 设计

5.4 HBase 使用经验法则

6 整合 Phoenix

6.1 安装

6.2 Phoenix Shell 操作

!table 或 !tables 显示所有表

创建表

插入数据

查询记录

删除记录

删除表

退出命令行 !quit

6.3 表的映射

6.4 数字类型说明

6.5 jdbc连接

7 二级索引

7.1 二级索引配置文件

7.2 全局索引(global index)

8 整合hive

1 概述

Apache HBase™ 是以 hdfs 为数据存储的,一种分布式、可扩展的 NoSQL 数据库

1.1 HBase 数据模型

HBase 的设计理念依据 Google 的 BigTable 论文,论文中对于数据模型的首句介绍:

Bigtable 是一个稀疏的、分布式的、持久的多维排序 map

之后对于映射的解释如下:

该映射由行键、列键和时间戳索引;映射中的每个值都是一个未解释的字节数组

最终 HBase 关于数据模型和 BigTable 的对应关系如下:

        HBase 使用与 Bigtable 非常相似的数据模型。用户将数据行存储在带标签的表中。数据行具有可排序的键和任意数量的列。该表存储稀疏,因此如果用户喜欢,同一表中的行可以具有疯狂变化的列。

        最终理解 HBase 数据模型的关键在于稀疏、分布式、多维、排序的映射。其中映射 map 指代非关系型数据库的 key-Value 结构。

1.1.1 HBase 逻辑结构

HBase 可以用于存储多种结构的数据,以 JSON 为例,存储的数据原貌为

{
  "row_key1": {
    "personal_info": {
      "name": "zhangsan","city": "北京","phone": "131********"
    },"office_info": {
      "tel": "010-1111111","address": "atguigu"
    }
  },"row_key11": {
    "personal_info": {
      "city": "上海","phone": "132********"
    },"office_info": {
      "tel": "010-1111111"
    }
  },"row_key2": {
    ......
  }
}

        存储数据稀疏,数据存储多维,不同的行具有不同的列。 数据存储整体有序,按照RowKey的字典序排列,RowKey为Byte数组

1.1.2 HBase 物理存储结构

物理存储结构即为数据映射关系,而在概念视图的空单元格,底层实际根本不存储

1.1.3 数据模型

1)Name Space

        命名空间,类似于关系型数据库的 database 概念,每个命名空间下有多个表。HBase 两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default表是用户默认使用的命名空间。

2)Table

        类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需 要声明具体的列。因为数据存储时稀疏的,所有往 HBase 写入数据时,字段可以动态、按需 指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。

3)Row

        HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重 要。

4)Column

        HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限 定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。

5)Time Stamp

        用于标识数据的不同版本(version),每条数据写入时,系统会自动为其加上该字段, 其值为写入 HBase 的时间。

6)Cell

        由{rowkey,column Family:column Qualifier,timestamp} 唯一确定的单元。cell 中的数 据全部是字节码形式存贮。

1.2 HBase 基本架构

1)Master

        实现类为 HMaster,负责监控集群中所有的 RegionServer 实例。主要作用如下:

(1)管理元数据表格 hbase:meta,接收用户对表格创建修改删除的命令并执行

(2)监控 region 是否需要进行负载均衡,故障转移和 region 的拆分。

通过启动多个后台线程监控实现上述功能:

①LoadBalancer 负载均衡器

        周期性监控 region 分布在 regionServer 上面是否均衡,由参数 hbase.balancer.period 控 制周期时间,默认 5 分钟。

②CatalogJanitor 元数据管理器

        定期检查和清理 hbase:meta 中的数据。meta 表内容在进阶中介绍。

③MasterProcWAL master 预写日志处理器

        把 master 需要执行的任务记录到预写日志 WAL 中,如果 master 宕机,让 backupMaster 读取日志继续干。

2)Region Server

Region Server 实现类为 HRegionServer,主要作用如下:

(1)负责数据 cell 的处理,例如写入数据 put,查询数据 get 等

(2)拆分合并 region 的实际执行者,有 master 监控,有 regionServer 执行。

3)Zookeeper

        HBase 通过 Zookeeper 来做 master 的高可用、记录 RegionServer 的部署信息、并且存储 有 meta 表的位置信息。

        HBase 对于数据的读写操作时直接访问 Zookeeper 的,在 2.3 版本推出 Master Registry 模式,客户端可以直接访问 master。使用此功能,会加大对 master 的压力,减轻对 Zookeeper 的压力。

4)HDFS

        HDFS 为 Hbase 提供最终的底层数据存储服务,同时为 HBase 提供高容错的支持

2 HBase 快速入门

2.1 HBase 安装部署

1. zookeeper、hadoop部署

2. hbase解压,配置

https://www.apache.org/dyn/closer.lua/hbase/2.4.17/hbase-2.4.17-bin.tar.gz

需要哪个版本直接改数字

hadoop3.X和Hbase2.X不兼容问题:

<property>
  <name>hbase.wal.provider</name>
  <value>filesystem</value>
</property>

3. 环境变量

#HBASE_HOME

export HBASE_HOME=/opt/module/hbase

export PATH=$PATH:$HBASE_HOME/bin

4. 配置文件

hbase-env.sh 修改内容,可以添加到最后:

export JAVA_HOME=/自己的java路径

export HBASE_MANAGES_ZK=false

hbase-site.xml 修改内容

  </configuration>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>

  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>hadoop1,hadoop2,hadoop3</value>
    <description>The directory shared by RegionServers.</description>
  </property>

  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://hadoop1:8020/hbase</value>
    <description>The directory shared by RegionServers.</description>
  </property>
<!-- <property>-->
<!-- <name>hbase.zookeeper.property.dataDir</name>-->
<!-- <value>/export/zookeeper</value>-->
<!-- <description> 记得修改 ZK 的配置文件 -->
<!-- ZK 的信息不能保存到临时文件夹-->
<!-- </description>-->
<!-- </property>-->
  <property>
    <name>hbase.wal.provider</name>
    <value>filesystem</value>
  </property>
</configuration>

regionservers

hadoop1

hadoop2

hadoop3

 解决 HBase 和 Hadoop 的 log4j 兼容性问题,修改 HBase 的 jar 包,使用 Hadoop 的 jar 包

mv $HBASE_HOME/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar $HBASE_HOME/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar.bak

分发

5. 原神启动

先启动hadoop 启动zk

1)单点启动

bin/hbase-daemon.sh start master

bin/hbase-daemon.sh start regionserver

2)群启

bin/start-hbase.sh

bin/stop-hbase.sh

http://hadoop1:16010/master-status

2.2 高可用(可选)

        在 HBase 中 HMaster 负责监控 HRegionServer 的生命周期,均衡 RegionServer 的负载, 如果 HMaster 挂掉了,那么整个 HBase 集群将陷入不健康的状态,并且此时的工作状态并不 会维持太久。所以 HBase 支持对 HMaster 的高可用配置。

2.2 基本操作

用不到的shell

进入命令行

bin/hbase shell

 help "COMMAND" 帮助命令

namespace

Group name: namespace
Commands: alter_namespace,create_namespace,describe_namespace,drop_namespace,list_namespace,list_namespace_tables

系统的命名空间不使用 

ddl

  Group name: ddl
  Commands: alter,alter_async,alter_status,clone_table_schema,create,describe,disable,disable_all,drop,drop_all,enable,enable_all,exists,get_table,is_disabled,is_enabled,list,list_regions,locate_region,show_filters

list自动过滤掉了系统两个表

创建表语句,不写命名空间默认default空间

甄姬8复杂,需要自己help看去

删除数据的方法有两个:delete 和 deleteall。

delete 表示删除一个版本的数据,即为 1 个 cell,不填写版本默认删除最新的一个版本。

hbase:026:0> delete 'bigdata:student','1001','info:name'

deleteall 表示删除所有版本的数据,即为当前行当前列的多个 cell。(执行命令会标记 数据为要删除,不会直接将数据彻底删除,删除数据只在特定时期清理磁盘时进行)

3 HBase API

3.1 环境准备

    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-server</artifactId>
      <version>2.4.11</version>
      <exclusions>
        <exclusion>
          <groupId>org.glassfish</groupId>
          <artifactId>javax.el</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.el</artifactId>
      <version>3.0.1-b06</version>
    </dependency>

3.2 创建连接

        HBase 的客户端连接由 ConnectionFactory 类来创建,用户使用完成 之后需要手动关闭连接。同时连接是一个重量级的,推荐一个进程使用一个连接,对 HBase 的命令通过连接中的两个属性 Admin 和 Table 来实现。

3.2.1 单线程创建连接

package org.example;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import java.io.IOException;

public class c1_connection1 {
    public static void main(String[] args) throws IOException {
        Configuration conf = new Configuration();
        conf.set("hbase.zookeeper.quorum","hadoop1,hadoop3");

//        同步连接
        Connection connection = ConnectionFactory.createConnection(conf);

//        异步连接
//        CompletableFuture<AsyncConnection> asyncConnection = ConnectionFactory.createAsyncConnection();

        System.out.println(connection);

        connection.close();
    }
}

hconnection-0x4802796d
 

3.2.2 多线程创建连接(推荐)

使用类单例模式,确保使用一个连接,可以同时用于多个线程。

public class c2_connection_mul {
    static Connection connection = null;
    static {
        // 把linux的hbase-site.xml导入resources就可以不用配置了
//        Configuration conf = new Configuration();
//        conf.set("hbase.zookeeper.quorum",hadoop3");
        try {
            connection = ConnectionFactory.createConnection();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void closeConnection() throws IOException {
        if (connection != null) {
            connection.close();
        }
    }

    public static void main(String[] args) throws IOException {
        System.out.println(connection);
        closeConnection();

    }
}

3.3.3 DDL

package org.example;

import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;
import java.io.StringReader;

public class c3_DDL {
    static Connection connection = c2_connection_mul.connection;

    public static void createNamespace(String namespace) throws IOException {
        Admin admin = connection.getAdmin();
        NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);
        builder.addConfiguration("user","yuange");  // 没实际含义

        try {
            admin.createNamespace(builder.build());
        }catch (IOException e){
            System.out.println("admin.createNamespace(builder.build());  error");
            e.printStackTrace();
        }

        admin.close();
    }

    public static boolean isTableExist(String namespace,String tablename) throws IOException {
        Admin admin = connection.getAdmin();
        boolean b = false;
        try {
            b = admin.tableExists(TableName.valueOf(namespace,tablename));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        admin.close();
        return b;
    }

    public static void createTable(String namespace,String tablename,String... columnFamilies) throws IOException {
        if (columnFamilies.length == 0){
            System.out.println("妹有列族");
            return;
        }
        if(isTableExist(namespace,tablename)){
            System.out.println("表格存在了");
            return;
        }

        Admin admin = connection.getAdmin();

        TableDescriptorBuilder tableDescriptorBuilder =
                TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace,tablename));
        for (String columnFamily : columnFamilies) {
            ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder =
                    ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));
            columnFamilyDescriptorBuilder.setMaxVersions(3);

            tableDescriptorBuilder.addColumnFamily(columnFamilyDescriptorBuilder.build());
        }
        try {
            admin.createTable(tableDescriptorBuilder.build());
        } catch (IOException e) {
            System.out.println("表格已经存在");
            throw new RuntimeException(e);
        }
    }

    public static void modifyTable(String namespace,String columnFamily,int ver) throws IOException {
        if (!isTableExist(namespace,tablename)){
            System.out.println("表格不存在");
            return;
        }

        Admin admin = connection.getAdmin();
        try {
            TableDescriptor descriptor = admin.getDescriptor(TableName.valueOf(namespace,tablename));



            // 2  (如果填写tablename的方法是创建了一个新的表格描述建造器,妹有之前的信息)
//        TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace,tablename));
            TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(descriptor);


            // 4
            ColumnFamilyDescriptor columnFamily1 = descriptor.getColumnFamily(Bytes.toBytes(columnFamily));

            ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);
            columnFamilyDescriptorBuilder.setMaxVersions(ver);
            // 3 这个上面有两个 准备句 4 (如果新创建,那别的参数就初始化了,改上边两句)
            tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());
            // 1 这个上面有两个 2 3
            admin.modifyTable(tableDescriptorBuilder.build());
        } catch (IOException e) {
            System.out.println("admin.modifyTable(tableDescriptorBuilder.build()); error");
            throw new RuntimeException(e);
        }

        admin.close();

    }

    public static boolean deleteTable(String namespace,String tablename) throws IOException {
        if(!isTableExist(namespace,tablename)){
            System.out.println("表格不存在  无法删除");
            return false;
        }

        Admin admin = connection.getAdmin();
        try {
            TableName tableName = TableName.valueOf(namespace,tablename);
            // 标记删除之前要标记为不可用
            admin.disableTable(tableName);
            admin.deleteTable(tableName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        admin.close();
        return true;
    }



    public static void main(String[] args) throws IOException {
        // 创建命名空间
//        createNamespace("test");
        // 判断表格存在
//        boolean exist = isTableExist("yuange","student");
//        System.out.println(exist);

//        createTable("yuange","student2","info","msg");

        deleteTable("yuange","student2");
        c2_connection_mul.closeConnection();
    }
}

3.3.4 DML

写入删除查找数据

package org.example;

import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.ColumnValueFilter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;

import java.io.IOException;

public class c4_DML {
    public static Connection connection = c2_connection_mul.connection;
    public static String namespace = "yuange";

    public static void putCell(String namespace,String rowkey,String colFamily,String colName,String value) throws IOException {
        // 对表格内容进行修改使用Table
        Table table = connection.getTable(TableName.valueOf(namespace,tablename));
        // 开调api 放一个行键,在放列族 列名 值
        Put put = new Put(Bytes.toBytes(rowkey));
        put.addColumn(Bytes.toBytes(colFamily),Bytes.toBytes(colName),Bytes.toBytes(value));
        table.put(put);
        table.close();
    }
    public static void get(String namespace,String colName) throws IOException {
        Table table = connection.getTable(TableName.valueOf(namespace,tablename));

        Get get = new Get(Bytes.toBytes(rowkey));
        // 不加就读一整行
//        get.addColumn(Bytes.toBytes(colFamily),Bytes.toBytes(colName));
        get.readAllVersions();
        Result result = null;
        try {
            result = table.get(get);
            Cell[] cells = result.rawCells();
            for (Cell cell : cells) {
                String s = new String(CellUtil.cloneValue(cell));
                System.out.println(s);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        table.close();
    }

    public static void scan(String namespace,String startrow,String endrow) throws IOException {
        Table table = connection.getTable(TableName.valueOf(namespace,tablename));
        Scan scan = new Scan();
        // scan不配置,直接扫描整个表
        scan.withStartRow(Bytes.toBytes(startrow));
        scan.withStopRow(Bytes.toBytes(endrow),false);

        ResultScanner scanner = null;
        try {
            scanner = table.getScanner(scan);
            for (Result result : scanner) {
                for (Cell cell : result.rawCells()) {
                    System.out.print(new String(CellUtil.cloneRow(cell))+"-"+new String(CellUtil.cloneFamily(cell))+"-"+
                            new String(CellUtil.cloneQualifier(cell))+"-"+ new String(CellUtil.cloneValue(cell)));
                }
                System.out.println();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        table.close();
    }

    public static void filterScan(String namespace,String colname,String value,tablename));

        // 添加过滤多个
        FilterList filterList = new FilterList();

        // 添加过滤器1
        // 结果只保留当前列
//        ColumnValueFilter columnValueFilter = new ColumnValueFilter(Bytes.toBytes(colFamily),Bytes.toBytes(colname),//                CompareOperator.EQUAL,Bytes.toBytes(value));
//        filterList.addFilter(columnValueFilter);

        // 添加过滤器2
        // 结果只保留当前行
        // 并且放弃过滤没有这个值的行,也打出来
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes(colFamily),CompareOperator.EQUAL,Bytes.toBytes(value));
        filterList.addFilter(singleColumnValueFilter);

        Scan scan = new Scan();

        // scan不配置,直接扫描整个表
        scan.withStartRow(Bytes.toBytes(startrow));
        scan.withStopRow(Bytes.toBytes(endrow),false);
        scan.setFilter(filterList);

        ResultScanner scanner = null;
        try {
            scanner = table.getScanner(scan);
            for (Result result : scanner) {
                for (Cell cell : result.rawCells()) {
                    System.out.print(new String(CellUtil.cloneRow(cell))+"-"+new String(CellUtil.cloneFamily(cell))+"-"+
                            new String(CellUtil.cloneQualifier(cell))+"-"+ new String(CellUtil.cloneValue(cell)));
                }
                System.out.println();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        table.close();
    }


    public static void delCol(String namespace,String colname) throws IOException {
        Table table = connection.getTable(TableName.valueOf(namespace,tablename));

        Delete delete = new Delete(Bytes.toBytes(rowkey));
        // 删除一个版本
        delete.addColumn(Bytes.toBytes(colFamily),Bytes.toBytes(colname));
        // 删除所有ver
//        delete.addColumns(Bytes.toBytes(colFamily),Bytes.toBytes(colname));
        table.delete(delete);
        table.close();
    }

    public static void main(String[] args) throws IOException {

        scan(namespace,"student","1","4");
        delCol(namespace,"3","name","ming");
        scan(namespace,"4");


        System.out.println("asda");
        connection.close();
    }
}

4 深层

4.1 Master

元数据表:

        全称 hbase:meta,只是在 list 命令中被过滤掉了,本质上和 HBase 的其他表格一样。 RowKey:

        ([table],[region start key],[region id]) 即 表名,region 起始位置和 regionID。

列:

         info:regioninfo 为 region 信息,存储一个 HRegionInfo 对象。

        info:server 当前 region 所处的 RegionServer 信息,包含端口号。

        info:serverstartcode 当前 region 被分到 RegionServer 的起始时间。

        如果一个表处于切分的过程中,即 region 切分,还会多出两列info:splitA 和 info:splitB, 存储值也是 HRegionInfo 对象,拆分结束后,删除这两列。

注意:在客户端对元数据进行操作的时候才会连接 master,如果对数据进行读写,直接连接zookeeper 读取目录/hbase/meta-region-server 节点信息,会记录 meta 表格的位置。直接读 取即可,不需要访问 master,这样可以减轻 master 的压力,相当于 master 专注 meta 表的 写操作,客户端可直接读取 meta 表

        在 HBase 的 2.3 版本更新了一种新模式:Master Registry。客户端可以访问 master 来读取 meta 表信息。加大了 master 的压力,减轻了 zookeeper 的压力。

4.2 RegionServer 架构

数据有序,所以需要中间的缓存缓冲区

1)MemStore

        写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好 序后,等刷写时才会刷写到 HFile,每次刷写都会形成一个新的 HFile,写入到对应的 文件夹 store 中。

2)WAL

        由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的 概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件 中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。

3)BlockCache

        读缓存,每次查询出的数据会缓存在 BlockCache 中,方便下次查询。

4.3 写流程

写流程从客户端创建指令开始, 到最终刷写落盘到hdfs上结束

(1)首先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server;

(2)访问对应的 Region Server,获取 hbase:meta 表,将其缓存到连接中,作为连接属 性 MetaCache,由于 Meta 表格具有一定的数据量,导致了创建连接比较慢; 之后使用创建的连接获取 Table,这是一个轻量级的连接,只有在第一次创建的时候会 检查表格是否存在访问 RegionServer,之后在获取 Table 时不会访问 RegionServer;

(3)调用Table的put方法写入数据,此时还需要解析RowKey,对照缓存的MetaCache, 查看具体写入的位置有哪个 RegionServer;

(4)将数据顺序写入(追加)到 WAL,此处写入是直接落盘的,并设置专门的线程控 制 WAL 预写日志的滚动(类似 Flume);

(5)根据写入命令的 RowKey 和 ColumnFamily 查看具体写入到哪个 MemStory,并且 在 MemStory 中排序;

(6)向客户端发送 ack;

(7 )等达到 MemStore 的刷写时机后,将数据刷写到对应的 story 中。

4.4 刷写机制

MemStore 刷写由多个线程控制,条件互相独立:

主要的刷写规则是控制刷写文件的大小,在每一个刷写线程中都会进行监控:

(1)当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size(默认值 128M), 其所在 region 的所有 memstore 都会刷写。

当 memstore 的大小达到了

hbase.hregion.memstore.flush.size(默认值 128M)

* hbase.hregion.memstore.block.multiplier(默认值 4)

时,会刷写,同时阻止继续往该 memstore 写数据(由于线程监控是周期性的,所有有可能面对数据洪峰,尽管可能性比较小)

(2)由 HRegionServer 中的属性 MemStoreFlusher 内部线程 FlushHandler 控制。标准为 LOWER_MARK(低水位线)和 HIGH_MARK(高水位线),意义在于避免写缓存使用过多的内 存造成 OOM

        当 region server 中 memstore 的总大小达到低水位线,region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。直到 region server 中所有 memstore 的总大小减小到上述值以下。

        当 region server 中 memstore 的总大小达到高水位线时,会同时阻止继续往所有的 memstore 写数据。

(3)为了避免数据过长时间处于内存之中,到达自动刷写的时间,也会触发 memstore flush。由 HRegionServer 的属性 PeriodicMemStoreFlusher 控制进行,由于重要性比较低,5min才会执行一次。自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval(默认 1 小时)。

(4)当 WAL 文件的数量超过 hbase.regionserver.max.logs,region 会按照时间顺序依次 进行刷写,直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下(该属性名已经废弃, 现无需手动设置,最大值为 32)。

4.5 读流程

4.5.1 HFile 结构

HFile 是存储在 HDFS 上面每一个 store 文件夹下实际存储数据的文件。

包括数据本身(keyValue 键值对)、元数据记录、文件信息、数据索引、元数据索引和 一个固定长度的尾部信息(记录文件的修改情况)。

        键值对按照块大小(默认 64K)保存在文件中,数据索引按照块创建,块越多,索引越 大。每一个 HFile 还会维护一个布隆过滤器(就像是一个很大的地图,文件中每有一种 key, 就在对应的位置标记,读取时可以大致判断要 get 的 key 是否存在 HFile 中)。

由于 HFile 存储经过序列化,所以无法直接查看。可以通过 HBase 提供的命令来查看存 储在 HDFS 上面的 HFile 元数据内容。

bin/hbase hfile -m -f /hbase/data/命名空间/表名/regionID/列族/HFile 名

4.5.2 读流程

4.5.3 合并读取数据优化

每次读取数据都需要读取三个位置,最后进行版本的合并。效率会非常低,所有系统需 要对此优化。

(1)HFile 带有索引文件,读取对应 RowKey 数据会比较快。

(2)Block Cache 会缓存之前读取的内容和元数据信息,如果 HFile 没有发生变化(记录在 HFile 尾信息中),则不需要再次读取。

(3)使用布隆过滤器能够快速过滤当前 HFile 不存在需要读取的 RowKey,从而避免读 取文件。(布隆过滤器使用 HASH 算法,不是绝对准确的,出错会造成多扫描一个文件,对 读取数据结果没有影响)

4.6 StoreFile Compaction

        由于 memstore 每次刷写都会生成一个新的 HFile,文件过多读取不方便,所以会进行文 件的合并,清理掉过期和删除的数据,会进行 StoreFile Compaction。

分为两种,分别是 Minor Compaction 和 Major Compaction

  • Minor Compaction 会将临近的若干个较小的 HFile 合并成一个较大的 HFile,并清理掉部分过期和删除的数据, 有系统使用一组参数自动控制,
  • Major Compaction 会将一个 Store 下的所有的 HFile 合并成 一个大 HFile,并且会清理掉所有过期和删除的数据,由参数 hbase.hregion.majorcompaction 控制,默认 7 天。

4.7 Region Split

Region 切分分为两种,创建表格时候的预分区即自定义分区,同时系统默认还会启动一 个切分规则,避免单个 Region 中的数据量太大。

每一个 region 维护着 startRow 与 endRowKey,如果加入的数据符合某个 region 维护的 rowKey 范围,则该数据交给这个 region 维护。那么依照这个原则,我们可以将数据所要投 放的分区提前大致的规划好,以提高 HBase 性能。

        Region 的拆分是由 HRegionServer 完成的,在操作之前需要通过 ZK 汇报 master,修改 对应的 Meta 表信息添加两列 info:splitA 和 info:splitB 信息。之后需要操作 HDFS 上面对 应的文件,按照拆分后的 Region 范围进行标记区分,实际操作为创建文件引用,不会挪动数据。刚完成拆分的时候,两个 Region 都由原先的 RegionServer 管理。之后汇报给 Master, 由Master将修改后的信息写入到Meta表中。等待下一次触发负载均衡机制,才会修改Region 的管理服务者,而数据要等到下一次压缩时,才会实际进行移动。

5 优化(调参)

5.1 RowKey 设计

        一条数据的唯一标识就是 rowkey,那么这条数据存储于哪个分区,取决于 rowkey 处于 哪个一个预分区的区间内,设计 rowkey的主要目的 ,就是让数据均匀的分布于所有的 region 中,在一定程度上防止数据倾斜。

  • 1)生成随机数、hash、散列值
  • 2)时间戳反转
  • 3)字符串拼接

5.4 HBase 使用经验法则

官方给出了权威的使用法则:

(1)Region 大小控制 10-50G

(2)cell 大小不超过 10M(性能对应小于 100K 的值有优化),如果使用 mob(Mediumsized Objects 一种特殊用法)则不超过 50M。

(3)1 张表有 1 到 3 个列族,不要设计太多。最好就 1 个,如果使用多个尽量保证不 会同时读取多个列族。

(4)1 到 2 个列族的表格,设计 50-100 个 Region。

(5)列族名称要尽量短,不要去模仿 RDBMS(关系型数据库)具有准确的名称和描述。

(6)如果 RowKey 设计时间在最前面,会导致有大量的旧数据存储在不活跃的 Region 中,使用的时候,仅仅会操作少数的活动 Region,此时建议增加更多的 Region 个数。

(7)如果只有一个列族用于写入数据,分配内存资源的时候可以做出调整,即写缓存 不会占用太多的内存。

6 整合 Phoenix

6.1 安装

Overview | Apache Phoenix

        Phoenix 在 5.0 版本默认提供有两种客户端使用(瘦客户端和胖客户端),在 5.1.2 版本 安装包中删除了瘦客户端,本文也不再使用瘦客户端。而胖客户端和用户自己写 HBase 的 API 代码读取数据之后进行数据处理是完全一样的。

解压、把里面的server包复制到hbase的lib

cp phoenix-server-hbase-2.4-5.1.2.jar /opt/module/hbase/lib/

幻景变量 

#phoenix

export PHOENIX_HOME=/opt/module/phoenix

export PHOENIX_CLASSPATH=$PHOENIX_HOME export PATH=$PATH:$PHOENIX_HOME/bin

重启 HBase

连接 Phoenix

/opt/module/phoenix/bin/sqlline.py hadoop1,hadoop3:2181

6.2 Phoenix Shell 操作

!table 或 !tables 显示所有表

创建表

直接指定单个列作为 RowKey

CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,name VARCHAR,age BIGINT,addr VARCHAR);

在 phoenix 中,表名等会自动转换为大写,若要小写,使用双引号,如"us_population"。

指定多个列的联合作为 RowKey

CREATE TABLE IF NOT EXISTS student1 (
id VARCHAR NOT NULL,name VARCHAR NOT NULL,addr VARCHAR
CONSTRAINT my_pk PRIMARY KEY (id,name));

注:Phoenix 中建表,会在 HBase 中创建一张对应的表。为了减少数据对磁盘空间的占 用,Phoenix 默认会对 HBase 中的列名做编码处理

若不想对列名编码,可在建表语句末尾加 上 COLUMN_ENCODED_BYTES = 0;

插入数据

upsert into student values('1001','zhangsan',10,'beijing');

查询记录

select * from student;

select * from student where id='1001';

删除记录

delete from student where id='1001';

删除表

drop table student;

退出命令行 !quit

6.3 表的映射

默认情况下, HBase 中已存在的表,通过 Phoenix 是不可见的。如果要在 Phoenix 中操 作 HBase 中已存在的表,可以在 Phoenix 中进行表的映射。

映射方式有两种:视图映射和表 映射。

        Phoenix 创建的视图是只读的,所以只能用来做查询,无法通过视图对数据进行修改等操作。在 phoenix 中创建关联 test 表的视图

create view "test"(
id varchar primary key,"info1"."name" varchar,"info2"."address" varchar);

        在 Pheonix 创建表去映射 HBase 中已经存在的表,是可以修改删除 HBase 中已经存在 的数据的。而且,删除 Phoenix 中的表,那么 HBase 中被映射的表也会被删除。

注:进行表映射时,不能使用列名编码,需将 column_encoded_bytes 设为 0。

create table"test"(
id varchar primary key,"info2"."address" varchar) 
column_encoded_bytes=0;

6.4 数字类型说明

        HBase 中的数字,底层存储为补码,而 Phoenix 中的数字,底层存储为在补码的基础上, 将符号位反转。故当在 Phoenix 中建表去映射 HBase 中已存在的表,当 HBase 中有数字类型 的字段时,会出现解析错误的现象。

        Phoenix 种提供了 unsigned_int,unsigned_long 等无符号类型,其对数字的编码解 码方式和 HBase 是相同的,如果无需考虑负数,那在 Phoenix 中建表时采用无符号类型是最 合适的选择。

        如需考虑负数的情况,则可通过 Phoenix 自定义函数,将数字类型的最高位,即 符号位反转即可,自定义函数可参考如下链接:https://phoenix.apache.org/udf.html。???3202年还有bug

6.5 jdbc连接

    <dependency>
      <groupId>org.apache.phoenix</groupId>
      <artifactId>phoenix-client-hbase-2.4</artifactId>
      <version>5.1.2</version>
    </dependency>
package org.example;

import java.sql.*;
import java.util.Properties;

public class c5_jdbc {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:phoenix:hadoop1,hadoop3:2181";

        Properties properties = new Properties();

        Connection connection = DriverManager.getConnection(url,properties);

        PreparedStatement preparedStatement = connection.prepareStatement(
                "select * from student"
        );

        ResultSet resultSet = preparedStatement.executeQuery();

        while (resultSet.next()) {
            System.out.println(resultSet.getString(1) + resultSet.getString(2) + resultSet.getString(3));

        }

        connection.close();

    }
}

7 二级索引

7.1 二级索引配置文件

添加如下配置到 HBase 的 HRegionserver 节点的 hbase-site.xml。

<!-- phoenix regionserver 配置参数-->
<property>
  <name>hbase.regionserver.wal.codec</name>
  <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>

分发,重启hbase

7.2 全局索引(global index)

        Global Index 是默认的索引格式,创建全局索引时,会在 HBase 中建立一张新表。也就 是说索引数据和数据表是存放在不同的表中的,因此全局索引适用于多读少写的业务场景。

         写数据的时候会消耗大量开销,因为索引表也要更新,而索引表是分布在不同的数据节 点上的,跨节点的数据传输带来了较大的性能消耗。

在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。

CREATE INDEX my_index ON my_table (my_col);
#例如
0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> 
create index
my_index on student1(age);

#删除索引
DROP INDEX my_index ON my_table
0: jdbc:phoenix:hadoop102,hadoop104> 
drop index my_index
on student1;

如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的 提升。若想解决上述问题,可采用如下方案:

(1)使用包含索引

创建携带其他字段的全局索引(本质还是全局索引)

(2)本地索引

Local Index 适用于写操作频繁的场景。 索引数据和数据表的数据是存放在同一张表中(且是同一个 Region),避免了在写操作 的时候往不同服务器的索引表中写索引带来的额外开销。

my_column 可以是多个。

CREATE LOCAL INDEX my_index ON my_table (my_column);

本地索引会将所有的信息存在一个影子列族中,虽然读取的时候也是范围扫描,但是没有全局索引快,优点在于不用写多个表了。

8 整合hive

在 hive-site.xml 中添加 zookeeper 的属性,如下:

<property>
 <name>hive.zookeeper.quorum</name>
 <value>hadoop102,hadoop104</value>
</property>
<property>
 <name>hive.zookeeper.client.port</name>
 <value>2181</value>
</property>

原文地址:https://blog.csdn.net/qq_58551342/article/details/134072275

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读301次。你可以使用Thrift客户端来测试HBase Thrift服务。例如,在Python中,你可以使用。请确保你的HBase伪集群已正确配置并且Thrift服务已经启动。这将在你的伪集群中启动HBase Thrift服务。库或者直接使用Thrift接口。进入HBase的安装目录,找到。请根据需要进行相应的配置。这将停止Thrift服务。_hbase 单机 thrift 配置
文章浏览阅读565次。hive和hbase数据迁移_hive转hbase
文章浏览阅读707次。基于单机版安装HBase,前置条件为Hadoop安装完成,安装Hadoop可以参考链接,Hadoop单机安装。地址:https://dlcdn.apache.org/hbase/2.4.13/hbase-2.4.13-src.tar.gz2.解压缩文件3.进入到conf目录下4.修改配置文件 hbase-env.sh示例:示例:6.修改配置文件 hbase-site.xml示例:8.访问页面访问你所以在服务器的16010端口,查看页面以上就是单机版安装HBase的内容,后续_hbase 2.4.13下载
文章浏览阅读301次。linux集群搭建-HBase_linux中在/home目录下创建目录hbase
文章浏览阅读933次。中没有库的概念,说一个数据说的是哪一个名称空间下的那一张表下的哪一个行键的哪一个列族下面的哪一个列对应的是这个数据。注意:put数据需要指定往哪个命名空间的哪个表的哪个rowKey的哪个列族的哪个列中put数据,put的值是什么。注意:put数据需要指定往哪个命名空间的哪个表的哪个rowKey的哪个列族的哪个列中put数据,put的值是什么。注意:put数据需要指定往哪个命名空间的哪个表的哪个rowKey的哪个列族的哪个列中put数据,put的值是什么。操作Hbase系统DDL,对名称空间等进行操作。_hbase中报错undefined method for main:object
文章浏览阅读1k次,点赞16次,收藏21次。整理和梳理日常hbase的监控核心指标,作为经验沉淀_hbase 对应promethus指标名
文章浏览阅读1.5k次,点赞45次,收藏20次。今天把之前学习Hbase的入门基础知识笔记翻出来了,为了不忘记也是帮助身边的小伙伴,我把他又整理了下放了出来给大家,希望对HBASE一知半解的小伙伴,能够对Hbase有一个清晰的认识,好了废话不多说,进入正题。以上内容就是初的识HBase 入门知识,包含了hbase的由来,特性,物理存储,逻辑存储模型,以及优缺点,应用场景这些内容,相信后面在使用或更深入的研究Hbase打下了良好的基础,后面的更深入的学习内容,看计划安排在后面的文章中进行更新。
文章浏览阅读655次。HDFS,适合运行在通用硬件上的分布式文件系统,是一个高度容错性的系统,适合部署在廉价的机器上。Hbase,是一个分布式的、面向列的开源数据库,适合于非结构化数据存储。MapReduce,一种编程模型,方便编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。Chukwa,是一个开源的用于监控大型分布式系统的数据收集系统。_开源非结构化数据存储
文章浏览阅读1.9k次。mongodb和hbase的区别和应用场景_hbase和mongodb的区别
文章浏览阅读1.2k次。Hbase入门篇01---基本概念和部署教程_hbase教程
文章浏览阅读1.6k次,点赞19次,收藏25次。hbase相关内容
文章浏览阅读942次,点赞16次,收藏20次。在hbase1.x中transition是令广大大数据运维人员头疼的一个话题,因为,region 的状态转移涉及到了三个核心组件,分别为:hbase master,zookeeper和hbase 的regionserver,这三个组件中的某一个region的状态都是一致的情况下,这个region 才算是正常,状态转移过程及其复杂,hbase 集群很容易出现RIT。好消息是,hbase2.x中有个工具HBCK2,这个工具可不是简单的hbase1.x中hbck 的升级,变化有点大,详细变化请参考帮助文档(
文章浏览阅读1k次。在HBase中,Region分裂是一种自动的机制,用于在Region大小达到一定阈值时将其分裂成两个Region,以便更好地管理数据。HBase中的Region大小是可以配置的,通过设置HBase表的最小和最大Region大小来控制。需要注意的是,禁止Region分裂后,当表的大小达到一定阈值时,数据将不再分裂成新的Region,因此需要根据实际需求进行调整。需要注意的是,禁止Region分裂后,当表的大小达到一定阈值时,数据将不再分裂成新的Region,因此需要根据实际需求进行调整。_hbase region大小
文章浏览阅读737次。可以看出,HBase作为数据仓库的一种补充,可以用于存储和管理大量数据,以便快速地分析和查询。是一种基于数据库的形式,用于存储和管理大量数据,以便快速地分析和查询。例如,可以使用HBase存储一些用户行为数据,然后进行分析,以便更好地了解用户行为和需求。其次,需要配置HBase相关的环境变量,例如JAVA_HOME、HBASE_HOME等。HBase可以用于存储结构化和非结构化数据,包括文本、图像、视频等。例如,可以使用HBase存储一些传感器数据,然后进行实时分析和处理。一、HBase集群环境搭建。_用hbase 搭建数仓
文章浏览阅读1.9k次。Data。_springboot整合hbase
文章浏览阅读880次,点赞23次,收藏20次。etc/abrt下的两个文件,分别是:abrt-action-save-package-data.conf 和 abrt.conf,修改内容如下。我们后面排查的时候去查看/run/cloudera-scm-agent/process/2325-hbase-REGIONSERVER下是否有。发现有个hs_err_pid15967.log JVM生成的错误日志,那么把这个日志下载查看,返现日志这么写的。接下来就等下一次hbase的节点挂了之后查看转储文件,转储文件在/var/sqool/abrt下。_regionserver 退出 没有错误日志
文章浏览阅读1.7k次。以下命令都需要在Hbase Shell中运行:Hbase信息status:服务器状态version:版本表操作查看所有表:list表基本信息:describe "表名称"查看表是否存在:exists '表名称'创建表:create '表名称', '列族1', '列族2', '列族3'删除表:首先禁用表:disable '表名称'然后删除表:drop '表名称'修改表:表数据操作查看所有数据:scan "表名称"..._hbase sehll怎么看登录的是哪个hbase
文章浏览阅读885次,点赞18次,收藏21次。在HBase中执行查询操作通常使用HBase Shell或编程语言API(如Java或Python)来执行。使用编程语言API,您可以使用相应的HBase客户端库来执行查询操作。这是一个简单的Java代码示例,演示了如何使用HBase Java API进行单行查询。这些示例仅为基本查询操作,HBase Shell还提供其他高级查询功能,如按时间戳过滤,使用正则表达式进行查询等。请注意,这只是HBase查询的基本示例,您可以根据实际需求和HBase的数据模型进行更复杂的查询操作。
文章浏览阅读7.3k次,点赞7次,收藏28次。找到hbase的bin目录并进入,执行启动hbase hmaster命令。问题原因 hmaster挂了 ,需要重新启动hmaster才行。hbase shell输入命令出现如下问题。_keepererrorcode = nonode for /hbase/master
文章浏览阅读1.3k次。三次信息化浪潮。_大数据应用开发技术笔记