更新时间:2024-05-13 GMT+08:00
分享

Duplicate模型

在某些多维分析场景下,数据既没有主键,也没有聚合需求。因此,我们引入Duplicate数据模型来满足这类需求。

表1 数据

ColumnName

Type

SortKey

Comment

timestamp

DATETIME

Yes

日志时间

type

INT

Yes

日志类型

error_code

INT

Yes

错误码

error_msg

VARCHAR(1024)

No

错误详细信息

op_id

BIGINT

No

负责人 ID

op_time

DATETIME

No

处理时间

建表语句。

CREATE TABLE IF NOT EXISTS example_db.expamle_tbl
(
    `timestamp` DATETIME NOT NULL COMMENT "日志时间",
    `type` INT NOT NULL COMMENT "日志类型",
    `error_code` INT COMMENT "错误码",
    `error_msg` VARCHAR(1024) COMMENT "错误详细信息",
    `op_id` BIGINT COMMENT "负责人id",
    `op_time` DATETIME COMMENT "处理时间"
)
DUPLICATE KEY(`timestamp`, `type`)
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

这种数据模型区别于Aggregate和Unique模型。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。 而在建表语句中指定的DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。(更贴切的名称应该为“Sorted Column”,这里取名“DUPLICATE KEY”只是用以明确表示所用的数据模型。关于“Sorted Column”的更多解释,请参见 前缀索引)。在DUPLICATE KEY的选择上,我们建议适当的选择前2-4列就可以。

这种数据模型适用于既没有聚合需求,又没有主键唯一性约束的原始数据的存储。更多使用场景,可参见聚合模型的局限性。

聚合模型的局限性

  • 这Aggregate模型&Unique模型。

    这里我们针对Aggregate模型(包括Unique模型),来介绍下聚合模型的局限性。

    在聚合模型中,模型对外展现的,是最终聚合后的数据。也就是说,对于任何还未聚合的数据(例如说两个不同导入批次的数据),必须通过某种方式保证对外展示的一致性。

    假设表结构如下:

表2 数据

ColumnName

Type

AggregationType

Comment

user_id

LARGEINT

-

用户 ID

date

DATE

-

数据导入日期

cost

BIGINT

SUM

用户总消费

假设存储引擎中有如下两个已经导入完成的批次的数据。

batch1

表3 数据

user_id

date

cost

10001

2017-11-20

50

10002

2017-11-21

39

batch2

表4 数据

user_id

date

cost

10001

2017-11-20

1

10001

2017-11-21

5

10003

2017-11-22

22

可以看到,用户10001分属在两个导入批次中的数据还没有聚合。但是为了保证用户只能查询到如下最终聚合后的数据。

表5 数据

user_id

date

cost

10001

2017-11-20

51

10001

2017-11-21

5

10002

2017-11-21

39

10003

2017-11-22

22

建数据表。

CREATE TABLE IF NOT EXISTS example_db.expamle_tb2
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据导入日期时间",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费"
)
AGGREGATE KEY(`user_id`, `date`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

插入表3表5的数据。

INSERT INTO example_db.expamle_tb2 (user_id,date,cost) VALUES('10001','2017-11-20','50'),('10002','2017-11-21','39'),('10001','2017-11-20','1'),('10001','2017-11-21','5'),('10003','2017-11-22','22');

我们在查询引擎中加入了聚合算子,来保证数据对外的一致性。

另外,在聚合列(Value)上,执行与聚合类型不一致的聚合类查询时,要注意语意。例如我们在如上示例中执行如下查询:

mysql> SELECT MIN(cost) FROM example_db.expamle_tb2;
+-------------+
| min(`cost`) |
+-------------+
|           5 |
+-------------+
1 row in set (0.02 sec)

得到的结果是5,而不是1。

同时,这种一致性保证,在某些查询中,会极大的降低查询效率。

我们以最基本的count(*)查询为例:

SELECT COUNT(*) FROM table;

为在实现上,我们可以通过如导入时对行进行计数,保存count的统计信息,或者在查询时仅扫描某一列数据,获得count值的方式,只需很小的开销,即可获得查询结果。但是在Doris的聚合模型中,这种查询的开销非常大。以刚才的数据为例。

batch1

表6 数据

user_id

date

cost

10001

2017-11-20

50

10002

2017-11-21

39

batch2

表7 数据

user_id

date

cost

10001

2017-11-20

1

10001

2017-11-21

5

10003

2017-11-22

22

最终聚合结果如下表所示。

表8 数据

user_id

date

cost

10001

2017-11-20

51

10001

2017-11-21

5

10002

2017-11-21

39

10003

2017-11-22

22

查询结果。

mysql> SELECT COUNT(date) FROM example_db.expamle_tb2;
+---------------+
| count(`date`) |
+---------------+
|             4 |
+---------------+
1 row in set (0.01 sec)

所以,select count(*) from table; 的正确结果应该为4。但如果我们只扫描user_i 这一列,如果加上查询时聚合,最终得到的结果是3(10001, 10002, 10003)。而如果不加查询时聚合,则得到的结果是5(两批次一共5行数据)。可见这两个结果都是不对的。

为了得到正确的结果,我们必须同时读取user_id和date这两列的数据,再加上查询时聚合,才能返回4这个正确的结果。也就是说,在count( * ) 查询中,Doris必须扫描所有的AGGREGATE KEY列(这里就是user_id 和 date),并且聚合后,才能得到语意正确的结果。当聚合列非常多时,count( * )查询需要扫描大量的数据。

因此,当业务上有频繁的count( * )查询时,我们建议用户通过增加一个值恒为1的,聚合类型为SUM的列来模拟count(*)。如刚才的例子中的表结构,我们修改如下:

表9 参数说明

ColumnName

Type

AggregateType

Comment

user_id

BIGINT

-

用户 ID

date

DATE

-

数据导入日期

cost

BIGINT

SUM

用户总消费

count

BIGINT

SUM

用于计算 count

增加一个count列,并且导入数据中,该列值恒为1。则select count(*) from table; 的结果等价于select sum(count) from table;。而后者的查询效率将远高于前者。不过这种方式也有使用限制,就是用户需要自行保证,不会重复导入AGGREGATE KEY列都相同的行。否则,select sum(count) from table; 只能表述原始导入的行数,而不是select count(*) from table; 的语义,前者值会错误的增大。

另一种方式,就是 将如上的count列的聚合类型改为REPLACE,且依然值恒为1。那么select sum(count) from table; 和select count(*) from table; 的结果将是一致的。并且这种方式,没有导入重复行的限制。

Duplicate

Duplicate模型没有聚合模型的这个局限性。因为该模型不涉及聚合语意,在做count(*)查询时,任意选择一列查询,即可得到语意正确的结果。

分享:

    相关文档

    相关产品