• HBase表数据分页处理


    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

  • 相关阅读:
    大话设计模式Python实现-代理模式
    大话设计模式Python实现-装饰模式
    大话设计模式Python实现-策略模式
    设计模式Python实现-简单工厂模式
    Python文件读写机制
    python 多线程剖析
    I/O多路复用-EPOLL探索
    Python学习笔记:魔术方法详解
    Django学习笔记:为Model添加Action
    【Django】Django Debug Toolbar调试工具配置
  • 原文地址:https://www.cnblogs.com/zhangzl/p/9003086.html
Copyright © 2020-2023  润新知