首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Hbase入门篇03---Java API使用,HBase高可用配置和架构设计

Hbase入门篇03---Java API使用,HBase高可用配置和架构设计

作者头像
大忽悠爱学习
发布2023-05-23 10:12:56
发布2023-05-23 10:12:56
1.1K0
举报
文章被收录于专栏:c++与qt学习c++与qt学习

Hbase入门篇03---Java API使用,HBase高可用配置和架构设计

需求

某某自来水公司,需要存储大量的缴费明细数据。以下截取了缴费明细的一部分内容。

用户id

姓名

用户地址

性别

缴费时间

表示数(本次)

表示数(上次)

用量(立方)

合计金额

查表日期

最迟缴费日期

4944191

登卫红

贵州省铜仁市德江县7单元267室

2020-05-10

308.1

283.1

25

150

2020-04-25

2020-06-09

因为缴费明细的数据记录非常庞大,该公司的信息部门决定使用HBase来存储这些数据。并且,他们希望能够通过Java程序来访问这些数据。


环境搭建

  • 引入依赖
代码语言:javascript
复制
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
                <updatePolicy>never</updatePolicy>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>

  • 如果在 settings.xml 文件和当前项目的 pom.xml 文件中都指定了同一个 repository 配置,那么 pom.xml 中的配置会覆盖 settings.xml 中的配置。
  • 这意味着,如果在 pom.xml 中定义了特定的存储库,Maven 将会使用 pom.xml 中指定的配置,而不是 settings.xml 中的配置。
  • 但是,如果在 pom.xml 中没有指定,Maven 会尝试在 settings.xml 中查找相应的配置。
  • 如果在settings.xml 中也没有找到,则 Maven 将默认使用 Maven 中央存储库。
  • 复制HBase和Hadoop配置文件
    • 将以下三个配置文件复制到resource目录中
      • hbase-site.xml
        • 从Linux中下载:sz /export/server/hbase-2.1.0/conf/hbase-site.xml
      • core-site.xml
        • 从Linux中下载:sz /export/server/hadoop-2.7.5/etc/hadoop/core-site.xml
      • log4j.properties

注意:请确认配置文件中的服务器节点hostname/ip地址配置正确

在访问HBase时,需要使用HBase和Hadoop的相关配置信息来与集群进行通信。通常情况下,这些配置文件位于集群中的节点上,Java应用程序需要知道这些配置信息才能连接到HBase集群。因此,将这些配置文件复制到Java项目中可以方便Java应用程序获取配置信息,从而连接到HBase集群。如果不将这些配置文件复制到Java项目中,则需要手动配置Java应用程序中的相关配置信息。

sz 命令是一种用于从远程服务器下载文件的命令。在该命令中,/export/server/hbase-2.1.0/conf/hbase-site.xml 是要下载的文件的路径。该命令会将文件下载到当前目录中。通常,sz 命令需要在客户端终端中运行,以从远程服务器下载文件。


  • 创建HBase连接及Admin管理对象
代码语言:javascript
复制
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import java.io.IOException;

public class TableAmdinTest {
    private Configuration configuration;
    private Connection connection;
    private Admin admin;

    @BeforeTest
    public void beforeTest() throws IOException {
        configuration = HBaseConfiguration.create();
        //可以不引入hbase-site.xml配置文件,手动指定zk地址,客户端从zk拉取,获取master和regionServer的地址
        configuration.set("hbase.zookeeper.quorum", "node1");
        connection = ConnectionFactory.createConnection(configuration);
        admin = connection.getAdmin();
    }
    
    @AfterTest
    public void afterTest() throws IOException {
        admin.close();
        connection.close();
    }
}

表的CRUD

创建表:

创建一个名为WATER_BILL的表,包含一个列蔟C1。

  • 调用tableExists判断表是否存在
  • 在HBase中,要去创建表,需要构建TableDescriptor(表描述器)、ColumnFamilyDescriptor(列蔟描述器),这两个对象不是直接new出来,是通过builder来创建的
  • 将列蔟描述器添加到表描述器中
  • 使用admin.createTable创建表
