文档章节

HBase RowKey设计的那些事

loki_lan
 loki_lan
发布于 2015/07/13 10:07
字数 1130
阅读 3469
收藏 77

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

先谈HBase底层架构

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

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

合理的RowKey设计方法

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

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

RowKey前面的字符过于固定

l  集群结点数量过少

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

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

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

改良后的RowKey设计方案

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

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

         我在实际测试过程中,前后两种方案对比,前者的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;

    }

   

}



注意点:

l  这里主要用到了RowFilter对RowKey进行过滤,并且我在查阅相关资料的时候,别人建议不要在大数据量下使用ColumnFilter,性能非常低。

l  可以通过Configuration为Map/Reduce传输参数值。

© 著作权归作者所有

loki_lan

loki_lan

粉丝 89
博文 87
码字总数 54760
作品 0
中山
程序员
私信 提问
加载中

评论(8)

loki_lan
loki_lan 博主

引用来自“dragon_blog”的评论

前面两个字符是随机的话,那么我如果想查今天早上10点到11点的数据,这个scan是会所有region都会检索一遍吧
这个肯定不会的,利用filter跟start、stop row限制,这样恰好利用了分布式存储的优点。
掠水
掠水
随机数方案劣势较大。不便查询。
一致性hash算法用来做散列很合适。
kidbei
kidbei
如果想范围检索的话这样的设计不可取,如果拿来当纯键值查询那就差不多
s
sdasfd34
前面两个字符是随机的话,那么我如果想查今天早上10点到11点的数据,这个scan是会所有region都会检索一遍吧
沃德天拉莫帅
沃德天拉莫帅

引用来自“成熟的毛毛虫”的评论

这样的rowkey能否以CompanyCode进行模糊查询呢?把业务相关的信息往前放,方便模糊查询,比如这样:随机字符(2位) + CompanyCode(4位)+ 时间位(14位)换成这样会不会好点?0

引用来自“loki_lan”的评论

这个取决于你有业务更偏向哪个维度查询,因此你说的只要符合你的业务场景,就是最优的,忘记在博文说了,你看得真用心,感谢提醒,哈哈 :)
之前也设计过,所以略懂,key的设计就是hbase的整体灵魂了,因为key查询不太方便,我记得当初也用sql数据库来冗余key,整个业务查询过程是先从sql查出一个范围的key,然后再到hbase查询出相应的记录
loki_lan
loki_lan 博主

引用来自“成熟的毛毛虫”的评论

这样的rowkey能否以CompanyCode进行模糊查询呢?把业务相关的信息往前放,方便模糊查询,比如这样:随机字符(2位) + CompanyCode(4位)+ 时间位(14位)换成这样会不会好点?0
这个取决于你有业务更偏向哪个维度查询,因此你说的只要符合你的业务场景,就是最优的,忘记在博文说了,你看得真用心,感谢提醒,哈哈 :)
沃德天拉莫帅
沃德天拉莫帅
这样的rowkey能否以CompanyCode进行模糊查询呢?把业务相关的信息往前放,方便模糊查询,比如这样:随机字符(2位) + CompanyCode(4位)+ 时间位(14位)换成这样会不会好点?0
叶秀兰
叶秀兰
赞~
HBase基本知识介绍及典型案例分析

本文来自于2018年10月20日由中国 HBase 技术社区在武汉举办的中国 HBase Meetup 第六次线下交流会。 HBase基本知识介绍及典型案例分析 PPT 下载:http://hbase.group/slides/162 本次分享的内...

hbase小能手
2018/11/19
0
0
hbase 数据库简介安装与常用命令的使用

一:hbase 简介与架构功能 二:hbase 安装与配置 三:hbase 常见shell 命令操作 一:hbase 简介与架构功能 1.1 为什么要使用hbase 数据库 1.2 hbase 简介: 1.3 HBase与Hadoop的对比 1.4 HB...

flyfish225
2018/04/12
0
0
HBase之Rowkey设计总结

本文转载自:HBase之Rowkey设计总结及易观方舟实战篇 一、引言 HBase由于其存储和读写的高性能,在OLAP即时分析中越来越发挥重要的作用,在易观精细化运营产品–易观方舟也有广泛的应用。作为...

淡淡的倔强
2018/08/15
0
0
Hbase Schema 设计注意事项及最佳实践总结

一个列族的所有列在硬盘上存放在一起,使用这个特性可以把不同访问模式的列放在不同列族,以便隔离它们。这也是HBase被称为面向列族的存储(column-family-oriented store)的原因。 1、RowKe...

PeakFang-BOK
2018/10/21
101
0
修改HBase的rowkey设计把应用的QPS从5W提升到50W

UTT是Aliexpress的营销消息运营平台,运营希望促销活动时APP消息推送的QPS达到34W。 UTT刚接入APP消息推送时,QPS只能达到5W,离运营的要求有很大的距离。 通过改造,QPS达到了50W,其中最主...

饭菜两光
2018/08/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Nginx 快速安装详解

一、Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамбле...

网络小虾米
12分钟前
4
0
技术分享 | slave_relay_log_info 表认知的一些展开

作者:胡呈清 slave_relay_log_info 表是这样的: mysql> select * from mysql.slave_relay_log_info\G *************************** 1. row *************************** Number_of_lin......

爱可生
14分钟前
3
0
nginx配置http访问自动跳转到https

server {listen 80;server_name www.域名.com;rewrite ^(.*) https://$server_name$1 permanent;}server {listen 443;server_name www.域名.com;root /home/www;ssl on;......

很好亦平凡ms
14分钟前
2
0
SpreadJS:一款中国研发的类Excel开发工具,功能涵盖Excel的 95% 以上

Excel 作为一款深受用户喜爱的电子表格工具,借助其直观的界面、出色的计算性能、数据分析和图表,已经成为数据统计领域不可或缺的软件之一。 基于Excel对数据处理与分析的卓越表现,把Excel...

葡萄城技术团队
14分钟前
2
0
用javafx框架tornadofx做了个天气预报的程序

class WeatherApp : App(WeatherView::class)class WeatherView : View("十五天天气预报") { val weatherVM: WeatherViewModel by inject() val controller: WeatherController by......

oschina4cyy
18分钟前
3
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部