一个列族的所有列在硬盘上存放在一起,使用这个特性可以把不同访问模式的列放在不同列族,以便隔离它们。这也是HBase被称为面向列族的存储(column-family-oriented store)的原因。
1、RowKey设计
在设计HBase表时,行键是唯一重要的事情,应该基于预期的访问模式来为行键建模。
行键决定了访问HBase表时可以得到的性能。这个结论根植于两个事实:region基于行键为一个区间的行提供服务,并且负责区间内每一行;HFile在硬盘上存储有序的行。当region刷写留在内存里的行时生成了HFile。这些行已经排过序,也会有序地刷写到硬盘上。HBase表的有序特性和底层存储格式可以让你根据如何设计行键以及把什么放入列限定符来推理其性能表现。 有效的行键设计不仅要考虑把什么放入行键中,而且要考虑它们在行键里的位置。 信息在行键里的位置和选择放入什么信息同等重要。 HBase中的数据是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度组合可以对HBase中的数据进行快速定位。RowKey长度控制
RowKey是一个二进制字节数组,可以是任意字符串,最大长度 64kb ,实际应用中一般为10-100bytes,以byte[] 形式保存,一般设计成定长。建议越短越好,不要超过16个字节。因为Hbase按照column family列族组织存储,每个列族存储时都包含RowKey,防止RowKey本身占用过多空间,64位OS,内存8字节对齐,控制在16个字节,8字节的整数倍利用了OS的最佳特性。
RowKey唯一原则
必须在设计上保证其唯一性。由于在HBase中数据存储是Key-Value形式,若HBase中同一表插入相同Rowkey,则原先的数据会被覆盖掉(如果表的version设置为1的话),所以务必保证Rowkey的唯一性。
Rowkey的排序原则
HBase的Rowkey是按照ASCII有序设计的,在设计Rowkey时要充分利用这点。通常不将有序的信息放置在RowKey的高位,防止出现热点问题。
Rowkey散列原则
实际应用中需要将数据均衡分布在每个RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。
Region热点问题
HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。 热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。 设计良好的数据访问模式以使集群被充分,均衡的利用。
2、避免热点问题的策略
Reverse反转
针对固定长度的Rowkey反转后存储,这样可以使Rowkey中经常改变的部分放在最前面,可以有效的随机Rowkey。如手机号反转,就避免了以手机号那样比较固定开头(137x、15x等)导致热点问题;为放置时间戳导致热点问题,可以将时间反转Long.Max_Value - timestamp 追加到key的末尾。但这样做的缺点是牺牲了Rowkey的有序性。
Salt加盐
Salting是将每一个Rowkey加一个前缀,前缀使用一些随机字符,使得数据分散在多个不同的Region,达到Region负载均衡的目标。Salt增加了写操作的吞吐量,不过缺点是同时增加了读操作的开销。
Hash散列或者Mod
用Hash散列来替代随机Salt前缀的好处是能让一个给定的行有相同的前缀,这在分散了Region负载的同时,使读操作也能够推断。确定性Hash(比如md5后取前4位做前缀)能让客户端重建完整的RowKey,可以使用get操作直接get想要的行。如果Rowkey是数字类型的,也可以考虑Mod方法。
3、Column Family
ColumnFamily尽量少,原因是过多的columnfamily之间会互相影响。
4、column
对于column需要扩展的应用,column可以按普通的方式设计,但是对于列相对固定的应用,最好采用将一行记录封装到一个column中的方式,这样能够节省存储空间。
5、Hbase Schema设计的最佳实践汇总
- 在HBase中,Value永远和它的Key一起传输。如果你的RowKey和列名很大,会占据大量的存储空间传输IO压力,同时影响Hbase的缓存和整体性能。列族尽可能越短越好,最好是一个字符,冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好。
- HBase没有跨行事务的概念,在单个API调用里而不是多个API调用里完成访问模式。HBase不支持跨行事务,要避免在客户端代码里维护这种复杂的逻辑。
- 通过合理hbase 行键(rowkey)设计实现快速的多条件查询,所采用的方法将所有要用于查询中的列经过一些处理后存储在rowkey中,查询时通过rowkey进行查询,提高rowkey的利用率,加快查询速度。
- 嵌套的实体是从关系型映射到非关系型的又一个工具。如果你得到子实体的唯一方法是通过父实体,并且你希望在一个父实体的所有子实体上有事务级保护,这种技术是最正确的选择。
- 在列限定符和时间戳上建立索引,可以让你在一行上不用扫描前面所有的列而直接跳到正确的列。
- 在同一列族里存储相似访问模式的所有数据。
- 列限定符可以用来存储数据,就像单元一样。