HBase RowKey 设计的那些事

   在说rowkey设计之前,先回答一下大家配置HBase时可能有的疑问,关于HBase是否需要单独的ZooKeeper托管?嗯,如果只是部署HBase,我建议不要用单独的ZooKeeper进行托管,用HBase自带的ZooKeeper就可以,假如要部署其他应用,比如Spark等可以单独部署一个ZooKeeper集群。好,废话不多说了,下面说说RowKey设计的事。

先谈HBase底层架构

对于新手来说,RowKey的设计是比较陌生的一件事,看上去很简单的东西,其实非常复杂,RowKey的设计基本上可以划分成两大影响,分别是分析维度、查询性能。为什么要这样分呢?我们再回头看看HBase系统架构图:

这种设计看上去并没有什么问题,但是这种设计隐藏了非常多陷阱,假如CompanyCode字段非常固定,而TimeStamp变化比较大的话,会造成单个Region连续地存储这些数据,数据量非常大的时候,这个Region会集中了这些数据,当有应用需要访问这些数据时,造成了RPC timeout,甚至应用程序直接报错,无法执行。

合理的RowKey设计方法

     基于上面的原因,我们需要考虑单点集中以及数据查询两方面的因素,因此,在RowKey上我们要针对这两个问题进行方案设计。

     首先是单点集中问题,我们出现这样单点集中的原因大概有以下几种:

l RowKey前面的字符过于固定

l 集群结点数量过少

集群结点数量是由我们自身硬件资源限制的,这个我们不考虑在内,我们主要考虑RowKey设计。既然是因为前面字符过于集中,那么我们可以通过在RowKey前面添加随机的一个字符串,下面是引自《HBase Essential》里面的一个随机字符计算方法:

int saltNumber = new Long(new Long(timestamp).hashCode()) %<number of region servers>

用这种方法,我们在插入数据的时候可以人为地随机把一断时间内的数据打散,分布到各个RegionServer下的Region中,充分利用分布式的优势,这样做不紧可以加快数据的读写访问,也解决了数据集中的问题。

改良后的RowKey设计方案

     通过上面的技术研讨,可以制定出以下的RowKey设计方案了:

随机字符(2位) + 时间位(14位)+ CompanyCode(4位)

     我在实际测试过程中,前后两种方案对比,前者的MR程序跑了1个小时,后者只花了5分钟。

合理地编写查询代码

     我们完成数据存储之后,假如要取出某部分数值,需要设置Scan查询,以下是我在实战中用到的部分代码,仅供参考:
public class HBaseTableDriver extends Configured implements Tool {

