HBase表數據分頁處理

 

HBase表數據分頁處理

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

首先說一下表數據分頁中不可迴避的一個指標:總記錄數。在關係數據庫中很容易統計出記錄總數,但在HBase中,這倒是一個大難題,至少在目前,朋友們根本不要奢望可以經過相似「SELECT COUNT(*) FROM TABLE」的方式統計出一個表的總行數。HBase自己提供的錶行數統計功能是一個MapReduce任務,極爲耗時,因此在對HBase表數據進行分頁處理時,咱們只能忽略總記錄數這個統計指標了。linux

若是總記錄數不肯定,那麼總分頁數也是不肯定的,是否存在下一頁也是未知的,以及由此引起的其餘問題,都是咱們在進行HBase表數據分頁處理時須要特別注意的。數據庫

一、HBase表數據分頁模型類apache

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;
    }
}網絡

綜上所述,咱們沒有對總記錄數和總頁數進行統計處理,而且用「已檢索記錄數」代替了「總記錄數」。另外,對每次檢索的耗時信息進行了統計記錄,便於開發人員調試統計效率。分佈式

二、HBase表數據分頁檢索方法oop

就像關係數據庫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;
}this

 

順便貼出「獲取HBase表第一行數據」的接口方法。spa

 

/**
 * 檢索指定表的第一行記錄。<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;
}

三、HBase表數據分頁檢索應用實例

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

Hadoop+HBase搭建雲存儲總結 PDF http://www.linuxidc.com/Linux/2013-05/83844.htm

HBase 結點之間時間不一致形成regionserver啓動失敗 http://www.linuxidc.com/Linux/2013-06/86655.htm

Hadoop+ZooKeeper+HBase集羣配置 http://www.linuxidc.com/Linux/2013-06/86347.htm

Hadoop集羣安裝&HBase實驗環境搭建 http://www.linuxidc.com/Linux/2013-04/83560.htm

基於Hadoop集羣的HBase集羣的配置 http://www.linuxidc.com/Linux/2013-03/80815.htm

Hadoop安裝部署筆記之-HBase徹底分佈模式安裝 http://www.linuxidc.com/Linux/2012-12/76947.htm

單機版搭建HBase環境圖文教程詳解 http://www.linuxidc.com/Linux/2012-10/72959.htm

HBase 的詳細介紹請點這裏
HBase 的下載地址請點這裏

本文永久更新連接地址http://www.linuxidc.com/Linux/2016-01/127261.htm

相關文章
相關標籤/搜索