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 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

HBase表数据分页处理的更多相关文章

  1. 数据分页处理系列之二:HBase表数据分页处理

      HBase是Hadoop大数据生态技术圈中的一项关键技术,是一种用于分布式存储大数据的列式数据库,关于HBase更加详细的介绍和技术细节,朋友们可以在网络上进行搜寻,笔者本人在接下来的日子里也会写 ...

  2. HBase(三): Azure HDInsigt HBase表数据导入本地HBase

    目录: hdfs 命令操作本地 hbase Azure HDInsight HBase表数据导入本地 hbase hdfs命令操作本地hbase: 参见  HDP2.4安装(五):集群及组件安装 , ...

  3. 一种HBase表数据迁移方法的优化

    1.背景调研: 目前存在的hbase数据迁移主要分如下几类: 根据上图,可以看出: 其实主要分为两种方式:(1)hadoop层:因为hbase底层是基于hdfs存储的,所以可以通过把hdfs上的数据拷 ...

  4. 数据分页处理系列之一:Oracle表数据分页检索SQL

      关于Oracle数据分页检索SQL语法,网络上比比皆是,花样繁多,本篇也是笔者本人在网络上搜寻的比较有代表性的语法,绝非本人原创,贴在这里,纯粹是为了让"数据分页专题系列"看起 ...

  5. spark读HFile对hbase表数据进行分析

    要求:计算hasgj表,计算每天新增mac数量. 因为spark直接扫描hbase表,对hbase集群访问量太大,给集群造成压力,这里考虑用spark读取HFile进行数据分析. 1.建立hasgj表 ...

  6. HBase表数据的转移之使用自定义MapReduce

    目标:将fruit表中的一部分数据,通过MR迁入到fruit_mr表中 Step1.构建ReadFruitMapper类,用于读取fruit表中的数据 package com.z.hbase_mr; ...

  7. hbase操作(shell 命令,如建表,清空表,增删改查)以及 hbase表存储结构和原理

    两篇讲的不错文章 http://www.cnblogs.com/nexiyi/p/hbase_shell.html http://blog.csdn.net/u010967382/article/de ...

  8. 使用MapReduce查询Hbase表指定列簇的全部数据输出到HDFS(一)

    package com.bank.service; import java.io.IOException; import org.apache.hadoop.conf.Configuration;im ...

  9. 浅谈hbase表中数据导出导入(也就是备份)

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=23916356&id=3321832 最近因为生产环境hbase ...

随机推荐

  1. Excel 2007中自定义数字格式前要了解的准则

    要在Excel 2007中创建自定义数字格式,首先应了解自定义数字格式的准则,并从选择某一内置数字格式开始.然后,可以更改该格式的任意代码部分,从而创建自己的自定义数字格式. 数字格式最多可包含四个代 ...

  2. runtime 实现方法交换 viewwillappear方法

    1.新建分类 #import "UIViewController+swizzling.h"#import <objc/runtime.h> @implementatio ...

  3. running a background task over ssh

    原文: Why does running a background task over ssh fail if a pseudo-tty is allocated? 问题: I've recently ...

  4. 如何在unigui中用代码展开一棵树?

    procedure expandtree(tree:tunitreeview);begin   UniSession.AddJS('setTimeout("' + Tree1.JSName ...

  5. C语言链表结构体(学习笔记)

    #include <stdio.h> #define LENTEST 100 // 采取逐步删除的方法求的素数 //先假设1-100都是素数,然后剔除2的倍数, //3的倍数,直到剔除所有 ...

  6. django-pagination分页

    1. 将该APP安装至Django项目中.(settings.py) INSTALLED_APPS = ( # ... 'pagination', ) 2. 在Django项目的middleware中 ...

  7. PHP之冒泡排序的优化

    冒泡排序是一个常见的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成. 对于冒泡排序 ...

  8. BEC listen and translation exercise 38

    很高兴看到有这么多人想了解我们的体育设施.It's good to see that there are so many people wanting to find out about our sp ...

  9. Java中日期和时间的相关问题

    1.java.lang.System类 System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位 ...

  10. 关于解决SSHD 连接 认证失败的问题

    网上找有很多方法,有时候情况不一样 ,也不实用 其实找到解决问题的思路更总要 首先分析日志文件 less /var/log/secure | grep sshd ,看具体出现什么问题 然后再去搜索相关 ...