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. [java]Arrays.copyOf() VS System.arrayCopy()

    If we want to copy an array, we can use either System.arraycopy() or Arrays.copyOf(). In this post, ...

  2. linux lvm

    一.linux的lv(logical volume) lv各层次示例图如下: 核心思想:最底层的pv就是一个一个的磁盘,在保证总体容量的情况下,可以移除部分磁盘,在pv上面设置一个vg,相当于vg把所 ...

  3. table+css与xhtmL+css实现同一登陆框(代码片段)

    1.从网上下载了一个不错的登录框样式(table结合css布局): 2.初学xhtmL,自己改写了一下,希望有用 3. 我怎么不会上传截图呢?  ​1. [代码]xhtml_css组合    < ...

  4. APP被应用商店下架了怎么办?

    上周五的时候,知乎被各大APP应用商店下架,原因是因为在应用内一些信息不符合相关的规定所以被强制性下架,看起来只是简单的一个应用被下架的事情.但是作为一个推广人员,我第一件事情想到的是,假如我自己公司 ...

  5. asp.net中关于《%=》《%#》《%》 的用法——(转帖)

    1:在.aspx页面,<% %>标签相当于在.cs页面的代码,也就是说你在.cs文件里面怎样写,就可以在.aspx文件里面的<% %>标签里面怎样写. 2:在.aspx页面,& ...

  6. g++能过,c++过不了

    可能原因: 1.在递归的时候,递归函数中忘记加返回return.(详见Wrong Answer,Memory Limit Exceeded) 代码1:错误 g++--------accepted c+ ...

  7. 关于React前端构建的一般过程 - 理论篇

    概要 本文以个人阅读实践经验归纳前端架构构建过程,以Step by Step方式说明创建一个前端项目的过程.并会对每个阶段所使用的技术进行可替代分析,如Express替换Hapi或者Koa的优缺点分析 ...

  8. 数据库连接池(connection pool)

    1.JDBC数据库连接池的必要性  在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: –      在主程序(如servlet.beans)中建立数据库连接. –      进行sql ...

  9. Cot

    题目大意 两种操作 给坐标上一个直角三角形中每个整点权值$+1$ 求坐标上一个直角三角形中每个整点权值之和 题解 一顿分析思考加推导之后,发现并不存在这样的数据结构(大概是有,只是我不知道),于是考虑 ...

  10. 【JVM】java方法区

    java方法区[名词解析]        --->和java堆一样,方法区是一块所有线程共享的内存区域.        --->保存系统的类信息,比如,类的字段,方法,常量池等.      ...