云服务器内容精选

  • Duplicate模型 在某些多维分析场景下,数据既没有主键,也没有聚合需求。因此,我们引入Duplicate数据模型来满足这类需求。 表13 数据 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”只是用以明确表示所用的数据模型。在DUPLICATE KEY的选择上,我们建议适当的选择前2-4列就可以。 这种数据模型适用于既没有聚合需求,又没有主键唯一性约束的原始数据的存储。更多使用场景,可参见聚合模型的局限性。
  • Unique模型 在某些多维分析场景下,用户更关注的是如何保证Key的唯一性,即如何获得Primary Key唯一性约束。因此,我们引入了Unique的数据模型。该模型本质上是聚合模型的一个特例,也是一种简化的表结构表示方式。举例说明: Unique模型表,不推荐开启merge-on-write属性,默认使用merge-on-read。 表11 参数说明 ColumnName Type IsKey Comment user_id BIGINT Yes 用户 ID username VARCHAR(50) Yes 用户昵称 city VARCHAR(20) No 用户所在城市 age SMALLINT No 用户年龄 sex TINYINT No 用户性别 phone LARGEINT No 用户电话 address VARCHAR(500) No 用户住址 register_time DATETIME No 用户注册时间 这是一个典型的用户基础信息表。这类数据没有聚合需求,只需保证主键唯一性。(这里的主键为user_id+username)。那么我们的建表语句如下: CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用户id", `username` VARCHAR(50) NOT NULL COMMENT "用户昵称", `city` VARCHAR(20) COMMENT "用户所在城市", `age` SMALLINT COMMENT "用户年龄", `sex` TINYINT COMMENT "用户性别", `phone` LARGEINT COMMENT "用户电话", `address` VARCHAR(500) COMMENT "用户地址", `register_time` DATETIME COMMENT "用户注册时间" ) UNIQUE KEY(`user_id`, `username`) DISTRIBUTED BY HASH(`user_id`) BUCKETS 1 PROPERTIES ( "replication_allocation" = "tag.location.default: 1" ); 而这个表结构,完全同等于以下使用聚合模型描述的表结构: 表12 参数说明 ColumnName Type AggregationType Comment user_id BIGINT - 用户 ID username VARCHAR(50) - 用户昵称 city VARCHAR(20) REPLACE 用户所在城市 age SMALLINT REPLACE 用户年龄 sex TINYINT REPLACE 用户性别 phone LARGEINT REPLACE 用户电话 address VARCHAR(500) REPLACE 用户住址 register_time DATETIME REPLACE 用户注册时间 建表语句。 CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用户id", `username` VARCHAR(50) NOT NULL COMMENT "用户昵称", `city` VARCHAR(20) REPLACE COMMENT "用户所在城市", `age` SMALLINT REPLACE COMMENT "用户年龄", `sex` TINYINT REPLACE COMMENT "用户性别", `phone` LARGEINT REPLACE COMMENT "用户电话", `address` VARCHAR(500) REPLACE COMMENT "用户地址", `register_time` DATETIME REPLACE COMMENT "用户注册时间" ) AGGREGATE KEY(`user_id`, `username`) DISTRIBUTED BY HASH(`user_id`) BUCKETS 1 PROPERTIES ( "replication_allocation" = "tag.location.default: 1" ); 即Unique模型完全可以用聚合模型中的REPLACE方式替代。其内部的实现方式和数据存储方式也完全一样。
  • Aggregate模型 以实际的例子来说明什么是聚合模型,以及如何正确的使用聚合模型。 示例1:导入数据聚合 假设业务有以下模式: 表1 参数说明 ColumnName Type AggregationType Comment user_id LARGEINT - 用户 ID date DATE - 数据导入日期 city VARCHAR(20) - 用户所在城市 age SMALLINT - 用户年龄 sex TINYINT - 用户性别 last_visit_date DATETIME REPLACE 用户最后一次访问时间 cost BIGINT SUM 用户总消费 max_dwell_time INT MAX 用户最大停留时间 min_dwell_time INT MIN 用户最小停留时间 转换成建表语句,如下所示。 CREATE TABLE IF NOT EXISTS demo.example_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用户id", `date` DATE NOT NULL COMMENT "数据灌入日期时间", `city` VARCHAR(20) COMMENT "用户所在城市", `age` SMALLINT COMMENT "用户年龄", `sex` TINYINT COMMENT "用户性别", `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间", `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费", `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间", `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间" ) AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`) DISTRIBUTED BY HASH(`user_id`) BUCKETS 1 PROPERTIES ( "replication_allocation" = "tag.location.default: 1" ); 可以看到,这是一个典型的用户信息和访问行为的事实表。在一般星型模型中,用户信息和访问行为一般分别存放在维度表和事实表中。这里我们为了更加方便的解释Doris的数据模型,将两部分信息统一存放在一张表中。 表中的列按照是否设置了AggregationType,分为Key(维度列)和Value(指标列)。没有设置AggregationType的,如user_id、date、age、sex称为Key,而设置了AggregationType的称为Value。 当导入数据时,对于Key列相同的行会聚合成一行,而Value列会按照设置的AggregationType进行聚合。AggregationType目前有以下四种聚合方式: SUM:求和,多行的Value进行累加。 REPLACE:替代,下一批数据中的Value会替换之前导入过的行中的Value。 MAX:保留最大值。 MIN:保留最小值。 表2 原始数据 user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 A 20 0 2017-10-01 06:00:00 20 10 10 10000 2017-10-01 A 20 0 2017-10-01 07:00:00 15 2 2 10001 2017-10-01 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 D 35 0 2017-10-03 10:20:22 11 6 6 我们假设这是一张记录用户访问某商品页面行为的表。我们以第一行数据为例,解释如下: 表3 参数说明 数据 说明 10000 用户id,每个用户唯一识别id 2017-10-01 数据入库时间,精确到日期 A 用户所在城市 20 用户年龄 0 性别男(1 代表女性) 2017-10-01 06:00:00 用户本次访问该页面的时间,精确到秒 20 用户本次访问产生的消费 10 用户本次访问,驻留该页面的时间 10 用户本次访问,驻留该页面的时间(冗余) 那么当这批数据正确导入到Doris中后,Doris中最终存储如下: 表4 插入数据 user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 A 20 0 2017-10-01 07:00:00 35 10 2 10001 2017-10-01 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 D 35 0 2017-10-03 10:20:22 11 6 6 可以看到,用户10000只剩下了一行聚合后的数据。而其余用户的数据和原始数据保持一致。这里先解释下用户10000 聚合后的数据: 前5列没有变化,从第6列 last_visit_date 开始: 2017-10-01 07:00:00:因为last_visit_date列的聚合方式为REPLACE,所以2017-10-01 07:00:00替换了2017-10-01 06:00:00保存了下来。 在同一个导入批次中的数据,对于REPLACE这种聚合方式,替换顺序不做保证。如在这个例子中,最终保存下来的,也有可能是2017-10-01 06:00:00。而对于不同导入批次中的数据,可以保证,后一批次的数据会替换前一批次。 35:因为cost列的聚合类型为SUM,所以由20+15累加获得35。 10:因为max_dwell_time列的聚合类型为MAX,所以10和2取最大值,获得10。 2:因为min_dwell_time列的聚合类型为MIN,所以10和2取最小值,获得2。 经过聚合,Doris中最终只会存储聚合后的数据。换句话说,即明细数据会丢失,用户不能够再查询到聚合前的明细数据了。 示例2:保留,明细数据。 接示例1,将表结构修改如下: 表5 参数说明 ColumnName Type AggregationType Comment user_id LARGEINT - 用户 ID date DATE - 数据导入日期 timestamp DATETIME - 数据导入时间,精确到秒 city VARCHAR(20) - 用户所在城市 age SMALLINT - 用户年龄 sex TINYINT - 用户性别 last_visit_date DATETIME REPLACE 用户最后一次访问时间 cost BIGINT SUM 用户总消费 max_dwell_time INT MAX 用户最大停留时间 min_dwell_time INT MIN 用户最小停留时间 即增加了一列timestamp,记录精确到秒的数据导入时间。 同时,将AGGREGATE KEY设置为AGGREGATE KEY(user_id, date, timestamp, city, age, sex)。 导入数据如下: 表6 原始数据 user_id date timestamp city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 2017-10-01 08:00:05 A 20 0 2017-10-01 06:00:00 20 10 10 10000 2017-10-01 2017-10-01 09:00:05 A 20 0 2017-10-01 07:00:00 15 2 2 10001 2017-10-01 2017-10-01 18:12:10 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 2017-10-02 13:10:00 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 2017-10-02 13:15:00 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 2017-10-01 12:12:48 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 2017-10-03 12:38:20 D 35 0 2017-10-03 10:20:22 11 6 6 那么当这批数据正确导入到Doris中后,Doris中最终存储如下: 表7 数据结果 user_id date timestamp city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 2017-10-01 08:00:05 A 20 0 2017-10-01 06:00:00 20 10 10 10000 2017-10-01 2017-10-01 09:00:05 A 20 0 2017-10-01 07:00:00 15 2 2 10001 2017-10-01 2017-10-01 18:12:10 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 2017-10-02 13:10:00 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 2017-10-02 13:15:00 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 2017-10-01 12:12:48 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 2017-10-03 12:38:20 D 35 0 2017-10-03 10:20:22 11 6 6 示例3:导入数据与已有数据聚合。 接示例1中的参数列表,插入以下表中数据。 表8 原始数据 user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 A 20 0 2017-10-01 07:00:00 35 10 2 10001 2017-10-01 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 D 35 0 2017-10-03 10:20:22 11 6 6 在导入一批新的数据: 表9 新数据 user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10004 2017-10-03 D 35 0 2017-10-03 11:22:00 44 19 19 10005 2017-10-03 E 29 1 2017-10-03 18:11:02 3 1 1 那么当这批数据正确导入到Doris中后,Doris中最终存储如下: 表10 user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 A 20 0 2017-10-01 07:00:00 35 10 2 10001 2017-10-01 A 30 1 2017-10-01 17:05:45 2 22 22 10002 2017-10-02 B 20 1 2017-10-02 12:59:12 200 5 5 10003 2017-10-02 C 32 0 2017-10-02 11:20:00 30 11 11 10004 2017-10-01 D 35 0 2017-10-01 10:00:15 100 3 3 10004 2017-10-03 D 35 0 2017-10-03 11:22:00 55 19 6 10005 2017-10-03 E 29 1 2017-10-03 18:11:02 3 1 1 可以看到,用户10004的已有数据和新导入的数据发生了聚合。同时新增了10005用户的数据。 数据的聚合,在Doris中有如下三个阶段发生: 每一批次数据导入的ETL阶段。该阶段会在每一批次导入的数据内部进行聚合。 底层BE进行数据Compaction的阶段。该阶段,BE会对已导入的不同批次的数据进行进一步的聚合。 数据查询阶段。在数据查询时,对于查询涉及到的数据,会进行对应的聚合。 数据在不同时间,可能聚合的程度不一致。例如一批数据刚导入时,可能还未与之前已存在的数据进行聚合。但是对于用户而言,用户只能查询到聚合后的数据。即不同的聚合程度对于用户查询而言是透明的。用户需始终认为数据以最终的完成的聚合程度存在,而不应假设某些聚合还未发生。
  • 数据模型选择 Doris数据模型上目前分为三类:AGGREGATE KEY,UNIQUE KEY,DUPLICATE KEY。三种模型中数据都是按KEY进行排序。 Aggregate模型。 Aggregate模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。但是该模型对count( * ) 查询很不友好。同时因为固定了Value列上的聚合方式,在进行其他类型的聚合查询时,需要考虑语意正确性。 Aggregate Key相同时,新旧记录进行聚合,目前支持的聚合函数有SUM,MIN,MAX,REPLACE。 CREATE TABLE site_visit ( siteid INT, city SMALLINT, username VARCHAR(32), pv BIGINT SUM DEFAULT '0' ) AGGREGATE KEY(siteid, city, username) DISTRIBUTED BY HASH(siteid) BUCKETS 10; Unique模型。 Unique模型针对需要唯一主键约束的场景,Unique key相同时,新记录覆盖旧记录,可以保证主键唯一性约束。适用于有更新需求的分析业务。目前Unique key实现上和Aggregate key的 REPLACE聚合方法一样,二者本质上相同。但是无法利用ROLLUP等预聚合带来的查询优势(因为本质是REPLACE,没有SUM这种聚合方式)。 CREATE TABLE sales_order ( orderid BIGINT, status TINYINT, username VARCHAR(32), amount BIGINT DEFAULT '0' ) UNIQUE KEY(orderid) DISTRIBUTED BY HASH(orderid) BUCKETS 10; Duplicate模型。 Duplicate模型相同的行不会合并,适合任意维度的Ad-hoc查询。虽然无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(列裁剪、向量执行等)。 CREATE TABLE session_data ( visitorid SMALLINT, sessionid BIGINT, visittime DATETIME, city CHAR(20), province CHAR(20), ip varchar(32), brower CHAR(20), url VARCHAR(1024) ) DUPLICATE KEY(visitorid, sessionid) DISTRIBUTED BY HASH(sessionid, visitorid) BUCKETS 10;
  • 大宽表与Star Schema 业务方建表时, 为了和前端业务适配, 往往不对维度信息和指标信息加以区分, 而将Schema定义成大宽表,这种操作对于数据库其实不是那么友好,我们更建议用户采用星型模型。 Schema中字段数比较多, 聚合模型中可能key列比较多, 导入过程中需要排序的列会增加。 维度信息更新会反应到整张表中,而更新的频率直接影响查询的效率。 使用过程中,建议用户尽量使用Star Schema区分维度表和指标表。频繁更新的维度表也可以放在MySQL外部表中。而如果只有少量更新, 可以直接放在Doris中。在Doris中存储维度表时,可对维度表设置更多的副本,提升Join的性能。