代码语言:javascript
复制
    /**
     * 创建一个名为WATER_BILL的表,包含一个列蔟C1
     */
    @Test
    public void createTableTest() throws IOException {
        // 表名
        String TABLE_NAME = "WATER_BILL";
        // 列蔟名
        String COLUMN_FAMILY = "C1";

        // 1. 判断表是否存在
        if (admin.tableExists(TableName.valueOf(TABLE_NAME))) {
            return;
        }

        // 2. 构建表描述构建器
        TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE_NAME));

        // 3. 构建列蔟描述构建器
        ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(COLUMN_FAMILY));

        // 4. 构建列蔟描述
        ColumnFamilyDescriptor columnFamilyDescriptor = columnFamilyDescriptorBuilder.build();

        // 5. 构建表描述
        // 添加列蔟
        tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
        TableDescriptor tableDescriptor = tableDescriptorBuilder.build();

        // 6. 创建表
        admin.createTable(tableDescriptor);
    }

注意:

  • 在HBase中所有的数据都是以byte[]形式来存储的,所以需要将Java的数据类型进行转换
  • 经常会使用到一个工具类:Bytes(hbase包下的Bytes工具类)
  • 这个工具类可以将字符串、long、double类型转换成byte[]数组
  • 也可以将byte[]数组转换为指定类型

删除表:

代码语言:javascript
复制
    @Test
    public void dropTable() throws IOException {
        // 表名
        TableName tableName = TableName.valueOf("WATER_BILL");

        // 1. 判断表是否存在
        if (admin.tableExists(tableName)) {
            // 2. 禁用表
            admin.disableTable(tableName);
            // 3. 删除表
            admin.deleteTable(tableName);
        }
    }

此处列举HBase Java客户端使用过程中可能会遇到的一些坑:

命令执行卡住不动 ?

HBase Java客户端在调用相关方法时,会自动进行重试和超时机制,如果一直无法建立连接或响应,则可能会导致方法一直卡住。

为了避免这种情况,可以设置一个较短的超时时间或者关闭自动重试机制。可以使用以下方法实现:

  • 设置超时时间:
    • 可以通过调用HBase Configuration对象的set方法设置“hbase.client.operation.timeout”参数的值,以毫秒为单位。
    • 例如,设置为1秒钟:
代码语言:javascript
复制
Configuration conf = HBaseConfiguration.create();
conf.setInt("hbase.client.operation.timeout", 1000);
  • 关闭自动重试:
    • 可以通过调用HBase Configuration对象的set方法设置“hbase.client.retries.number”参数的值为0,表示关闭自动重试机制。
    • 例如:
代码语言:javascript
复制
Configuration conf = HBaseConfiguration.create();
conf.setInt("hbase.client.retries.number", 0);

但是需要注意,关闭自动重试机制可能会导致某些操作失败。因此,需要根据实际情况选择适当的配置。


上面的配置只是为了让客户端出现连接异常时,能够快速失败,而不是不断的重试和超时等待,导致我们无法及时感知错误发生。

当异常抛出来之后,下面就是根据异常分类处理了,下面我列举我遇到的一些异常情况:

RegionServer只在本地127.0.0.1监听16020端口导致外网连接被拒
代码语言:javascript
复制
Caused by: java.net.ConnectException: Call to node2/xxx:16020 failed on connection exception: org.apache.hbase.thirdparty.io.netty.channel.ConnectTimeoutException: connection timed out: node2/123.60.166.193:16020
	at org.apache.hadoop.hbase.ipc.IPCUtil.wrapException(IPCUtil.java:165)
	at org.apache.hadoop.hbase.ipc.AbstractRpcClient.onCallFinished(AbstractRpcClient.java:390)
	at org.apache.hadoop.hbase.ipc.AbstractRpcClient.access$100(AbstractRpcClient.java:95)
	at org.apache.hadoop.hbase.ipc.AbstractRpcClient$3.run(AbstractRpcClient.java:410)
	at org.apache.hadoop.hbase.ipc.AbstractRpcClient$3.run(AbstractRpcClient.java:406)

解决方法,在hbase-site.xml配置文件中配置RegionServer在0.0.0.0地址上监听16020端口:

代码语言:javascript
复制
  <property>
    <name>hbase.regionserver.ipc.address</name>
    <value>0.0.0.0</value>
  </property>

RegionServer所在主机的/etc/hosts文件存在额外的回环地址映射信息,导致客户端拿到无法识别的主机名

