一文看懂HBase倒序分頁查詢(實現分頁跳轉)

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~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章