12.10 模式设计
通过HBase与RDBMS的比较,可以了解到二者无论是在物理视图、逻辑视图还是具体操作上都存在很大的区别。例如,HBase中没有Join的概念。但是,大表的结构可以使其不需要Join操作就能解决Join操作所解决的问题。比如,在一条行记录加上一个特定的行关键字,便可以实现把所有关于Join的数据合并在一起。另外,Row Key的设计也非常关键。以天气数据存储为例。假如将监测站的值作为Row Key的前缀,那么天气数据将以监测站聚簇存放。同时将倒序的时间作为监测站的后缀,那么同一监测站的数据将从新到旧进行排列。这样的特定存储功能可以满足用户特殊的需要。
一般来说HBase的使用是为了解决或优化某一问题,恰当的模式设计可以使其具有HBase本身所不具有的功能,并且使其执行效率得到成百上千倍的提高。
12.10.1 模式设计应遵循的原则
在进行HBase数据库模式设计的时候,不当的设置可能对系统的性能产生不良的影响。当数据量比较小的时候,表现可能并不明显,但随着数据量的增加这些微小的差别将有可能对系统的性能产生很大的影响。有下面几点需要特别注意。
1.列族的数量及列族的势
我们建议将HBase列族的数量设置得越少越好。当前,对于两个或两个以上的列族HBase并不能处理得很好。这是由于HBase的Flushing(冲洗,即将内存中的数据写入磁盘)和压缩是基于Region的。当一个列族所存储的数据达到Flushing的阈值时,该表中的所有列族将同时进行Flshing操作。这将带来不必要的I/O开销,列族越多,该特性带来的影响越大。对于压缩也是同样的道理。
此外,还要考虑到同一个表中不同列族所存储的记录数量的差别,即列族的势(Cardinality)。当两个列族数量差别过大时将会使包含记录数量较少列族的数据分散在多个Region之上,而Region有可能存储在不同的Regionserver之上。这样,当进行查询或scan操作的时候,系统的效率会受到一定的影响。该影响的大小要视具体的情况而定。
2.行键(Row Key)的设计
首先,应该避免使用时序或单调(递增/递减)行键。因为当数据到来的时候,HBase首先需要根据记录的行键来确定存储的位置,即Region的位置。如果使用时序或单调行键,那么连续到来的数据将会被分配到同一个Region当中,而此时系统中的其他Region/Regionserver将处于空闲状态,这是分布式系统最不希望看到的情况。如果必须存储这种类型的数据,例如时序值,那么该怎么办呢?在OpenTSB中,行键的设计如下所示:
[metric_type][event_timestamp]
上述方法将时序(event_timestamp)作为行键的第二个“字段”,并为行键添加一个前缀。但是,具体选择什么样的规则来创建行键也需要视情况而定,没有万能的规则。
3.尽量最小化行键和列族的大小
在HBase中,一个具体的值由存储该值的行键、对应的列(列族:列)以及该值的时间戳决定。HBase中的索引是为了加速随机访问的速度。该索引的创建是基于“行键+列族:列+时间戳+值”的,如果行键和列族的大小过大,甚至超过值本身的大小,那么将会增加索引的大小。并且,在HBase中数据记录往往非常之多,重复的行键、列将不但使得索引的大小过大,也将加重系统存储的负担。
4.版本的数量
HBase在进行数据存储的时候,新的数据并不会直接覆盖旧的数据,而是进行追加操作,不同的数据通过时间戳进行区分。默认情况下,每行数据存储三个版本,该值可以通过HColumnDescriptor进行设置,建议不要将其设置得过大。
下面我们通过两个例子,让读者对HBase的模式设计有一个初步的认识。