概念视图与物理视图
BigTable论文和HBase官网上的示例
概念视图
Row Key | Time Stamp | ColumnFamily contents | ColumnFamily anchor |
---|---|---|---|
"com.cnn.www" | t1 | anchor:cnnsi.com = "CNN" | |
"com.cnn.www" | t2 | anchor:my.look.ca = "CNN.com" | |
"com.cnn.www" | t3 | contents:html = "..." | |
"com.cnn.www" | t4 | contents:html = "..." | |
"com.cnn.www" | t5 | contents:html = "..." |
物理视图
ColumnFamily anchor
Row Key | Time Stamp | Column Family anchor |
---|---|---|
"com.cnn.www" | t1 | anchor:cnnsi.com = "CNN" |
"com.cnn.www" | t2 | anchor:my.look.ca = "CNN.com" |
ColumnFamily contents
Row Key | Time Stamp | Column Family contents |
---|---|---|
"com.cnn.www" | t3 | contents:html = "..." |
"com.cnn.www" | t4 | contents:html = "..." |
"com.cnn.www" | t5 | contents:html = "..." |
- 概念视图中空白cell在物理上是不存储的
- rowkey和column family在实际存储中确实会重复存储
- 想象一下把传统数据库中每列变成“列名:值”,然后这些列全部存入只有一列的表中
Row Key行键设计
Rowkey可以是任意字符串(最大长度是64KB,实际应用中长度一般为10-100bytes),在HBase内部,rowkey保存为字节数组。存储时,数据按照rowkey的字典序(byte order)排序存储。设计rowkey时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。byte order意味着,对于1-20这样的数据,排列出来的结果为:1,10,11...19,2,20,3,4..9。所以rowkey的长度最好是一致的,长度不够以0或某些字符左补齐。
传统关系型数据库的主键通常有业务主键和逻辑主键之分,关于两者谁更优秀的争论至今未止。而在HBase或者说NoSql中,rowkey的设计通常更像业务主键。rowkey的设计至关重要,若前期rowkey设计不合理,在开发阶段往往会发现想要获取数据进行分析会变得异常困难,在传统数据库向NoSQL数据库转移阶段这个问题会显得尤其明显。
rowkey不要设计成单调递增数据或时间戳数据:在Tom White的《Hadoop: The Definitive Guide》一书中,有一个章节描述了一个值得注意的问题:在一个集群中,一个导入数据的进程一动不动,所有的client都在等待一个region,过了一会后,变成了下一个region。如果使用了单调递增或者时序的key就会造成这样的问题。使用了顺序的key会将本没有顺序的数据变得有顺序,把负载压在一台机器上。所以要尽量避免时间戳或者(e.g. 1, 2, 3)这样的key。
目前我所遇到的业务中,rowkey通常采用K+时间戳(14位)+传统业务主键的形式(12位+),注意,短键对提高访问(scan/get)速度没有帮助,所以不用纠结行键长度过长,当然,无意义的增加行键的长度是不可取的。其中K=集群大小*每台机器的CPU核心数。例如8个tasktracker,每台机器4核心,则rowkey=[00-32随机数]+时间戳(14位)+传统业务主键。这么处理的好处:
- 建表的时候就可以预先创建多个region,可有效防止热点问题
- 导入数据时主键前两位随机生成,可有效的将数据分散开来
- 理想情况下是每个核心数启动N个Map,而每个region又对应着一个Map,所以region若能平均分布在每台机器上能使集群效率最大
- 按时间处理数据时可以通过RegexStringComparator来过滤数据
- 当数据量十分大的时候,可以将前面的随机数乘以N,N>=1
当然,rowkey的设计没有最佳实践,符合业务需求的才是最好的。rowkey往往包含了很多信息,这些信息为选择数据而服务。
列族和列的设计
同一个列族在HBase里面会存放在同一个HStore中,所以尽量将IO行为相同的列放在同一个列族中。在HBase的存储文件HFile中,有一个索引用来方便值的随机访问,但是访问一个单元的坐标要是太大的话,会占用很大的内存,这个索引会被用尽。所以要想解决,可以设置一个更大的块大小,当然也可以使用更小的列族名和列名。
- 列族名:尽量短小,最好一个字符
- 列名:保证易读的前提下尽量短小
现在HBase并不能很好的处理两个或者三个以上的列族,所以尽量让你的列族数量少一些。目前,flush和compaction操作是针对一个Region。所以当一个列族操作大量数据的时候会引发一个flush。那些不相关的列族也有进行flush操作,尽管他们没有操作多少数据。Compaction操作现在是根据一个列族下的全部文件的数量触发的,而不是根据文件大小触发的。当很多的列族在flush和compaction时,会造成很多没用的I/O负载,要想解决这个问题,需要将flush和compaction操作只针对一个列族。
尽量在你的应用中使用一个列族。只有你的所有查询操作只访问一个列族的时候,可以引入第二个和第三个列族。例如,你有两个列族,但你查询的时候总是访问其中的一个,从来不会两个一起访问。
表关联
HBase是否支持表关联,答案是不支持。那么如何才能做到传统关系型数据库中的外键、一对多等功能?目前来说有两种方式:
- 自己写MR程序进行处理
- 通过重新设计表结构来实现
自己写MR就不细说了,方法有很多,值得研究的内容也很多,如nested loops vs hash-joins。通过设计表结构应该算是比较合适的方式。先看下图,传统关系型数据库的一对多:
HBase中则应该如下图所示,物理视图:
RowKey | Time Stamp | ColumnFamily "info" |
---|---|---|
电话1 | t1 | info:入网日期=".." |
电话1 | t1 | info:用户名=".." |
电话1 | t1 | info:性别=".." |
电话1 | t1 | info:...=".." |
RowKey | Time Stamp | ColumnFamily "records" |
---|---|---|
电话1 | t1 | r:通话开始时间00001=".." |
电话1 | t1 | r:通话时长00001=".." |
电话1 | t1 | r:通话号码00001=".." |
电话1 | t1 | r:通话开始时间00002=".." |
电话1 | t1 | r:通话时长00002=".." |
电话1 | t1 | r:通话号码00002=".." |
逻辑结构:
RowKey | Time Stamp | ColumnFamily "info" | ColumnFamily "r" | ||||||
---|---|---|---|---|---|---|---|---|---|
info:入网日期 | info:用户名 | info:性别 | info:… | r:通话开始时间00001 | r:... | r:通话开始时间00002 | r:... | ||
电话1 | t1 | .. | .. | .. | .. | .. | .. | .. | .. |
传统关系型数据库在一对多的"多"端纵向扩展来增加数据,HBase则通过横向扩展来增加"多"端数据。对于上面的例子,有多少的通话记录则扩展多少列。如此结构的表,可以轻松满足一对多的关系,其他的关系也可以类似处理。这样的做的一个可能好处就是,使用
最后,关于这个问题的处理方式和朋友讨论过几次,目前来说感觉这样处理可能是比较合适的。抛砖引玉,希望能有更佳的处理方式。
你好,请问我现在碰到这么一种场景
一份全量 用户数据 格式 简单描述为
userid1_k1_20160616 -> value
userid1_k2_20160616 -> value
userid1_k3_20160616 -> value
userid1 下有很多中不同类型数据 k1 k2 k3
现在这份数据 存入到了 hbase 中,有一个问题是,假设某一天的数据出了问题,需要重新跑一份
但是新的数据,并不一定是 k1 k2 k3 都有数据 可能 只有 k1 k2 有数据,k3没有数据
这样的话,通过bulkload 将数据载入,查询的时候 查 userid1 这样 k3 也会被查出来,一个个删不太现实
没有想到一个很好的方法, 有时间的话 麻烦帮忙看下有什么好的设计方法。
hoaxthis story was told with different characters , already - several tiems.There never had been an interview with this Australian General in real ;) - although it COULD have happened ^^