Hbase 设计与开发实战
Posted 电商与管理
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hbase 设计与开发实战相关的知识,希望对你有一定的参考价值。
大数据技术如火如荼,在大数据挖掘及分析平台技术中,作为海量数据操作及高客户端并发解决方案的 NoSQL 技术特别是 Hbase 首当其冲,在众多项目中得到广泛的应用,但对于 Hbase 表设计模式/IO 考虑/性能调优等实战经验性资料较为匮乏。 本文介绍了 Hbase 的数据模型原理,分析了 Hbase 表扫描/查询操作的时间复杂度,并通过一个游戏公司客户实际案例的讲解,分析了 Hbase 表设计及开发在实际案例中的运用,对比了不同的 Hbase 设计考量对客户端访问模式及检索性能的差异。读者通过案例中 Hbase 表设计模式可以更深刻的理解 Hbase 原理及设计,并且熟悉 Hbase 客户端开发的思路及实现。 |
传统的关系型数据库处理方式是基于全面的ACID保证,遵循SQL92的标准表设计模式(范式)和数据类型,基于SQL语言的DML数据交互方式。长期以来这种基于关系型数据库的IT信息化建设中发展良好,但受制于关系型数据库提供的数据模型,对于逐渐出现的,为预先定义模型的数据集,关系型数据库不能很好的工作。越来越多的业务系统需要能够适应不同种类的数据格式和数据源,不需要预先范式定义,经常是非结构化的或者半结构化的(如用户访问网站的日志),这需要系统处理比传统关系型数据库高几个数量级的数据(通常是TB及PB规模级别)。传统关系型数据库能够纵向扩展到一定程度(如Oracle的RAC,IBM的pureScale)。但这通常意味着高昂的软件许可费用和复杂的应用逻辑。
基于系统需求发生了巨大变化,数据技术的先驱们不得不重新设计数据库,基于大数据的NoSQL的曙光就这样出现了,大数据及NoSQL的使用首先在google、facebook等互联网公司,随后是金融、电信行业,众多Hadoop&NoSQL的开源大数据项目如雨后春笋般发展,被互联网等公司用于处理海量和非结构化类型的数据。一些项目关注于快速key-value的键值存储,一些关注内置数据结构或者基于文档的抽象化,一些NoSQL数据管理技术框架为了性能而牺牲当前的数据持久化,不支持严格的ACID,一些开源框架甚至为了性能放弃写数据到硬盘……
Hbase就是NoSQL中卓越的一员,Hbase提供了键值API,承诺强一致性,所以客户端能够在写入后马上看到数据。HBase依赖Hadoop底层分布式存储机制,因此能够运行在多个节点组成的集群上,并对客户端应用代码透明,从而对每个开发人员来说设计和开发Hbase的大数据项目变得简单易行。Hbase被设计来处理TB到PB级的数据,并针对该类海量数据和高并发访问做了优化,作为Hadoop生态系统的一部分,它依赖Hadoop其他组件提供的重要功能,如DataNode数据冗余和MapReduce批注处理。
本文中我们简要介绍下Hbase的架构及框架,Hbase是一种专门为半结构化数据和水平扩展性设计的数据库。它把数据存储在表中,表按“行健,列簇,列限定符和时间版本”的四维坐标系来组织。Hbase是无模式数据库,只需要提前定义列簇,并不需要指定列限定符。同时它也是无类型数据库,所有数据都是按二进制字节方式存储的,对Hbase的操作和访问有5个基本方式,即Get、Put、Delete和Scan以及Increment。Hbase基于非行健值查询的唯一途径是通过带过滤器的扫描。
由于Hbase针对PB、TB存储级别,亿级行数据的海量表记录的高并发,极限性能查询检索的设计初衷,Hbase在物理架构方面设计成一个依靠HadoopHDFS的全分布式的存储集群,并基于Hadoop的MapReduce网格计算框架,用以支持高吞吐量数据访问,支持可用性和可靠性,其整体架构如下图所示:
图1.Hbase整体架构图
从上图我们可以看出Hbase的组成部件,HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,由HRegionServer管理,管理哪些HRegion由HMaster分配。
HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族(ColumnFamily)创建一个Store实例,每个Store都会有0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile,HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。此外,每个HRegion还拥有一个MemStore内存缓存实例。
HBase存储格式是基于Hadoop的HDFS分布式文件系统,HBase中的所有数据文件都存储在HadoopHDFS上,格式主要有两种:
(1)HFile:HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile。
(2)HLogFile:HBase中WAL(WriteAheadLog)的存储格式,物理上是Hadoop的SequenceFile。
HBase是基于类似Google的BigTable的面向列的分布式存储系统,其存储设计是基于Memtable/SSTable设计的,主要分为两部分:一部分为内存中的MemStore(Memtable),另外一部分为HDFS上的HFile(SSTable)。还有就是存储WAL的log,主要实现类为HLog。
(3)MemStore:MemStore即内存里放着的保存KEY/VALUE映射的MAP,当MemStore(默认64MB)写满之后,会开始flush到磁盘(即Hadoop的HDFS上)的操作。
为帮助读者理解,这里我们再把HFile更detail的数据结构做简要的介绍,HFile是基于HadoopTFile的文件类型,其结构如下图所示:
图2.HFile结构图
如上图所示,HFile的文件长度是变长的,仅FILEINFO/Trailer部分是定长,Trailer中有指针指向其他数据块的起始点。而Index数据块则记录了每个Data块和Meta块的起始点。Data块和Meta块都是可有可无的,但对于大多数HFile,都有Data块。
HLog用来存放HBase的日志文件,与传统关系型数据库类似,为保证读一致性及undo/redo回滚等数据恢复操作,Hbase在写入数据时即先进行write-ahead-log(WAL)操作。每个HRegionServer对应一个HLog实例,HRegion在初始化的时候HRegionServer会将该HLog作为构造函数传入其中,以便初始化HLog实例。
HLogFile是一个SequenceFile,只能在文件的末尾添加内容。除了文件头以外,HLogFile由一条条HLog.Entry构成。Entry是HLog的基本组成部分,也是Read/Write的基本单位。
读者如果对HBase架构的细节内容感兴趣,可以通过Hbase官网,了解Hbase整体架构及底层物理存储机制。
Hbase检索时间复杂度
既然使用Hbase的目的是高效、高可靠、高并发的访问海量非结构化数据,那么Hbase检索数据的时间复杂度是关系到基于Hbase的业务系统开发设计的重中之重,Hbase的运算有多快,本文从计算机算法的数学角度做简要分析,以便读者理解后文的项目实例中Hbase业务建模及设计模式中的考量因素。
我们先以如下变量定义Hbase的相关数据信息:
n=表中KeyValue条目数量(包括Put结果和Delete留下的标记)
b=HFile李数据库(HFileBlock)的数量
e=平均一个HFile里面KeyValue条目的数量(如果知道行的大小,可以计算得到)
c=每行里列的平均数量
我们知道Hbase中有两张特殊表:-ROOT-&.META.,其中.META.表记录Region分区信息,同时,.META.也可以有多个Region分区,同时-ROOT-表又记录.META.表的Region信息,但-ROOT-只有一个Region,而-ROOT-表的位置由Hbase的集群管控框架,即Zookeeper记录。
关于-ROOT-&.META.表的细节这里不再累述,感兴趣的读者可以参阅Hbase–ROOT-及.META.表资料,理解HbaseIO及数据检索时序原理。
Hbase检索一条数据的流程如下图所示。
图3.Hbase检索示意图
如上图我们可以看出,Hbase检索一条客户数据需要的处理过程大致如下:
(1)如果不知道行健,直接查找列key-value值,则你需要查找整个region区间,或者整个Table,那样的话时间复杂度是O(n),这种情况是最耗时的操作,通常客户端程序是不能接受的,我们主要分析针对行健扫描检索的时间复杂度情况,也就是以下2至4步骤的内容。
(2)客户端寻找正确的RegionServer和Region。话费3次固定运算找到正确的region,包括查找ZooKeeper,查找-ROOT-表,找找.META表,这是一次O(1)运算。
(3)在指定Region上,行在读过程中可能存在两个地方,如果还没有刷写到硬盘,那就是在MemStore中,如果已经刷写到硬盘,则在一个HFile中
假定只有一个HFile,这一行数据要么在这个HFile中,要么在Memstore中。
(4)对于后者,时间复杂度通常比较固定,即O(loge),对于前者,分析起来要复杂得多,在HFile中查找正确的数据块是一次时间复杂度为O(logb)的运算,找到这一行数据后,再查找列簇里面keyvalue对象就是线性扫描过程了(同一列簇的列数据通常是在同一数据块中的),这样的话扫描的时间复杂度是O(elb),如果列簇中的列数据不在同一数据块,则需要访问多个连续数据块,这样的时间复杂度为O(c),因此这样的时间复杂度是两种可能性的最大值,也就是O(max(c,elb)
综上所述,查找Hbase中某一行的时间开销为:
O(1)用于查找region
+O(loge)用来在region中定位KeyValue,如果它还在MemStore中
+O(logb)用来查找HFile里面正确的数据块
+O(max(celb)用来查找HFile
对上述O(1),log等标记不熟悉的读者,可以参见<https://en.wikibooks.org/wiki/Data_Structures/Asymptotic_Notation>
Hbase设计实战
由上文介绍的Hbase的整体架构及检索的时间复杂度分析我们可以看出,行键、列簇等的设计及数据存储决定了Hbase总体的性能及执行查询的效率,很多使用Hbase的项目及技术人员能熟练的使用HbaseShell或SDKAPI访问Hbase,进行表创建、删除等DDL,以及put/delete/scan等DML操作,但并深入探究需要多少个列簇,一个列簇需要多少列,什么数据应该存入列名中,以及什么数据应该存入单元等开发设计中的关键问题。
基于Hbase的系统设计与开发中,需要考虑的因素不同于关系型数据库,Hbase模式本身很简单,但赋予你更多调整的空间,有一些模式写性能很好,但读取数据时表现不好,或者正好相反,类似传统数据库基于范式的OR建模,在实际项目中考虑Hbase设计模式是,我们需要从以下几方面内容着手:
1. 这个表应该有多少个列簇
2. 列簇使用什么数据
3. 每个列簇应有多少个列
4. 列名应该是什么,尽管列名不必在建表时定义,但是读写数据时是需要的
5. 单元应该存放什么数据
6. 每个单元存储什么时间版本
7. 行健结构是什么,应该包括什么信息
以下我们以一个使用Hbase技术的真实客户案例为例,说明Hbase设计模式在真实项目中的实践,并通过不同的表设计模式,可以看出在模式是如何影响到表结构和读写表的方式方法,以及对客户端检索查询的性能的影响
客户场景介绍
客户简介:客户是一个互联网手机游戏平台,需要针对广大手游玩家进行手游产品的统计分析,需要存储每个手游玩家即客户对每个手游产品的关注度(游戏热度),且存储时间维度上的关注度信息,从而能针对客户的喜好进行挖掘并进行类似精准营销的手游定点推送,广告营销等业务,从而扩大该平台的用户量并提升用户粘着度。
该平台上手游产品分类众多,总共在500余以上,注册玩家(用户帐号)数量在200万左右,在线玩家数量5万多,每天使用手游频率峰值在10万/人次以上,年增量10%以上。
根据以上需求,手游产品动态增长,无法确定哪些手游产品需要被存储,全部存储又会表超过200列,造成大量空间浪费,玩家每天使用手游的频率及分类不确定,客户注册用户超百万,按天的使用热度数据量超过1000万行,海量数据也使得表查询及业务分析需要的集群数量庞大及SQL优化,效率低下,因此传统关系型数据库不适合该类数据分析和处理的需求,在项目中我们决定采用Hbase来进行数据层的存储的分析。
高表设计
假设每天每个手游玩家对每个产品的关注度都存在该表中,则一个可能的设计方案是每个用户每天对应一行,一用户ID+当天的时间戳作为行健,建立一个保存手游产品使用信息的列簇,每列代表该天该用户对该产品的使用次数。
本案例中我们只设计一个列簇,一个特定的列簇在HDFS上会由一个Region负责,这个region下的物理存储可能有多个HFile,一个列簇使得所有列在硬盘上存放在一起,使用这个特性可以使不同类型的列数据放在不同的列簇上,以便隔离,这也是Hbase被称为面向列存储的原因,在这张表里,因为所有手游产品并没有明显的分类,对表的访问模式也不需区分手游产品类型,因此并不需要多个列簇的划分,你需要意识到一点:一旦创建了表,任何对该表列簇的动作通常都需要先让表offline。
我们可以使用HbaseShell或者HbaseSDKapi创建表,Hbaseshell脚本示例如下:
清单1.Hbaseshell脚本示例
$hbase shell Version 0.92.0, r1231986, Mon Nov 16 13:16:35 UTC 2015 $hbase(main):001:0 >create 'prodFocus' , 'degeeInfo' 0 row(s) in 0.1200 seconds hbase(main):008:0> describe 'prodFocus' DESCRIPTION ENABLED 'prodFocus', {NAME => 'cf', DATA_BLOCK_ENCODING => true 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VE RSIONS => '0', TTL => '2147483647', KEEP_DELETED_CE LLS => 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'} 1 row(s) in 0.0740 seconds |
现在的表如下图所示,一个存有示例数据的表。
表1.prodFocus表示例
rowkey:用户ID$当天时间 |
degee(列簇,手游热度信息) |
||
QQ121102645$20141216 |
degee:3DARPG:6 |
degeeInfo:DTLegend:1 |
|
WeiXin_295y603765de8$12140928 |
degree:DTLegend:3 |
||
ChaoChenYY$12141109 |
degree:3CountryBattle:1 |
degree:ForgetXian:1 |
|
QQ5863976645$20141214 |
degree:Frus3D:2 |
||
HeXaoYang$20140907 |
degree:SpaceHunter:1 |
degree:3CountryBattle:2 |
degree:Frus3D:1 |
FengKe_Tony$20150216 |
degree:DTLegend:1 |
||
junping_Jeff$20141204 |
degree:Frus3D:2 |
||
XiaoFenXia$20150716 |
degree:ForgetXian:3 |
表设计解释如下:
rowkey为QQ121102645$20141216表示帐号为QQ121102645的手游玩家(以QQ号联邦认证的)在2014年12月16日当天的游戏记录;列簇focuspro记录该行账户当天对每种产品类型的点击热度(游戏次数),比如SpaceHunter::1表示玩(或者点开)SpaceHunter:(时空猎人)的次数为1次
现在你需要检验这张表是否满足需求,为此最重要的事是定义访问模式,也就是应用系统如何访问Hbase表中的数据,在整个Hbase系统设计开发过程中应该尽早这么做。
我们现在来看,我们设计的该Hbase表是否能回答客户关心的问题:比如“帐号为QQ121102645的用户关注过哪些手游?”,沿着这个方向进一步思考,有相关业务分析的问题:“QQ121102645用户是否玩过3CountryBattle(三国3)手游?”“哪些用户关注了DTLegend(刀塔传奇)?”“3CountryBattle(三国3)手游被关注过吗?”
基于现在的prodFocus表设计,要回答“帐号为QQ121102645的用户关注过哪些手游?”这个访问模式,可以在表上执行一个简单的Scan扫描操作,该调用会返回整个QQ121102645前缀的整个行,每一行的列簇进行列遍历就能找到用户关注的手游列表。
代码示例如下:
清单2.客户端查询用户关注手游列表
static { Configuration HBASE_CONFIG = new Configuration(); HBASE_CONFIG.set(“hbase.zookeeper.quorum”,“192.168.2.6”); HBASE_CONFIG.set(“hbase.zookeeper.property.clientPort”,“2181”); cfg = new HBaseConfiguration(HBASE_CONFIG); } HTablePool pool = new HTablePool(); HTableInterface prodTable = pool.getTable(“prodFocus”); Scan a = new Scan(); a.addFamily(Bytes.toBytes(“degreeInfo”)); a.setStartRow(Bytes.toBytes(“QQ121102645”)); ResultScanner results = prodTable.getScanner(a); List<KeyValue> list = result.list(); List<String> followGamess = new ArrayList<String>(); for(Result r:results){ KeyValue kv = iter.next();; String game =kv.get(1]; followGames.add(user); } |
代码解释:首先通过Configuration设置HbaseMaster主机及客户端连接端口,然后使用HtableInterface接口示例连接上prodFocus表,因为prodFocus表rowkey设计为用户ID+$+当天的时间戳,因此我们创建以用户“QQ121102645”为检索前缀的Scan扫描,扫描返回的ResultScanner即为该用户相关的所有行数据,遍历每行的“degreeInfo”列簇中的各个列即可获得该用户所有关注(玩过)的手游产品。
关于HbaseAPI操作的代码这里不再详述,感兴趣的读者可以查阅HbaseSDK,熟悉操作HBase表及put、scan、delete代码。
第二个问题“QQ121102645用户是否玩过3CountryBattle(三国3)手游”的业务跟第一个类似,客户端代码可以用Scan找出行健为QQ121102645前缀的所有行,返回的result集合可以创建一个数组列表,遍历这个列表检查3CountryBattles手游是否作为列名存在,即可判断该用户是否关注某一手游,相应代码与上文问题1的代码类似:
清单3.客户端判断用户是否关注某一手游
HTablePool pool = new HTablePool(); HTableInterface prodTable = pool.getTable(“prodFocus”); Scan a = new Scan(); a.addFamily(Bytes.toBytes(“degreeInfo”)); a.setStartRow(Bytes.toBytes(“QQ121102645”)); ResultScanner results = prodTable.getScanner(a); List<Integer> degrees = new ArrayList<Integer>(); List<KeyValue> list = results.list(); Iterator<KeyValue> iter = list.iterator(); String gameNm =“3CountryBattle”; while(iter.hasNext()){ KeyValue kv = iter.next(); if(gameNm.equals(Bytes.toString(kv.getKey()))){ return true; } } prodTable.close(); |
代码解释:同样通过扫描前缀为“QQ121102645”的Scan执行表检索操作,返回的List<keyValue>数组中每一Key-value是degreeInfo列簇中每一列的键值对,即用户关注(玩过)的手游产品信息,判断其Key值是否包含“3CountryBattle”的游戏名信息即可知道该用户是否关注该手游产品。
看起来这个表设计是简单实用的,但是如果我们接着看第三个和第四个业务问题“哪些用户关注了DTLegend(刀塔传奇)?”“3CountryBattle(三国3)手游被关注过吗?”
如你所看到的,现有的表设计对于多个手游产品是放在列簇的多个列字段中的,因此当某一用户对产品的喜好趋于多样化的时候(productkey-value键值对会很多,意味着某一rowkey的表列簇会变长,这本身也不是大问题,但它影响到了客户端读取的代码模式,会让客户端应用代码变得很复杂。
同时,对于第三和第四问题而言,每增加一种手游关注的key-value键值,客户端代码必须要先读出该用户的row行,再遍历所有行列簇中的每一个列字段。从上文Hbase索引的原理及内部检索的机制我们知道,行健是所有Hbase索引的决定性因素,如果不知道行健,就需要把扫描限定在若干HFile数据块中,更麻烦的是,如果数据还没有从HDFS读到数据块缓存,从硬盘读取HFile的开销更大,从上文Hbase检索的时间复杂度分析来看,现在的Hbase表设计模式下需要在Region中检索每一列,效率是列的个数*O(max(elb),从理论上已经是最复杂的数据检索过程。
对关注该平台业务的客户公司角度考虑,第三个第四个的业务问题更加关注客户端获取分析结果的实时分析的性能,因此从设计模式上应该设计更长的行健,更短的列簇字段,提高Hbase行健的检索效率并同时减少访问宽行的开销。
宽表设计
Hbase设计模式的简单和灵活允许您做出各种优化,不需要做很多的工作就可以大大简化客户端代码,并且使检索的性能获得显著提升。我们现在来看看prodFocus表的另一种设计模式,之前的表设计是一种宽表(widetable)模式,即一行包括很多列。每一列代表某一手游的热度。同样的信息可以用高表(talltable)形式存储,新的高表形式设计的产品关注度表结构如表2所示。
表2.prodFocusV2表示例
rowkey:被关注产品$某用户 |
cf(列簇,按日期时间戳的关注度数据) |
3DARPG$QQ121102645 |
20141224:6 |
DTLegend$QQ121102645 |
20141216:1 |
DTLegend$WeiXin_295y603765de8 |
20141212:3 |
3CountryBattle$ChaoChenYY |
20141214:2 |
Frus3D$QQ5863976645 |
20150906:2 |
SpaceHunter:&HeXaoYang |
20140907:1 |
3CountryBattle$HeXiaoYang |
20140907:2 |
Frus3D$HexiaoYang |
20140907:1 |
DTLegend$FengKe_Tony |
20150216:1 |
Frus3D$junping_Jeff |
20141204:2 |
ForgetXian$XiaoFenXia |
20150716:3 |
表解释:将产品在某一天被某用户关注的关联关系设计到rowkey中,而其关注度数据只用一个key-value来存储,行健Daqier_weixin1398765386465串联了两个值,产品名和用户的帐号,这样原来表设计中某一用户在某天的信息被转换为一个“产品-关注的用户”的关系,这是典型的高表设计。
HFile中的keyvalue对象存储列簇名字。使用短的列簇名字在减少硬盘和网络IO方面很有帮助。这种优化方式也可以应用到行健,列名,甚至单元。紧凑的rowkey存储业务数据意味应用程序检索时,IO负载方面会大大降低。这种新设计在回答之前业务关心的“哪些用户关注了XXXX产品?”或者“XXXX产品被关注过吗?”这类问题时,就可以基于行健使用get()直接得到答案,列簇中只有一个单元,所以不会有第一种设计中多个keyvalue遍历的问题,在Hbase中访问呢驻留在BlockCache离得一个窄行是最快的读操作。从IO方面来看,扫描这些行在一个宽行上执行get命令然后遍历所有单元相比,从RegionServer读取的数据量是相同的,但索引访问效率明显大大提高了
例如要分析“3CountryBattles(三国群雄)手游是否被QQ121102645用户关注?”时,客户端代码示例如下:
清单4.客户端判断某一手游产品是否被关注
HTablePool pool = new HTablePool(); HTableInterface prodTable = pool.getTable(“prodFocusV2”); String userNm =“QQ121102645”; String gameNm =“3CountryBattles”; Get g = new Get(Bytes.toBytes(userNm+”$”+gameNm)); g.addFamily(Bytes.toBytes(“degreeInfo”)); Result r = prodTable.get(g); if(!r.isEmpty()){ return true; } table.close();
|
代码解释:由于prodFocusV2的rowkey设计改为被关注产品$用户Id的高表模式,手游产品及用户信息直接存放在行健中,因此代码以手游产品名“3CountryBattles”+“$”+用户帐号“QQ121102645”的Byte数据作为Get键值,在表上直接执行Get操作,判断返回的Result结果集是否为空即可知道该手游产品是否被用户关注。
我们使用压力测试来检验一下两种Hbase表设计模式下的并发访问性能的对比,在百万级及千万级行数据条件下,采用宽表和高表的两种设计模式下,在进行”关注3CountryBattles手游的用户”查询,取得result检索结果的相应时间如下表所示:
表3.高表vs宽表检索性能对比
宽表设计模式 (prodFocsV1) |
高表设计模式 (prodFocsV2) |
|
5百万行数据 |
0.237s |
0.079s |
1千万行数据 |
0.418s |
0.112s |
2千万行数据 |
0.83s |
0.283s |
可以看到在客户关心的产品关注度维度上,高表的性能比宽表要高出50%以上,这是rowkey和列簇的设计影响到Hbase索引检索在Hbase设计模式中成功运用的表现。掌握Hbase数据存储机制及内部检索工作机制之所以重要,很大一部分原因就在于运用该机制是提升性能的机会。
当然还有一些其他优化技巧。你可以使用MD5值做为行健,这样可以得到定长的rowkey。使用散列键还有其他好处,你可以在行健中使用MD5去掉“$”分隔符,这会带来两个好处:一是行键都是统一长度的,可以帮助你更好的预测读写性能。第二个好处是,不再需要分隔符后,scan的操作代码更容易定义起始和停止键值。这样的话你使用基于用户+手游名的MD5散列值来设定Scan扫描紧邻的起始行(startRow和stopRow)就可以找到该手游受关注的最新的热度信息。
使用散列键也会有助于数据更均匀的分布在region上。如该案例中,如果客户的关注度是正常的(即每天都有不同的客户玩不同的游戏),那数据的分布不是问题,但有可能某些客户的关注度是天生倾斜的(即某用户就是喜欢某一两个产品,每天热度都在这一两个产品上),那就会是一个问题,你会遇到负载没有分摊在整个Hbase集群上而是集中在某一个热点的region上,这几个region会成为整体性能的瓶颈,而如果对Daqier_weixin1398765386465模式做MD5计算并把结果作为行键,你会在所有region上实现一个均匀的分布。
使用MD5散列prodFocusV2表后的表示例如下:
表4.rowkeyMD5表示例
rowkey:MD5(被关注产品$某用户) |
cf(列簇,按日期时间戳的关注度数据) |
3b2c85f43d6410d6 |
20141224:6 |
82c85c2cdf16dcee |
20141216:1 |
8480986fd88c1a39 |
20141212:3 |
3671c0efbe01ae88 |
20141214:2 |
baf933cac7dd2814 |
12141109:2 |
65ae48cfaae57972 |
20140907:1 |
732106051f4a2ef8 |
20140907:1 |
f3b59010d3f8fb2d |
20140907:2 |
402480df0adfbcf9 |
20150216:1 |
9171607fa5190507 |
20141204:2 |
296be556a86dd505 |
20150716:3 |
本文介绍了Hadoop大数据平台下的nonsql的典型平台-Hbase的整体架构及基本原理,分析了在Hbase物理模型和检索工作机制下Hbase表的设计模式。并以一个实际手游公司的客户案例,描述了设计Hbase表时针对客户访问模式及性能需求时的技巧,通过不同设计模式下代码实现和测试对比进行了最佳实践参考案例的详解。
<<<--------------------->>>
B2B2C电子商务与技术及企业管理!
以专业和分享为理念,关注电商、大数据、云计算、技术管理和企业管理!
致力于成为电商与管理领域的观察者、思考者和创新者。
◆手机微信扫描二维码订阅
以上是关于Hbase 设计与开发实战的主要内容,如果未能解决你的问题,请参考以下文章
分布式爬虫系统设计实现与实战:爬取京东苏宁易购全网手机商品数据+MySQLHBase存储