一文看懂HBase倒序分页查询(实现分页跳转)

2023年1月4日12:58:37

HBase分页方式

HBase分页查询常见的方式有两种,一种是只能点击下一页上一页,不支持跳转到某一页,而另一种则是可以点击上一页下一页,同时也可以选择跳转到某个指定的页面。我们这篇完成的例子是第二种分页方式,可跳转到某个页面。

Hbase倒序查询

Hbase实现倒序查询非常简单,只需将扫描器设置为倒序扫描即可。

// 设置倒序扫描(倒序查询的关键)
scan.setReversed(true);

HBase分页思想

HBase分页的核心思想就是结合rowkey比较过滤器(RowFilter)分页过滤器 (PageFilter)进行查询

  • 分页过滤器 (PageFilter)

    使用这个过滤器可以实现对结果按行进行分页,在创建PageFilter实例的时候需要传入每页的行数。

  • RowKey比较过滤器(RowFilter)

    使用这个过滤器可以实现对查询数据根据rowkey进行比较,比较的规则和比较的rowkey在创建RowFilter实例时传入。

首先我们需要配置PageFilter,设置我们每一页需要查询的条数

// 设置查询条数
PageFilter pageFilter = new PageFilter(pageSize);

配置完了分页过滤器之后,我们需要创建一个RowFilter

// 创建RowFilter
RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.LESS, new BinaryComparator(Bytes.toBytes(startRowKey)));

在创建RowFilter我们传入了两个参数:
第一个参数是比较运算符CompareFilter.CompareOp.LESS表示小于

第二个参数是比较器new BinaryComparator(Bytes.toBytes(startRowKey))表示使用Bytes.compareTo(byte [],byte [])按字典序进行比较。

所以到这里就能明白,上面创建的rowFilter代表的含义,即使用Bytes.compareTo(byte [],byte [])方式按照字典顺序获取数据中rowkeystartRowKey小的数据。

这里为什么使用小于呢,因为我们本篇使用的是倒序查询。比较运算符和比较器需要结合实际的需求来决定,而不是固定的

HBase分页具体代码

POM文件

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-common</artifactId>
    <version>1.3.1</version>
</dependency>

java类

package com.xiaoming.springboot.util.hbase;

import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.log4j.Logger;

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

/**
 * Hbase工具类
 *
 * @author xiaoming
 * @date 2020-05-26 19:37
 **/
public class HbaseUtils {

    private static Logger logger = Logger.getLogger(HbaseUtils.class);

    private static HbaseUtils hbaseUtils;
    private static Configuration configuration;
    private static Connection connection;

    private HbaseUtils() {
    }