数据的CRUD

  • 插入姓名列数据
    • 在表中插入一个行,该行只包含一个列。

ROWKEY

姓名(列名:NAME)

4944191

登卫红

  • 首先要获取一个Table对象,这个对象是要和HRegionServer节点连接,所以将来HRegionServer负载是比较高的
  • HBase的connection对象是一个重量级的对象,将来编写代码(Spark、Flink)的时候,避免频繁创建,使用一个对象就OK,因为它是线程安全的
代码语言:javascript
复制
Connection creation is a heavy-weight operation. Connection implementations are thread-safe
  • Table这个对象是一个轻量级的,用完Table需要close,因为它是非线程安全的
代码语言:javascript
复制
Lightweight. Get as needed and just close when done.
  • 需要构建Put对象,然后往Put对象中添加列蔟、列、值
  • 当执行一些繁琐重复的操作用列标记:
    • ctrl + shift + ←/→,可以按照单词选择,非常高效
代码语言:javascript
复制
 @Test
    public void addTest() throws IOException {
        // 1.使用Hbase连接获取Htable
        TableName waterBillTableName = TableName.valueOf("WATER_BILL");
        Table waterBillTable = connection.getTable(waterBillTableName);

        // 2.构建ROWKEY、列蔟名、列名
        String rowkey = "4944191";
        String cfName = "C1";
        String colName = "NAME";

        // 3.构建Put对象(对应put命令)
        Put put = new Put(Bytes.toBytes(rowkey));

        // 4.添加姓名列
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colName)
                , Bytes.toBytes("登卫红"));

        // 5.使用Htable表对象执行put操作
        waterBillTable.put(put);
        // 6. 关闭表
        waterBillTable.close();
    }
  • 插入其他列

列名

说明

ADDRESS

用户地址

贵州省铜仁市德江县7单元267室

SEX

性别

PAY_DATE

缴费时间

2020-05-10

NUM_CURRENT

表示数(本次)

308.1

NUM_PREVIOUS

表示数(上次)

283.1

NUM_USAGE

用量(立方)

25

TOTAL_MONEY

合计金额

150

RECORD_DATE

查表日期

2020-04-25

LATEST_DATE

最迟缴费日期

2020-06-09

代码语言:javascript
复制
    @Test
    public void addTest1() throws IOException {
        // 1.使用Hbase连接获取Htable
        TableName waterBillTableName = TableName.valueOf("WATER_BILL");
        Table waterBillTable = connection.getTable(waterBillTableName);

        // 2.构建ROWKEY、列蔟名、列名
        String rowkey = "4944191";
        String cfName = "C1";
        String colName = "NAME";
        String colADDRESS = "ADDRESS";
        String colSEX = "SEX";
        String colPAY_DATE = "PAY_DATE";
        String colNUM_CURRENT = "NUM_CURRENT";
        String colNUM_PREVIOUS = "NUM_PREVIOUS";
        String colNUM_USAGE = "NUM_USAGE";
        String colTOTAL_MONEY = "TOTAL_MONEY";
        String colRECORD_DATE = "RECORD_DATE";
        String colLATEST_DATE = "LATEST_DATE";

        // 3.构建Put对象(对应put命令)
        Put put = new Put(Bytes.toBytes(rowkey));

        // 4.添加姓名列
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colName)
                , Bytes.toBytes("登卫红"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colADDRESS)
                , Bytes.toBytes("贵州省铜仁市德江县7单元267室"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colSEX)
                , Bytes.toBytes("男"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colPAY_DATE)
                , Bytes.toBytes("2020-05-10"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colNUM_CURRENT)
                , Bytes.toBytes("308.1"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colNUM_PREVIOUS)
                , Bytes.toBytes("283.1"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colNUM_USAGE)
                , Bytes.toBytes("25"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colTOTAL_MONEY)
                , Bytes.toBytes("150"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colRECORD_DATE)
                , Bytes.toBytes("2020-04-25"));
        put.addColumn(Bytes.toBytes(cfName)
                , Bytes.toBytes(colLATEST_DATE)
                , Bytes.toBytes("2020-06-09"));

        // 5.使用Htable表对象执行put操作
        waterBillTable.put(put);

        // 6. 关闭表
        waterBillTable.close();
    }

  • 查看一条数据
