HBase表資料分頁處理

來源:互聯網
上載者:User

HBase表資料分頁處理

HBase是Hadoop大資料生態技術圈中的一項關鍵技術,是一種用於分布式儲存大資料的列式資料庫,關於HBase更加詳細的介紹和技術細節,朋友們可以在網路上進行搜尋,筆者本人在接下來的日子裡也會寫一個HBase方面的技術專題,有興趣的朋友們可以稍微的期待一下。不過本章節的重點是介紹下HBase表資料的分頁處理,其他的就不多說了。

首先說一下表資料分頁中不可迴避的一個指標:總記錄數。在關聯式資料庫中很容易統計出記錄總數,但在HBase中,這卻是一個大難題,至少在目前,朋友們根本不要奢望能夠通過類似“SELECT COUNT(*) FROM TABLE”的方式統計出一個表的總行數。HBase本身提供的表行數統計功能是一個MapReduce任務,極為耗時,所以在對HBase表資料進行分頁處理時,我們只能忽略總記錄數這個統計指標了。

如果總記錄數不確定,那麼總分頁數也是不確定的,是否存在下一頁也是未知的,以及由此引發的其他問題,都是我們在進行HBase表資料分頁處理時需要特別注意的。

1、HBase表資料分頁模型類

import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hbase.client.Result;
/**
 * Description: HBase表資料分頁模型類。<br>
 * 利用此類可管理多個HBaseQualifierModel對象。
 * Copyright: Copyright (c) 2014<br>
 * Company: 河南電力科學研究院智能電網所<br>
 * @author shangbingbing 2014-01-01編寫
 * @version 1.0
 */
public class HBasePageModel implements Serializable {
    private static final long serialVersionUID = 330410716100946538L;
    private int pageSize = 100;
    private int pageIndex = 0;
    private int prevPageIndex = 1;
    private int nextPageIndex = 1;
    private int pageCount = 0;
    private int pageFirstRowIndex = 1;
    private byte[] pageStartRowKey = null;
    private byte[] pageEndRowKey = null;
    private boolean hasNextPage = true;
    private int queryTotalCount = 0;
    private long startTime = System.currentTimeMillis();
    private long endTime = System.currentTimeMillis();
    private List<Result> resultList = new ArrayList<Result>();
    public HBasePageModel(int pageSize) {
        this.pageSize = pageSize;
    }
    /**
    * 擷取分頁記錄數量
    * @return
    */
    public int getPageSize() {
        return pageSize;
    }
    /**
    * 設定分頁記錄數量
    * @param pageSize
    */
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    /**
    * 擷取當前頁序號
    * @return
    */
    public int getPageIndex() {
        return pageIndex;
    }
    /**
    * 設定當前頁序號
    * @param pageIndex
    */
    public void setPageIndex(int pageIndex) {
        this.pageIndex = pageIndex;
    }
    /**
    * 擷取分頁總數
    * @return
    */
    public int getPageCount() {
        return pageCount;
    }
    /**
    * 設定分頁總數
    * @param pageCount
    */
    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }
    /**
    * 擷取每頁的第一行序號
    * @return
    */
    public int getPageFirstRowIndex() {
        this.pageFirstRowIndex = (this.getPageIndex() - 1) * this.getPageSize() + 1;
        return pageFirstRowIndex;
    }
    /**
    * 擷取每頁起始行鍵
    * @return
    */
    public byte[] getPageStartRowKey() {
        return pageStartRowKey;
    }
    /**
    * 設定每頁起始行鍵
    * @param pageStartRowKey
    */
    public void setPageStartRowKey(byte[] pageStartRowKey) {
        this.pageStartRowKey = pageStartRowKey;
    }
    /**
    * 擷取每頁結束行鍵
    * @return
    */
    public byte[] getPageEndRowKey() {
        return pageEndRowKey;
    }
    /**
    * 設定每頁結束行鍵
    * @param pageStartRowKey
    */
    public void setPageEndRowKey(byte[] pageEndRowKey) {
        this.pageEndRowKey = pageEndRowKey;
    }
    /**
    * 擷取上一頁序號
    * @return
    */
    public int getPrevPageIndex() {
        if(this.getPageIndex() > 1) {
            this.prevPageIndex = this.getPageIndex() - 1;
        } else {
            this.prevPageIndex = 1;
        }
        return prevPageIndex;
    }
    /**
    * 擷取下一頁序號
    * @return
    */
    public int getNextPageIndex() {
        this.nextPageIndex = this.getPageIndex() + 1;
        return nextPageIndex;
    }
    /**
    * 擷取是否有下一頁
    * @return
    */
    public boolean isHasNextPage() {
//這個判斷是不嚴謹的,因為很有可能剩餘的資料剛好夠一頁。
        if(this.getResultList().size() == this.getPageSize()) {
            this.hasNextPage = true;
        } else {
            this.hasNextPage = false;
        }
        return hasNextPage;
    }
    /**
    * 擷取已檢索總記錄數   
    */
    public int getQueryTotalCount() {
        return queryTotalCount;
    }
    /**
    * 擷取已檢索總記錄數   
    * @param queryTotalCount
    */
    public void setQueryTotalCount(int queryTotalCount) {
        this.queryTotalCount = queryTotalCount;
    }
    /**
    * 初始化起始時間(毫秒)
    */
    public void initStartTime() {
        this.startTime = System.currentTimeMillis();
    }
    /**
    * 初始化截止時間(毫秒)
    */
    public void initEndTime() {
        this.endTime = System.currentTimeMillis();
    }
    /**
    * 擷取毫秒格式的耗時資訊
    * @return
    */
    public String getTimeIntervalByMilli() {
        return String.valueOf(this.endTime - this.startTime) + "毫秒";
    }
    /**
    * 擷取秒格式的耗時資訊
    * @return
    */
    public String getTimeIntervalBySecond() {
        double interval = (this.endTime - this.startTime)/1000.0;
        DecimalFormat df = new DecimalFormat("#.##");
        return df.format(interval) + "秒";
    }
    /**
    * 列印時間資訊
    */
    public void printTimeInfo() {
        LogInfoUtil.printLog("起始時間:" + this.startTime);
        LogInfoUtil.printLog("截止時間:" + this.endTime);
        LogInfoUtil.printLog("耗費時間:" + this.getTimeIntervalBySecond());
    }
    /**
    * 擷取HBase檢索結果集合
    * @return
    */
    public List<Result> getResultList() {
        return resultList;
    }
    /**
    * 設定HBase檢索結果集合
    * @param resultList
    */
    public void setResultList(List<Result> resultList) {
        this.resultList = resultList;
    }
}