    public void init() {
        if (connection == null) {
            configuration = HBaseConfiguration.create();
            configuration.set("hbase.zookeeper.quorum", "127.0.0.1,127.0.0.2,127.0.0.3");
            configuration.set("hbase.zookeeper.property.clientPort", "2181");
            configuration.set("zookeeper.znode.parent", "/hbase-unsecure");
            configuration.setInt("zookeeper_timeout", 60000);
            try {
                connection = ConnectionFactory.createConnection(configuration);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static HbaseUtils getInstance() {
        if (hbaseUtils == null) {
            synchronized (HbaseUtils.class) {
                if (hbaseUtils == null) {
                    hbaseUtils = new HbaseUtils();
                    hbaseUtils.init();
                }
            }
        }
        return hbaseUtils;
    }

    /**
     * 功能描述: 获取分页数据
     *
     * @param tableName   表名
     * @param currentPage 当前页码
     * @param pageSize    每页条数
     * @return java.lang.String
     * @author xiaoming
     * @date 2020-5-26
     */
    private ResultScanner queryDataByPage(String tableName, int currentPage, int pageSize) {
        // 第一次查询时startRowKey为null
        String startRowKey = null;
        ResultScanner results = null;
        // 从第一页开始查询每页的数据
        for (int i = 0; i < currentPage; i++) {
            // 根据每一次传入的rowkey, 查询出排列顺序小于该 rowkey 的 pageSize 条数据, 则最后一页(currentPage)的数据就是最后一次查询的结果
            results = queryData(tableName, startRowKey, pageSize);
            Iterator<Result> iterator = results.iterator();
            while (iterator.hasNext()) {
                // 将每一页的最后一条数据做为下一页查询的起始行(不包含该条数据)
                startRowKey = Bytes.toString(iterator.next().getRow());
            }
        }
        return results;
    }

    /**
     * 功能描述: 查询数据
     *
     * @param tableName   表名
     * @param startRowKey 每页起始rowkey
     * @param pageSize    每页条数
     * @return org.apache.hadoop.hbase.client.ResultScanner
     * @author xiaoming
     * @date 2020-5-26
     */
    public ResultScanner queryData(String tableName, String startRowKey, int pageSize) {
        HTable table = null;
        ResultScanner results = null;

        Scan scan = new Scan();
        // 设置倒序扫描(倒序查询的关键)
        scan.setReversed(true);
        // MUST_PASS_ALL 表示需要满足过滤器集合中的所有的filter
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
        // 设置查询条数
        PageFilter pageFilter = new PageFilter(pageSize);
        filterList.addFilter(pageFilter);

        // 如果查询到了 startRowKey, 则过滤比 startRowKey 大的值
        if (StringUtils.isNotBlank(startRowKey)) {
            RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.LESS, new BinaryComparator(Bytes.toBytes(startRowKey)));
            filterList.addFilter(rowFilter);
        }
        scan.setFilter(filterList);

        try {
            table = (HTable) connection.getTable(TableName.valueOf(tableName));
            results = table.getScanner(scan);
        } catch (IOException e) {
            logger.error(e);
        } finally {
            if (table != null) {
                try {
                    table.close();
                } catch (IOException e) {
                    logger.error(e);
                }
            }
        }
        return results;
    }
}

分页倒序查询到这里就完成了,并且已经实现了可以跳转,但是由于采用的是遍历的方式,如果查看页数过大,可能会存在性能上的问题,但是如果数据量不大,或者只需查看比较前的页码数据,这个方法还是很不错的,比较hbase的速度非常的快。或者说可以采用上面说的分页的第一种方式,将每一次查询的startRowkey保存起来,只进行上一页下一页的操作。

由于我自己的电脑上面没有安装HBase,所以上面的代码没有在本地测试过,如果有问题,可以评论告诉一下我,我会尽快修改并答复的。Thank you~

补充
代码验证过了,可以运行,但是需要修改一处地方,需要在每次获取startRowKey的地方加上一个判断,代码如下:

    /**
     * 功能描述: 获取分页数据
     *
     * @param tableName   表名
     * @param currentPage 当前页码
     * @param pageSize    每页条数
     * @return java.lang.String
     * @author xiaoming
     * @date 2020-5-26
     */
    private ResultScanner queryDataByPage(String tableName, int currentPage, int pageSize) {
        // 第一次查询时startRowKey为null
        String startRowKey = null;
        ResultScanner results = null;
        // 从第一页开始查询每页的数据
        for (int i = 0; i < currentPage; i++) {
            // 根据每一次传入的rowkey, 查询出排列顺序小于该 rowkey 的 pageSize 条数据, 则最后一页(currentPage)的数据就是最后一次查询的结果
            results = queryData(tableName, startRowKey, pageSize);
            if (i < currentPage - 1) {
            	Iterator<Result> iterator = results.iterator();
            	while (iterator.hasNext()) {
                	// 将每一页的最后一条数据做为下一页查询的起始行(不包含该条数据)
                	startRowKey = Bytes.toString(iterator.next().getRow());
            	}
            }
        }
        return results;
    }

  • 作者:shixiaomingye
  • 原文链接:https://blog.csdn.net/qq_41581031/article/details/106367294
    更新时间:2023年1月4日12:58:37 ,共 5307 字。