代码语言:javascript
复制
    @Test
    public void getOneTest() throws IOException {
        // 1. 获取HTable
        TableName waterBillTableName = TableName.valueOf("WATER_BILL");
        Table waterBilltable = connection.getTable(waterBillTableName);

        // 2. 使用rowkey构建Get对象
        Get get = new Get(Bytes.toBytes("4944191"));

        // 3. 执行get请求
        Result result = waterBilltable.get(get);

        // 4. 获取所有单元格
        List<Cell> cellList = result.listCells();

        // 打印rowkey
        System.out.println("rowkey => " + Bytes.toString(result.getRow()));

        // 5. 迭代单元格列表
        for (Cell cell : cellList) {
            // 打印列蔟名
            System.out.print(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));
            System.out.println(" => " + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));

        }

        // 6. 关闭表
        waterBilltable.close();
    }

  • 删除一条数据
代码语言:javascript
复制
    // 删除rowkey为4944191的整条数据
    @Test
    public void deleteOneTest() throws IOException {
        // 1. 获取HTable对象
        Table waterBillTable = connection.getTable(TableName.valueOf("WATER_BILL"));

        // 2. 根据rowkey构建delete对象
        Delete delete = new Delete(Bytes.toBytes("4944191"));

        // 3. 执行delete请求
        waterBillTable.delete(delete);

        // 4. 关闭表
        waterBillTable.close();
    }

数据的导入导出

Import JOB

在HBase中,有一个Import的MapReduce作业,可以专门用来将数据文件导入到HBase中。

  • 用法:
代码语言:javascript
复制
hbase org.apache.hadoop.hbase.mapreduce.Import 表名 HDFS数据文件路径

导入数据演示:

  • 将文件上传到hdfs中
代码语言:javascript
复制
hadoop fs -mkdir -p /water_bill/output_ept_10W
hadoop fs -put part-m-00000_10w /water_bill/output_ept_10W
  • 启动YARN集群
代码语言:javascript
复制
start-yarn.sh
  • 使用以下方式来进行数据导入
代码语言:javascript
复制
hbase org.apache.hadoop.hbase.mapreduce.Import WATER_BILL /water_bill/output_ept_10W

导出数据演示:

代码语言:javascript
复制
hbase org.apache.hadoop.hbase.mapreduce.Export WATER_BILL /water_bill/output_ept_10W_export

数据查询

需求: 查询2020年6月份所有用户的用水量

  • 需求分析
    • 在Java API中,我们也是使用scan + filter来实现过滤查询。2020年6月份其实就是从2020年6月1日到2020年6月30日的所有抄表数据。

注意:

  • ResultScanner需要手动关闭,这个操作是比较消耗资源的,用完就应该关掉,不能一直都开着
  • 扫描使用的是Scan对象
  • SingleColumnValueFilter——过滤单列值的过滤器
  • FilterList——是可以用来组合多个过滤器
代码语言:javascript
复制
    // 查询2020年6月份所有用户的用水量数据
    @Test
    public void queryTest1() throws IOException {
        // 1. 获取表
        Table waterBillTable = connection.getTable(TableName.valueOf("WATER_BILL"));
        // 2. 构建scan请求对象
        Scan scan = new Scan();
        // 3. 构建两个过滤器
        // 3.1 构建日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较
        SingleColumnValueFilter startDateFilter = new SingleColumnValueFilter(Bytes.toBytes("C1")
                , Bytes.toBytes("RECORD_DATE")
                , CompareOperator.GREATER_OR_EQUAL
                , Bytes.toBytes("2020-06-01"));

        SingleColumnValueFilter endDateFilter = new SingleColumnValueFilter(Bytes.toBytes("C1")
                , Bytes.toBytes("RECORD_DATE")
                , CompareOperator.LESS_OR_EQUAL
                , Bytes.toBytes("2020-06-30"));

        // 3.2 构建过滤器列表
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL
                , startDateFilter
                , endDateFilter);

        scan.setFilter(filterList);

        // 4. 执行scan扫描请求
        ResultScanner resultScan = waterBillTable.getScanner(scan);

        // 5. 迭代打印result
        for (Result result : resultScan) {
            System.out.println("rowkey -> " + Bytes.toString(result.getRow()));
            System.out.println("------");

            List<Cell> cellList = result.listCells();

            // 6. 迭代单元格列表
            for (Cell cell : cellList) {
                // 打印列蔟名
                System.out.print(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));
                System.out.println(" => " + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));

            }
            System.out.println("------");
        }

        resultScan.close();


        // 7. 关闭表
        waterBillTable.close();
    }

