logo资料库

HBase多表关联查找资料.docx

第1页 / 共11页
第2页 / 共11页
第3页 / 共11页
第4页 / 共11页
第5页 / 共11页
第6页 / 共11页
第7页 / 共11页
第8页 / 共11页
资料共11页,剩余部分请下载后查看
HBase表设计及数据导入的一些思考
压缩算法的比较
BigTable和HBase中压缩算法的选择
实际项目中的实践经验
总结的话
hbase 表结构设计研究 因为一直在做 hbase 的应用层面的开发,所以体会的比较深的一点是 hbase 的表结构 设计会对系统的性能以及开销上造成很大的区别,本篇文章先按照 hbase 表中的 rowkey、 columnfamily、column、timestamp 几个方面进行一些分析。最后结合分析如何设计一种适 合应用的高效表结构。 1、表的属性 (1)最大版本数:通常是 3,如果对于更新比较频繁的应用完全可以设置为 1,能够快 速的淘汰无用数据,对于节省存储空间和提高查询速度有效果。不过这类需求在海量数据领 域比较小众。 (2)压缩算法:可以尝试一下最新出炉的 snappy 算法,相对 lzo 来说,压缩率接近, 压缩效率稍高,解压效率高很多。 (3)inmemory:表在内存中存放,一直会被忽略的属性。如果完全将数据存放在内存中, 那么 hbase 和现在流行的内存数据库 memorycached 和 redis 性能差距有多少,尚待实测。 (4)bloomfilter:根据应用来定,看需要精确到 rowkey 还是 column。不过这里需要理 解一下原理,bloomfilter 的作用是对一个 region 下查找记录所在的 hfile 有用。即如果一个 region 下的 hfile 数量很多,bloomfilter 的作用越明显。适合那种 compaction 赶不上 flush 速度的应用。 2、rowkey rowkey 是 hbase 的 key-value 存储中的 key,通常使用用户要查询的字段作为 rowkey, 查询结果作为 value。可以通过设计满足几种不同的查询需求。 (1)数字 rowkey 的从大到小排序:原生 hbase 只支持从小到大的排序,这样就对于排 行榜一类的查询需求很尴尬。那么采用 rowkey = Integer.MAX_VALUE-rowkey 的方式将 rowkey 进行转换,最大的变最小,最小的变最大。在应用层再转回来即可完成排序需求。 (2)rowkey 的散列原则:如果 rowkey 是类似时间戳的方式递增的生成,建议不要使用 正序直接写入 rowkey,而是采用 reverse 的方式反转 rowkey,使得 rowkey 大致均衡分布, 这样设计有个好处是能将 regionserver 的负载均衡,否则容易产生所有新数据都在一个 regionserver 上堆积的现象,这一点还可以结合 table 的预切分一起设计。
3、columnfamily columnfamily 尽量少,原因是过多的 columnfamily 之间会互相影响。 4、column 对于 column 需要扩展的应用,column 可以按普通的方式设计,但是对于列相对固定 的应用,最好采用将一行记录封装到一个 column 中的方式,这样能够节省存储空间。封装 的方式推荐 protocolbuffer。 以下会分场景介绍一些特殊的表结构设计方法,只是一些摸索,欢迎讨论: value 数目过多场景下的表结构设计: 目前我碰到了一种 key-value 的数据结构,某一个 key 下面包含的 column 很多,以 致于客户端查询的时候 oom,bulkload 写入的时候 oom,regionsplit 的时候失败这三种后 果。通常来讲,hbase 的 column 数目不要超过百万这个数量级。在官方的说明和我实际的 测试中都验证了这一点。 有两种思路可以参考,第一种是单独处理这些特殊的 rowkey,第二种如下: 可以考虑将 column 设计到 rowkey 的方法解决。例如原来的 rowkey 是 uid1,,column 是 uid2,uid3...。重新设计之后 rowkey 为~~...当然大家会有 疑问,这种方式如何查询,如果要查询 uid1 下面的所有 uid 怎么办。这里说明一下 hbase 并不是只有 get 一种随机读取的方法。而是含有 scan(startkey,endkey)的扫描方法,而这种 方法和 get 的效率相当。需要取得 uid1 下的记录只需要 new Scan("uid1~","uid1~~")即可。 这里的设计灵感来自于 hadoop world 大会上的一篇文章,这篇文章本身也很棒,推 荐大家看一下 http://www.cloudera.com/resource/hadoop-world-2011-presentation-slides-advanced-hba se-schema-design/
HBase 一对多关系的表结构设计 前面刚开始使用 HBase 只是用于存取某些简单的 JAVA 对象或是简单数据,所以一般设置 列族和列标示时只用一个就行了。 最近有个任务是把系统中的站内消息移到 HBase 当中去,才开始查 HBase 中的一对多 关系,发现网上的资料讲的都不甚详尽,这篇 blog 记录一下我的设计和想法,这些想法毕 竟未经证实,尚需验证。如果有大虾认为有不妥甚至错误的地方请不吝指教。 首先讲两个我参考的资料,背景:一个主贴和 N 个回帖的一对多关系,学过一点数据库 的应该都能体会到,图我就不画了: 1.官方推荐资料: http://wiki.apache.org/hadoop/Hbase/DataModel 2.一位大大的简单 HBase 一对多表结构的介绍(感觉实际上他参考了资料 1,不过讲的不太。。 合理,而且下面列表的那个 comment_title 应该是写错了,一对多的那个例子貌似也让人很 不解): http://doudouclever.blog.163.com/blog/static/17511231020127893233972/ 最终的解决方案是这个表(按照官方资料): Table Row Key BlogTable ID Family Attributes(ColumnKeys/Qualifiers) info: text: comment title: comment author: comment text: Author,Title,URL No ColumnKey,3version Column keys are written like YYYMMDDHHmmss. Should be IN-MEMORY and have a 1 version Same keys. 1 Version Same keys. 1 Version 因为刚开始看的是第二个资料,官方资料也没细看,导致理解偏差了。一直想明白这个一对 多是怎么设计的,其实了解以下两个知识点就可以了: 1.HBase 的二维表结构:三个重要概念是 Column Family(以下简称为 CF)和 Column Key/Qualifier(以下简称为 CK)还有 RowKey。一个 CF 可以包含若干个 CK。相当于 CF 是个合并单元格;CK 才是具体的列标示,并且可以为空。Rowkey 就是行标示,可以理解 为主键。如下图所示:
2.Hbase 中,对于某个 Column Family 中的 Column Key 是可以动态增加的 存储于关系型数据库中的数据如下,简单起见某些字段删减了: 表头 ID Author Title Body 1 张三 消息头 这是内容 Hello World! 明细表: ID HeadID CommentAuthor Title Body 1 2 1 1 李四 王五 回复头 1 这是回复内容 1 回复头 2 这是回复内容 2 转移到 Hbase 中存储,需要把以前的明细“纵向延伸”(对于同一表头,明细表一条一条向 下加数据),转变为 HBase 的“横向延伸”(对同一 RowKey,添加明细的 ColumnKey),Hbase 中存储的数据如下,iteye 的表不会弄合并单元格,所以用 excel 截图来展示吧: 结论:从图中可以看出,HBase 是把以前关系数据库明细表的字段作为 ColumnFamily, 而明细表的主键作为 ColumnKey 的这种结构来达到一对多的效果的。关系型数据库,明 细增多时是纵向添加数据;对于 Hbase,则是通过 ColumnKey 的增加来添加数据 由此可能产生的问题:  1.HBase 官方不推荐多 Column Family,超过 3 个是妥妥儿不推荐的,原文见 http://hbase.apache.org/book/number.of.cfs.html 可是一对多的这种关系是必须用多 Column Family 的,这点矛盾让我到现在还很不解。。
  2.RowKey 的存储问题,传统数据库主键一般都是递增的方式生成的一批证书值,但是 Hbase 采用这种方式做为 RowKey 的话会导致 regionserver 负载过高的问题,所以 RowKey 的生成方式需要再讨论。 3.这种一对多的方式,如果回复很多很多,比如贴吧随便一个帖子就是上 W 回复的, 会导致 ColumnKey 变得很多,也就是说 Hbase 表会变得很宽=。= 尽管看过帖子说 HBase 并不是传统意义的二维结构,就是不会单独为某个 Cell 为空的 区域留出空间存储数据(这里我可能理解和描述的都不太贴切), 总之这种“宽”的表结构,是 对传统数据库表结构意识形态的一种冲击,不知道会不会有问题。。。
