假如我们有这样一个场景:某个歌曲表(10亿条记录)的主键为id+ts,其中id代表歌曲ID是INT类型,ts是一个时间戳类型。用户查询有以下几种情况,
1. 查询某个id的详细信息(WHERE id=”)
2. 查询某个id在某个时间范围的详细信息(WHERE id=” and ts between and )
3. 统计天级别的id个数(WHERE ts between and )
在不创建额外索引的情况下,我们一般的思路可能是使用以下建表语句,
create table song(
id int not null,
ts timestamp not null
)
primary key (id,ts)
salt using 10 partitions on ...
以上建表语句会使数据按照在id,ts的组合上进行hash分区,分布在10个partition上面。这样的建表语句对于上述3种场景是否适合呢?
对于1 —适合,因为id在前面,可以通过key扫描很快定位到具体某个key的数据
对于2 —适合,因为id在前面ts在后面,通过id就能过滤到大批数据,再结合ts过滤条件可以更快缩小数据范围
对于3 —不适合,因为ts在主键的第二个字段,而且id的UEC很高,这样在查询ts的时候性能会很差
如何在不影响1和2的性能的情况下还能提升3的性能呢?有两种方法:
1. 在ts上创建索引
2. 使用DIVISION BY多温度特性
对于上述第一种方式,由于Trafodion中的索引是以一个独立的HBase表来维护的,创建索引无疑会导致表数据加载性能下降。因此这里我们使用第二种方式,即”DIVISION BY”。”DIVISION BY”一般适用于这样的场景:
1. 表的数据与时间相关,表中有timetamp字段且timestamp字段在主键列中
2. timestamp字段由于UEC很高,一般不作为Leading Key(Key的第一位)
3. 主键除了timestamp还有其他字段如ID,尤其ID的UEC也很高,查询在只有timestamp条件时性能较差
为解决上述问题,使用”DIVISION BY”会在主键列的”SALT“后面添加一个隐藏列”DIVISION_1“,这个值可以是timestamp的一部分,如精确到天级别(date_trunc(‘DAY’,ts))。添加”DIVISION BY”子句后的建表语句基本如下,
create table song(
id int not null,
ts timestamp not null
)
primary key (id,ts)
salt using 10 partitions on ...
division by date_trunc('DAY',ts)
上述语句生成表的主键组成为”SALT“,”DIVISION_1“,ID,TS。拿上述建表语句为例,date_trunc(‘DAY’,ts)的结果是天,这意味着假如表中总共有一年的数据,那么”DIVISION_1“的UEC为365,因此对于上述场景3,按照MDAM查询原理,查询会变成WHERE “DIVISION_1” between and ,而”DIVISION_1“前面的字段为 “SALT“(UEC取决于分区数,所以不会很高),因此会很快定位到此范围的数据。