解决中文乱码问题:

  • 前面我们的代码,在打印所有的列时,都是使用字符串打印的,Hbase中如果存储的是int、double,那么有可能就会乱码了。

要解决的话,我们可以根据列来判断,使用哪种方式转换字节码。如下:

  1. NUM_CURRENT
  2. NUM_PREVIOUS
  3. NUM_USAGE
  4. TOTAL_MONEY

这4列使用double类型展示,其他的使用string类型展示

代码语言:javascript
复制
              if(colName.equals("NUM_CURRENT")
                        || colName.equals("NUM_PREVIOUS")
                        || colName.equals("NUM_USAGE")
                        || colName.equals("TOTAL_MONEY")) {
                    System.out.println(" => " + Bytes.toDouble(cell.getValueArray(), cell.getValueOffset()));
                }
                else {
                    System.out.println(" => " + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
                }

HBase高可用

考虑关于HBase集群的一个问题,在当前的HBase集群中,只有一个Master,一旦Master出现故障,将会导致HBase不再可用。所以,在实际的生产环境中,是非常有必要搭建一个高可用的HBase集群的。

HBase高可用简介

  • HBase的高可用配置其实就是HMaster的高可用。要搭建HBase的高可用,只需要再选择一个节点作为HMaster,在HBase的conf目录下创建文件backup-masters,然后再backup-masters添加备份Master的记录。一条记录代表一个backup master,可以在文件配置多个记录。

搭建HBase高可用

  1. 在hbase的conf文件夹中创建 backup-masters 文件
代码语言:javascript
复制
cd /export/server/hbase-2.1.0/conf
touch backup-masters
  1. 将node2和node3添加到该文件中
代码语言:javascript
复制
vim backup-masters
node2
node3
  1. 将backup-masters文件分发到所有的服务器节点中
代码语言:javascript
复制
scp backup-masters node2:$PWD
scp backup-masters node3:$PWD
  1. 重新启动hbase
代码语言:javascript
复制
stop-hbase.sh
start-hbase.sh
  1. 查看webui,检查Backup Masters中是否有node2,node3
  • http://node1:16010/master-status
  1. 尝试杀掉node1节点上的master
代码语言:javascript
复制
kill -9 HMaster进程id
  1. 访问http://node2:16010http://node3:16010,观察是否选举了新的Master

注意:

  • HBase的HA也是通过ZK来实现的(临时节点、watch机制)
  • 只需要添加一个backup-masters文件,往里面添加要称为Backup master的节点,HBase启动的时候,会自动启动多个HMaster
  • HBase配置了HA后,对Java代码没有影响。因为Java代码是通过从ZK中来获取Master的地址的

HBase架构

  • client:客户端,写的Java程序、hbase shell都是客户端(Flink、MapReduce、Spark)
  • Master Server
    • 监控RegionServer
    • 处理RegionServer故障转移
    • 处理元数据的变更
    • 处理region的分配或移除
    • 在空闲时间进行数据的负载均衡
    • 通过Zookeeper发布自己的位置给客户端

HMaster:主要是负责表的管理操作(创建表、删除表、Region分配),不负责具体的数据操作

  • Region Server
    • 处理分配给它的Region
    • 负责存储HBase的实际数据
    • 刷新缓存到HDFS
    • 维护HLog
    • 执行压缩
    • 负责处理Region分片
  • RegionServer中包含了大量丰富的组件,如下:
    • Write-Ahead logs
    • HFile(StoreFile)
    • Store
    • MemStore
    • Region

HRegionServer:负责数据的管理、数据的操作(增删改查)、负责接收客户端的请求来操作数据

  • Region
    • 在HBASE中,表被划分为很多「Region」,并由Region Server提供服务

一个表由多个Region组成,每个Region保存一定的rowkey范围的数据,Region中的数据一定是有序的,是按照rowkey的字典序来排列的

  • Store
    • 存储的是表中每一个列蔟的数据
    • Region按列族垂直划分为「Store」,存储在HDFS在文件中
  • MemStore
    • MemStore与缓存内存类似
    • 当往HBase中写入数据时,首先是写入到MemStore
    • 每个列族将有一个MemStore
    • 当MemStore存储快满的时候,整个数据将写入到HDFS中的HFile中

所有的数据都是先写入到MemStore中,可以让读写操作更快,当MemStore快满的时候,需要有一个线程定期的将数据Flush到磁盘中

  • StoreFile
    • 每当任何数据被写入HBASE时,首先要写入MemStore
    • 当MemStore快满时,整个排序的key-value数据将被写入HDFS中的一个新的HFile中
    • 写入HFile的操作是连续的,速度非常快
    • 物理上存储的是HFile

HFile是在HDFS上保存的数据,是HBase独有的一种数据格式(丰富的结构、索引、DataBlock、BloomFilter布隆过滤器…)

  • WAL
    • WAL全称为Write Ahead Log,它最大的作用就是 故障恢复
    • WAL是HBase中提供的一种高并发、持久化的日志保存与回放机制
    • 每个业务数据的写入操作(PUT/DELETE/INCR),都会保存在WAL中
    • 一旦服务器崩溃,通过回放WAL,就可以实现恢复崩溃之前的数据
    • 物理上存储是Hadoop的Sequence File

WAL预写日志,当客户端连接RegionServer写数据的时候,会先写WAL预写日志,put/delete/incr命令写入到WAL,有点类似于之前Redis中的AOF,当某一个RegionServer出现故障时,还可以通过WAL来恢复数据,恢复的就是MemStore的数据。


常见Bug记录

  • Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster
    • 找到$HADOOP_HOME/etc/mapred-site.xml,增加以下配置
    • 将配置文件分发到各个节点
    • 重新启动YARN集群
代码语言:javascript
复制
<property>
  <name>yarn.app.mapreduce.am.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
<property>
  <name>mapreduce.map.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
<property>
  <name>mapreduce.reduce.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
  • Caused by: java.net.ConnectException: Call to node2/192.168.88.101:16020 failed on connection exception: org.apache.hbase.thirdparty.io.netty.channel.ConnectTimeoutException: connection timed out: node2/192.168.88.101:16020
    • 无法连接到HBase,请检查HBase的Master是否正常启动
  • Starting namenodes on [localhost] ERROR: Attempting to launch hdfs namenode as root ,ERROR: but there is no HDFS_NAMENODE_USER defined. Aborting launch.
    • 解决办法: 是因为缺少用户定义造成的,所以分别编辑开始和关闭脚本
代码语言:javascript
复制
$ vim sbin/start-dfs.sh 
$ vim sbin/stop-dfs.sh 

在顶部空白处添加内容:

代码语言:javascript
复制
HDFS_DATANODE_USER=root 
HADOOP_SECURE_DN_USER=hdfs 
HDFS_NAMENODE_USER=root 
HDFS_SECONDARYNAMENODE_USER=root 
  • Starting resourcemanager ERROR: Attempting to launch yarn resourcemanager as root ERROR: but there is no YARN_RESOURCEMANAGER_USER defined. Aborting launch. Starting nodemanagers ERROR: Attempting to launch yarn nodemanager as root ERROR: but there is no YARN_NODEMANAGER_USER defined. Aborting launch
代码语言:javascript
复制
vim sbin/start-yarn.sh 
vim sbin/stop-yarn.sh 

YARN_RESOURCEMANAGER_USER=root
HADOOP_SECURE_DN_USER=yarn
YARN_NODEMANAGER_USER=root
  • Exception in thread "main" java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$POSIX.stat
    • 将 hadoop.dll 放到c:/windows/system32文件夹中,重启IDEA,重新运行程序

本部分思维导图

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Hbase入门篇03---Java API使用,HBase高可用配置和架构设计
  • 环境搭建
  • 表的CRUD
  • 数据的CRUD
  • 数据的导入导出
    • Import JOB
  • 数据查询
  • HBase高可用
    • HBase高可用简介
    • 搭建HBase高可用
  • HBase架构
  • 常见Bug记录
  • 本部分思维导图
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档