轉自:http://www.aboutyun.com/forum.php?mod=viewthread&tid=7030&extra=page=1
可以帶着下面問題來閱讀
1.hbasef分頁什么情況下比較有用?
2.hbase分頁的思路是什么?
3.hbase分頁代碼與sql分頁代碼有什么區別和聯系?
一、hbase分頁應用場景:
一、應用場景
hbase到底需不需要分頁,hbase的數據量肯定不少,肯定是需要分頁的。很多人認為數量量如此大,怎么會展示。這個從客戶角度來講,我們做的系統,不可能會給機器看的。這里面我們可以對其進行統計分析,這樣利於我們決策。
比如我們:平台中有一個場景是要做用戶歷史訂單數據的查詢,並且需要支持分頁。這里只是舉了一個場景,后面大家可以根據自己的經驗。下面給大家講講分頁的思路。
二、hbase分頁思路:
hbase通過scan來掃描表,通過startKey,stopKey來確定范圍,hbase官方提供了一個PageFilter來支持一次scan可以返回多少條數據即每頁的行數。假如一頁是10條,這樣是第一頁還好,但是第二頁呢,如果不改變PageFilter的pageSize,那返回的還是第一頁的數據,如果改變pageSize為20,則返回了第一頁10多余的數據,在客戶端要過濾掉,性能不好。那怎么辦呢,方法就是在查詢下一頁時,指定下一頁的startKey,這樣PageFilter每次就不會返回多余的記錄,stopKey可以不用變,那現在問題是,怎么得到下一頁的startKey(即下一頁第一行的rowkey)呢?,有兩種方法來取每一頁的startKey
1. 上一頁的最后一行記錄的rowkey作為下一頁的startKey。
2. 在每次scan時多取一條記錄,即把下一頁第一條行頁取出來,把該行的rowkey做為下一頁的startKey。
這兩種方法,都要注意,hbase scan時是包含startKey的,如果是采用第一種,則要在記錄多取一條,排除第一條。第二種頁是多取一條,但是排除最后一條,用來做下一頁的startKey。還有需要注意的是在計算是否有下一頁時,可以根據返回的條數來判斷。
startKey怎么取沒有問題了。但是怎么存儲呢,有同學可能會想到存到session,但是如果你的服務是rest api型的,就沒有session的概念了。那還有兩種選擇:
1. 是存到客戶端,讓客戶端每次請求時把startKey再傳回來,這樣需要依賴客戶端,如果客戶端是遠程,或者是開放平台的情況下,可能不合適。
2. 存在服務端,存在服務端需要注意並發訪問的情況。比如scan同一個表,一個訪問第2頁,一個訪問第3頁,服務端就需要對每一個table的scan 存每一頁的startKey,需要為同一個查詢條件包含pageSize,因為pageSize不一樣,startKey也會不一樣,
在服務crash情況下,從起后都從第一頁開始。
我自己是采用第二種方案,存在服務端.
----------------------------------------------------------------------------------------------------------------------------------------------------
三、代碼實現
- import java.io.IOException;
- import java.util.LinkedHashMap;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.hbase.HBaseConfiguration;
- import org.apache.hadoop.hbase.client.Get;
- import org.apache.hadoop.hbase.client.HTableInterface;
- import org.apache.hadoop.hbase.client.HTablePool;
- import org.apache.hadoop.hbase.client.Result;
- import org.apache.hadoop.hbase.client.ResultScanner;
- import org.apache.hadoop.hbase.client.Scan;
- import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
- import org.apache.hadoop.hbase.filter.Filter;
- import org.apache.hadoop.hbase.filter.FilterList;
- import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
- import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
- import org.apache.hadoop.hbase.util.Bytes;
- public class HBaseUtils {
- private static Configuration config = null;
- private static HTablePool tp = null;
- static {
- // 加載集群配置
- config = HBaseConfiguration.create();
- config.set("hbase.zookeeper.quorum", "xx.xx.xx");
- config.set("hbase.zookeeper.property.clientPort", "2181");
- // 創建表池(可偉略提高查詢性能,具體說明請百度或官方API)
- tp = new HTablePool(config, 10);
- }
- /*
- * 獲取hbase的表
- */
- public static HTableInterface getTable(String tableName) {
- if (StringUtils.isEmpty(tableName))
- return null;
- return tp.getTable(getBytes(tableName));
- }
- /* 轉換byte數組 */
- public static byte[] getBytes(String str) {
- if (str == null)
- str = "";
- return Bytes.toBytes(str);
- }
- /**
- * 查詢數據
- * @param tableKey 表標識
- * @param queryKey 查詢標識
- * @param startRow 開始行
- * @param paramsMap 參數集合
- * @return 結果集
- */
- public static TBData getDataMap(String tableName, String startRow,
- String stopRow, Integer currentPage, Integer pageSize)
- throws IOException {
- List<Map<String, String>> mapList = null;
- mapList = new LinkedList<Map<String, String>>();
- ResultScanner scanner = null;
- // 為分頁創建的封裝類對象,下面有給出具體屬性
- TBData tbData = null;
- try {
- // 獲取最大返回結果數量
- if (pageSize == null || pageSize == 0L)
- pageSize = 100;
- if (currentPage == null || currentPage == 0)
- currentPage = 1;
- // 計算起始頁和結束頁
- Integer firstPage = (currentPage - 1) * pageSize;
- Integer endPage = firstPage + pageSize;
- // 從表池中取出HBASE表對象
- HTableInterface table = getTable(tableName);
- // 獲取篩選對象
- Scan scan = getScan(startRow, stopRow);
- // 給篩選對象放入過濾器(true標識分頁,具體方法在下面)
- scan.setFilter(packageFilters(true));
- // 緩存1000條數據
- scan.setCaching(1000);
- scan.setCacheBlocks(false);
- scanner = table.getScanner(scan);
- int i = 0;
- List<byte[]> rowList = new LinkedList<byte[]>();
- // 遍歷掃描器對象, 並將需要查詢出來的數據row key取出
- for (Result result : scanner) {
- String row = toStr(result.getRow());
- if (i >= firstPage && i < endPage) {
- rowList.add(getBytes(row));
- }
- i++;
- }
- // 獲取取出的row key的GET對象
- List<Get> getList = getList(rowList);
- Result[] results = table.get(getList);
- // 遍歷結果
- for (Result result : results) {
- Map<byte[], byte[]> fmap = packFamilyMap(result);
- Map<String, String> rmap = packRowMap(fmap);
- mapList.add(rmap);
- }
- // 封裝分頁對象
- tbData = new TBData();
- tbData.setCurrentPage(currentPage);
- tbData.setPageSize(pageSize);
- tbData.setTotalCount(i);
- tbData.setTotalPage(getTotalPage(pageSize, i));
- tbData.setResultList(mapList);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- closeScanner(scanner);
- }
- return tbData;
- }
- private static int getTotalPage(int pageSize, int totalCount) {
- int n = totalCount / pageSize;
- if (totalCount % pageSize == 0) {
- return n;
- } else {
- return ((int) n) + 1;
- }
- }
- // 獲取掃描器對象
- private static Scan getScan(String startRow, String stopRow) {
- Scan scan = new Scan();
- scan.setStartRow(getBytes(startRow));
- scan.setStopRow(getBytes(stopRow));
- return scan;
- }
- /**
- * 封裝查詢條件
- */
- private static FilterList packageFilters(boolean isPage) {
- FilterList filterList = null;
- // MUST_PASS_ALL(條件 AND) MUST_PASS_ONE(條件OR)
- filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
- Filter filter1 = null;
- Filter filter2 = null;
- filter1 = newFilter(getBytes("family1"), getBytes("column1"),
- CompareOp.EQUAL, getBytes("condition1"));
- filter2 = newFilter(getBytes("family2"), getBytes("column1"),
- CompareOp.LESS, getBytes("condition2"));
- filterList.addFilter(filter1);
- filterList.addFilter(filter2);
- if (isPage) {
- filterList.addFilter(new FirstKeyOnlyFilter());
- }
- return filterList;
- }
- private static Filter newFilter(byte[] f, byte[] c, CompareOp op, byte[] v) {
- return new SingleColumnValueFilter(f, c, op, v);
- }
- private static void closeScanner(ResultScanner scanner) {
- if (scanner != null)
- scanner.close();
- }
- /**
- * 封裝每行數據
- */
- private static Map<String, String> packRowMap(Map<byte[], byte[]> dataMap) {
- Map<String, String> map = new LinkedHashMap<String, String>();
- for (byte[] key : dataMap.keySet()) {
- byte[] value = dataMap.get(key);
- map.put(toStr(key), toStr(value));
- }
- return map;
- }
- /* 根據ROW KEY集合獲取GET對象集合 */
- private static List<Get> getList(List<byte[]> rowList) {
- List<Get> list = new LinkedList<Get>();
- for (byte[] row : rowList) {
- Get get = new Get(row);
- get.addColumn(getBytes("family1"), getBytes("column1"));
- get.addColumn(getBytes("family1"), getBytes("column2"));
- get.addColumn(getBytes("family2"), getBytes("column1"));
- list.add(get);
- }
- return list;
- }
- /**
- * 封裝配置的所有字段列族
- */
- private static Map<byte[], byte[]> packFamilyMap(Result result) {
- Map<byte[], byte[]> dataMap = null;
- dataMap = new LinkedHashMap<byte[], byte[]>();
- dataMap.putAll(result.getFamilyMap(getBytes("family1")));
- dataMap.putAll(result.getFamilyMap(getBytes("family2")));
- return dataMap;
- }
- private static String toStr(byte[] bt) {
- return Bytes.toString(bt);
- }
- public static void main(String[] args) throws IOException {
- // 拿出row key的起始行和結束行
- // #<0<9<:
- String startRow = "aaaa#";
- String stopRow = "aaaa:";
- int currentPage = 1;
- int pageSize = 20;
- // 執行hbase查詢
- getDataMap("table", startRow, stopRow, currentPage, pageSize);
- }
- }
- class TBData {
- private Integer currentPage;
- private Integer pageSize;
- private Integer totalCount;
- private Integer totalPage;
- private List<Map<String, String>> resultList;
- public Integer getCurrentPage() {
- return currentPage;
- }
- public void setCurrentPage(Integer currentPage) {
- this.currentPage = currentPage;
- }
- public Integer getPageSize() {
- return pageSize;
- }
- public void setPageSize(Integer pageSize) {
- this.pageSize = pageSize;
- }
- public Integer getTotalCount() {
- return totalCount;
- }
- public void setTotalCount(Integer totalCount) {
- this.totalCount = totalCount;
- }
- public Integer getTotalPage() {
- return totalPage;
- }
- public void setTotalPage(Integer totalPage) {
- this.totalPage = totalPage;
- }
- public List<Map<String, String>> getResultList() {
- return resultList;
- }
- public void setResultList(List<Map<String, String>> resultList) {
- this.resultList = resultList;
- }
- }