綜上所述,我們沒有對總記錄數和總頁數進行統計處理,並且用“已檢索記錄數”代替了“總記錄數”。另外,對每次檢索的耗時資訊進行了統計記錄,便於開發人員調試統計效率。

2、HBase表資料分頁檢索方法

就像關聯式資料庫Oracle那樣,我們進行資料檢索時往往附帶有很多的檢索條件,HBase表資料檢索也不例外。HBase表資料檢索條件通常有以下幾種:RowKey行鍵範圍(如果不確定範圍的話則面向全表)、過濾器、資料版本。所以,當我們決定要設計一個比較通用的資料分頁檢索介面方法時,就不得不考慮以上幾種檢索條件。

/**
* 分頁檢索表資料。<br>
* (如果在建立表時為此表指定了非預設的命名空間,則需拼字上命名空間名稱,格式為【namespace:tablename】)。
* @param tableName 表名稱(*)。
* @param startRowKey 起始行鍵(可以為空白,如果為空白,則從表中第一行開始檢索)。
* @param endRowKey 結束行鍵(可以為空白)。
* @param filterList 檢索條件過濾器集合(不包含分頁過濾器;可以為空白)。
* @param maxVersions 指定最大版本數【如果為最大整數值,則檢索所有版本;如果為最小整數值,則檢索最新版本;否則只檢索指定的版本數】。
* @param pageModel 分頁模型(*)。
* @return 返回HBasePageModel分頁對象。
*/
public static HBasePageModel scanResultByPageFilter(String tableName, byte[] startRowKey, byte[] endRowKey, FilterList filterList, int maxVersions, HBasePageModel pageModel) {
    if(pageModel == null) {
        pageModel = new HBasePageModel(10);
    }
    if(maxVersions <= 0 ) {
        //預設只檢索資料的最新版本
        maxVersions = Integer.MIN_VALUE;
    }
    pageModel.initStartTime();
    pageModel.initEndTime();
    if(StringUtils.isBlank(tableName)) {
        return pageModel;
    }
    HTable table = null;
   
    try {
        //根據HBase表名稱,得到HTable表對象,這裡用到了筆者本人自己構建的一個表資訊管理類。
        table = HBaseTableManageUtil.getHBaseTable(tableName);
        int tempPageSize = pageModel.getPageSize();
        boolean isEmptyStartRowKey = false;
        if(startRowKey == null) {
            //則讀取表的第一行記錄,這裡用到了筆者本人自己構建的一個表資料操作類。
            Result firstResult = HBaseTableDataUtil.selectFirstResultRow(tableName, filterList);
            if(firstResult.isEmpty()) {
                return pageModel;
            }
            startRowKey = firstResult.getRow();
        }
        if(pageModel.getPageStartRowKey() == null) {
            isEmptyStartRowKey = true;
            pageModel.setPageStartRowKey(startRowKey);
        } else {
            if(pageModel.getPageEndRowKey() != null) {
                pageModel.setPageStartRowKey(pageModel.getPageEndRowKey());
            }
            //從第二頁開始,每次都多取一條記錄,因為第一條記錄是要刪除的。
            tempPageSize += 1;
        }
       
        Scan scan = new Scan();
        scan.setStartRow(pageModel.getPageStartRowKey());
        if(endRowKey != null) {
            scan.setStopRow(endRowKey);
        }
        PageFilter pageFilter = new PageFilter(pageModel.getPageSize() + 1);
        if(filterList != null) {
            filterList.addFilter(pageFilter);
            scan.setFilter(filterList);
        } else {
            scan.setFilter(pageFilter);
        }
        if(maxVersions == Integer.MAX_VALUE) {
            scan.setMaxVersions();
        } else if(maxVersions == Integer.MIN_VALUE) {
           
        } else {
            scan.setMaxVersions(maxVersions);
        }
        ResultScanner scanner = table.getScanner(scan);
        List<Result> resultList = new ArrayList<Result>();
        int index = 0;
        for(Result rs : scanner.next(tempPageSize)) {
            if(isEmptyStartRowKey == false && index == 0) {
                index += 1;
                continue;
            }
            if(!rs.isEmpty()) {
                resultList.add(rs);
            }
            index += 1;
        }
        scanner.close();
        pageModel.setResultList(resultList);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            table.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
    int pageIndex = pageModel.getPageIndex() + 1;
    pageModel.setPageIndex(pageIndex);
    if(pageModel.getResultList().size() > 0) {
        //擷取本次分頁資料首行和末行的行鍵資訊
        byte[] pageStartRowKey = pageModel.getResultList().get(0).getRow();
        byte[] pageEndRowKey = pageModel.getResultList().get(pageModel.getResultList().size() - 1).getRow();
        pageModel.setPageStartRowKey(pageStartRowKey);
        pageModel.setPageEndRowKey(pageEndRowKey);
    }
    int queryTotalCount = pageModel.getQueryTotalCount() + pageModel.getResultList().size();
    pageModel.setQueryTotalCount(queryTotalCount);
    pageModel.initEndTime();
    pageModel.printTimeInfo();
    return pageModel;
}

 

順便貼出“擷取HBase表第一行資料”的介面方法。

 

/**
 * 檢索指定表的第一行記錄。<br>
 * (如果在建立表時為此表指定了非預設的命名空間,則需拼字上命名空間名稱,格式為【namespace:tablename】)。
 * @param tableName 表名稱(*)。
 * @param filterList 過濾器集合,可以為null。
 * @return
 */
public static Result selectFirstResultRow(String tableName,FilterList filterList) {
    if(StringUtils.isBlank(tableName)) return null;
    HTable table = null;
    try {
        table = HBaseTableManageUtil.getHBaseTable(tableName);
        Scan scan = new Scan();
        if(filterList != null) {
            scan.setFilter(filterList);
        }
        ResultScanner scanner = table.getScanner(scan);
        Iterator<Result> iterator = scanner.iterator();
        int index = 0;
        while(iterator.hasNext()) {
            Result rs = iterator.next();
            if(index == 0) {
                scanner.close();
                return rs;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            table.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

3、HBase表資料分頁檢索應用執行個體

HBasePageModel pageModel = new HBasePageModel(pageSize);
pageModel = scanResultByPageFilter(“DLQX:SZYB_DATA”,null,null,null,pageModel);
if(pageModel.getResultList().size() == 0) {
    //本頁沒有資料,說明已經是最後一頁了。
    return;
}

Hadoop+HBase搭建雲端儲存總結 PDF

HBase 結點之間時間不一致造成regionserver啟動失敗

Hadoop+ZooKeeper+HBase叢集配置

Hadoop叢集安裝&HBase實驗環境搭建

基於Hadoop叢集的HBase叢集的配置 ‘

Hadoop安裝部署筆記之-HBase完全分布模式安裝

單機版搭建HBase環境圖文教程詳解

HBase 的詳細介紹:請點這裡
HBase 的:請點這裡

本文永久更新連結地址:

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.