    public int run(String[] arg0) throws Exception {

       if(arg0.length < 4 || arg0.length > 5)

           throw new IllegalArgumentException("The input argument need:start && stop && farmid && turbineNum && calid");

       if(arg0[0].length() != 8 || arg0[1].length() != 8)

           throw new IllegalArgumentException("The date format should be yyyyMMdd");

       Configuration conf = HBaseConfiguration.create();

       conf.set("hbase.zookeeper.quorum", ConstantValues.QUOREM);

       conf.set("hbase.zookeeper.property.clientPort", ConstantValues.CLIENT_PORT);

       //extract table && tagid && start time && end time

       conf.set("start", arg0[0]);

       conf.set("stop", arg0[1]);

        conf.set("farmid", arg0[2]);

       conf.set("turbineNum", arg0[3]);

       conf.set("calid", arg0[4]);

       String startRow = "0" + arg0[0] + " 000000" + arg0[2] + "001";

       String stopRow = "2" + arg0[1] + " 235959" + arg0[2] + RowKeyGenerator.addZero(Integer.parseInt(arg0[3]));

       String targetKpiTableName = "kpi2";

       Job job = Job.getInstance(conf, "KPIExtractor");

        job.setJarByClass(KPIExtractor.class);

        job.setNumReduceTasks(6);

        Scan scan = new Scan();

        scan.addColumn("f".getBytes(), "v".getBytes());

        String regEx = "^\\d{1}(?:" + arg0[0].substring(0, 4) + "|" + arg0[1].substring(0, 4) + ")\\d{17}";

        switch(arg0[4]){

        case "1":

               regEx = regEx + "(?:823|834)$";

               startRow = startRow + "823";

               stopRow = stopRow + "834";

            break;

        case "2":

            regEx = regEx + "211$";

            startRow = startRow + "211";

           stopRow = stopRow + "211";

            break;

        case "3":

            regEx = regEx + "544$";

            startRow = startRow + "544";

           stopRow = stopRow + "544";

            break;

        case "4":

            regEx = regEx + "208$";

            startRow = startRow + "208";

           stopRow = stopRow + "208";

            break;

        case "5":

            regEx = regEx + "(?:739|823)$";

            startRow = startRow + "739";

           stopRow = stopRow + "823";

            break;

        case "6":

            regEx = regEx + "(?:211|823)$";

            startRow = startRow + "211";

           stopRow = stopRow + "823";

            break;

        case "7":

            regEx = regEx + "708$";

            startRow = startRow + "708";

           stopRow = stopRow + "708";

            break;

        case "8":

            regEx = regEx + "822$";

            startRow = startRow + "822";

           stopRow = stopRow + "822";

            break;

        case "9":

            regEx = regEx + "211$";

            startRow = startRow + "211";

           stopRow = stopRow + "211";

            break;

        default:

            throw new IllegalArgumentException("UnKnown Argument calid:"+arg0[4]+",it should be between 1~9");

        }

        scan.setStartRow(startRow.getBytes());

        scan.setStopRow(stopRow.getBytes());

        scan.setFilter(new RowFilter(CompareOp.EQUAL, new RegexStringComparator(regEx)));

        TableMapReduceUtil.initTableMapperJob("hellowrold", scan , KPIMapper.class, ImmutableBytesWritable.class, ImmutableBytesWritable.class, job);

        TableMapReduceUtil.initTableReducerJob(targetKpiTableName, KPIReducer.class, job);

        job.waitForCompletion(true);

       return 0;

    }

}

