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