HBase 表设计及数据导入的一些思考 最近公司业务要用到 HBase,做了一些总结 求拍砖 1:查询需求 查询关键字:groupid,guid,onlycode,date。 (1) guid 分为 guid1 和 guid2,二者同时存在于日志数据 中,且需求要求只要有一个值满足条件,该行记录就应该被提取出来。 等价于 RDBMS 中的 OR 原则。 (2) guid 与 onlycode 是互斥查询,只能取其一,其余查询 条件是必选项。 2:设计原则 由于数据量大(每天 10 亿+),查询需求固定,故采用 nosql 数据库 HBase。 要求 key 直接命中。 由于存在 OR 的特殊需求和互斥查询,故采用了数据冗余机制和标志区 分。具体行关键字如下所示: 1_groupid_guid_date:针对 guid 的查询,由于存在 OR 的关系,故原 始数据中一行对应到 HBase 表中的两行,冗余了一份记录。 2_groupid_onlycode_date:针对 onlycode 的查询,冗余了一份记录。 以上设计可以满足需求,但存在以下问题 (1) 数据冗余了多份,从原始数据导入 HBase 的数据成倍 增加,导入时间增长。
(2) 不易扩展,如果增加新的查询条件,数据就得重新冗 余一份或者建立二级索引,表结构不整齐。 3:改进后的策略 原则:原始数据只存储一份,采用二级索引并且让数据和索引容易区分 又不用分表 具体行关键字如下所示: 数据 key 为:D-文件 split+incr,value 为 data 索引 key 为:I-1_groupid_guid_date 或者 2_groupid_onlycode_date, value 为数据 key 中的 key。 各个 key 都做了 hash,下面导入数据部分详述。 数据 key 是唯一的,对应唯一的一行数据。 索引 key 则对应 value 版本。 这样设计之后,数据以 D 开头,索引以 I 开头,分别占据同一张表的上 半区和下半区,不会混杂在一起,且以后新增查询关键字,对于历史数 据只需重新遍历建立索引(这一步是不可避免的),新增数据直接录入 即可,仍然保证数据只有一份。 4:数据导入优化 通常采用 bulkload 方式导入数据,其时间主要浪费在 HFile 的生成上, 后边的 mv 过程并不占用时间。 生成 HFile 的采用的优化主要有以下几点: (1) 压缩:
HFileOutputFormat 需要设置压缩,map 的 output 需要设置压缩。一方 面减少了 IO,另一方便也减少了网络传输(带宽对我们很珍贵)。 (2) 重写 Reduce 默认的 Reduce 过程有个 Treeset 进行排序。这是个内存排序过程必须 从实现过程中剔除。因为在我们的业务里,rowkey 相同的记录有很多, 没法直接实现内存排序(Reduce 端的 JVM 默认内存 200M)。其次从业 务角度考虑,这个过程也可以省去。原因如下:Treeset 是按照 kv 排序 的(行关键字、列族、列名),由于我们只设计了一个列族(没必要多 个),一个列(没必要多个,这个列包含了记录的详细信息,业务语义 不详述了)。因此 map 执行完之后就是有序的,进入 reduce 端的行关 键字都一样,对应的列族、列名都一样,只需要控制住时间戳,reduce 端无需再排序,就可以实现输出结果的有序性。我们并不关心同一个 rowkey 下不同版本 value 的顺序。因此直接将结果写出即可。 (3) 预分区+hash 建表需要预分区,增加 Reduce 的数目。如果不采用预分区,则空表只 有一个分区,默认只启动一个 reduce。如何分区合理,需根据具体业务。 通常都需要做 hash 操作,可以让数据更加均衡,某些情况下还能节省 存储空间。Hash 的合理性要和压缩相结合,使得分区均衡且容易压缩。 我们的业务中,并没有对整个 Key 字段都做 hash,而是排除了 date 字 段,考虑到业务场景是离线数据批量导入,date 是相同的。 (4) 具体参数设置参考网上的博客 关闭 split,手动 compact 和 balance 等
分享到:
收藏