文章转载自 开源中国社区 [http://www.oschina.net]

时间: 2024-10-02 01:22:08

HBase RowKey 设计的那些事的相关文章

HBase的Rowkey设计(mark)

       在HBase中细节上的设计,最最最重要的就是我该选取什么做Rowkey,Rowkey的选择,最直接的影响就是对你之后分析数据的影响了.      Rowkey是不可分割的字节数,按照字典排序由低到高存储在表中.一个空的数组用来标识表空间的起始或结尾.      在设计HBase表时,Rowkey设计是最重要的事情,应该基于预期的访问模式来为Rowkey建模.Rowkey决定了访问HBase表时可以得到的性能,原因有两个:Region基于Rowkey为一个区间的行提供服务,并且负责区

Hbase 学习(七) rowkey设计

一直以来对rowkey的设计都比较迷茫,<hbase权威指南>倒是给出了个还算靠谱的例子. 下面这个例子有点儿像帖子表结构,它的rowkey设计是这样的,可以简单的理解为,什么人在什么时间发了什么信息,信息包括什么附件,它是用户为主线的一个设计. <userId>-<date>-<messageId>-<attachmentId> 如果我们想查某个用户发的信息,我们可以设置scan的start rowkey 为该userId,end rowkey

分布式数据库HBase表设计

比较常用的数据库是关系型数据库,但很多场景下nosql数据库会更加擅长,从sql到nosql实施的第一步就是设计表结构,这是两种不同的思维方式,这里说下HBase表设计. 需求:需要一张stock表用于保存市场所有股票的分钟走向,即每个股票每分钟记录一次价格. 方案一:瘦表. 用stockId+datetime作为RowKey,这样方便通过stockId或datetime快速扫描获取到相关记录. RowKey ColumnFamily "stock_cf" stockId+dateti

HBase学习总结(5):HBase表设计

一.如何开始模式设计 当我们说到模式(schema),要考虑以下内容: (1)这个表应该有多少个列族? (2)列族使用什么数据? (3)每个列族应该有多少列? (4)列名应该是什么?(尽管列名不必在建表时定义,但是读写数据时是需要知道的.) (5)单元存放什么数据? (6)每个单元存储多少个时间版本? (7)行键结构是什么?应该包括什么信息? 1.问题建模 一个特定列族的所有数据在HDFS上会有一个物理存储.这个物理存储可能由多个HFile组成,理想情况下可以通过合并得到一个HFile.一个列族

Solr与HBase架构设计简析

前提:Solr.SolrCloud提供了一整套的数据检索方案,HBase提供了完善的大数据存储机制. 需求:1.对于添加到HBase中的结构化数据,能够检索出来. 2.数据量较大,达到10亿,100亿数据量. 3.检索的实时性要求较高,秒级更新. 说明:以下是使用Solr和HBase共同搭建的系统架构. 1.1  一次性创建索引 l.  删除全索引 效率很高,可以关闭Solr后,直接删除Data文件. 2. 重新创建全索引 拉取HBase中全数据,分批次创建索引. 1.2  增量创建索引 1.触

迅雷:网页设计那点事

从06年大学毕业到至今,一直在从事网页设计,刚开始在一家主要设计一些房地产项目网站,这类型网站要求:创意.互动.cool等;后来去另一家公司主要是设计一些企业类型的网站,至今主要负责门户类网站设计.经过这么多年的"操练",总结了一些点点滴滴和大家分享,说得不好请猛击指出! 这里我主要说下,门户类网站设计过程中注意的细节及要注意的问题! 门户网站设计有很多种.最为国人熟知的是像迅雷看看.新浪.腾讯网那种信息类门户.现在也有博客门户等.象当当.淘宝那样的购物平台,其实也是一种门户,只不过陈

关于原型设计的一些事

原型设计,是每个交互设计师和产品经理最最最基本的技能.这也是一个梳理思路很好的方式. 关于什么是原型 为了讨论方便,有必要先做一个简单的定义. 这里的原型指的是对最终产品各页面上内容的简单呈现,通常不会设置颜色和字体,也不含图片.这里的原型,也通常被称作线框图.示意图.蓝图.在一些极端的情况下,原型图往往可以先被抽象成一个个的模块组合,然后再去细化每个模块中的内容极其展示形式. 原型的主要作用是为了沟通最初的产品设想.原型图展示的是内容和结构及粗线条的布局,而不是视觉设计. 一定程度上,原型图是

交互设计的那些事

iPhone.Galaxy S3 以及 Lumia 920是市场上如今最引人注目的三款智能手机,正好对应iOS.Android及Windows Phone三大平台,尽管三大操作系统在市场份额.出货量以及用户认可方面都有着不小的差异.但更能我们兴趣,更有讨论价值的其实是这三大平台在设计方面的差异,这不仅最终决定着用户的体验,也决定了开发者的参与热情. 来自微信公众帐号"互联网er的早读课"的这篇文章从包括变局.导航逻辑.设计风格等六大方面的差异对三大平台的交互设计进行了较为深入的分析和讨

交互设计的那些事:ANDROID、IOS和WP平台的六大差异

iPhone.Galaxy S3 以及 Lumia 920是市场上如今最引人注目的三款智能手机,正好对应iOS.Android及Windows Phone三大平台,尽管三大操作系统在市场份额.出货量以及用户认可方面都有着不小的差异.但更能我们兴趣,更有讨论价值的其实是这三大平台在设计方面的差异,这不仅最终决定着用户的体验,也决定了开发者的参与热情. 一,布局形式的差异 1)iOS 经典的"tab bar"在 iOS 应用内如果要切换不同的模块,或者页面内要切换不同的栏目,往往都会用到&