Apache Doris 入门教程19:数据更新及删除

批量删除

目前Doris 支持 Broker Load,Routine Load, Stream Load 等多种导入方式,对于数据的删除目前只能通过delete语句进行删除,使用delete 语句的方式删除时,每执行一次delete 都会生成一个新的数据版本,如果频繁删除会严重影响查询性能,并且在使用delete方式删除时,是通过生成一个空的rowset来记录删除条件实现,每次读取都要对删除条件进行过滤,同样在条件较多时会对性能造成影响。对比其他的系统,greenplum 的实现方式更像是传统数据库产品,snowflake 通过merge 语法实现。

对于类似于cdc数据导入的场景,数据中insert和delete一般是穿插出现的,面对这种场景我们目前的导入方式也无法满足,即使我们能够分离出insert和delete虽然可以解决导入的问题,但是仍然解决不了删除的问题。使用批量删除功能可以解决这些个别场景的需求。数据导入有三种合并方式:

  1. APPEND: 数据全部追加到现有数据中;
  2. DELETE: 删除所有与导入数据key 列值相同的行(当表存在sequence列时,需要同时满足主键相同以及sequence列的大小逻辑才能正确删除,详见下边用例4);
  3. MERGE: 根据 DELETE ON 的决定 APPEND 还是 DELETE。

基本原理​

通过增加一个隐藏列__DORIS_DELETE_SIGN__实现,因为我们只是在unique 模型上做批量删除,因此只需要增加一个类型为bool 聚合函数为replace 的隐藏列即可。在be 各种聚合写入流程都和正常列一样,读取方案有两个:

在fe遇到 * 等扩展时去掉__DORIS_DELETE_SIGN__,并且默认加上 __DORIS_DELETE_SIGN__ != true 的条件, be 读取时都会加上一列进行判断,通过条件确定是否删除。

导入​

导入时在fe 解析时将隐藏列的值设置成 DELETE ON 表达式的值,其他的聚合行为和replace的聚合列相同。

读取​

读取时在所有存在隐藏列的olapScanNode上增加__DORIS_DELETE_SIGN__ != true 的条件,be 不感知这一过程,正常执行。

Cumulative Compaction​

Cumulative Compaction 时将隐藏列看作正常的列处理,Compaction逻辑没有变化。

Base Compaction​

Base Compaction 时要将标记为删除的行的删掉,以减少数据占用的空间。

启用批量删除支持​

启用批量删除支持有一下两种形式:

  1. 通过在fe 配置文件中增加enable_batch_delete_by_default=true 重启fe 后新建表的都支持批量删除,此选项默认为false;
  2. 对于没有更改上述fe配置或对于已存在的不支持批量删除功能的表,可以使用如下语句: ALTER TABLE tablename ENABLE FEATURE "BATCH_DELETE" 来启用批量删除。本操作本质上是一个schema change 操作,操作立即返回,可以通过show alter table column 来确认操作是否完成。

那么如何确定一个表是否支持批量删除,可以通过设置一个session variable 来显示隐藏列 SET show_hidden_columns=true ,之后使用desc tablename,如果输出中有__DORIS_DELETE_SIGN__ 列则支持,如果没有则不支持。

语法说明​

导入的语法设计方面主要是增加一个指定删除标记列的字段的column映射,并且需要在导入的数据中增加一列,各种导入方式设置的语法如下

Stream Load​

Stream Load 的写法在header 中的 columns 字段增加一个设置删除标记列的字段, 示例 -H "columns: k1, k2, label_c3" -H "merge_type: [MERGE|APPEND|DELETE]" -H "delete: label_c3=1"

Broker Load​

Broker Load 的写法在 PROPERTIES 处设置删除标记列的字段,语法如下:

LOAD LABEL db1.label1
(
    [MERGE|APPEND|DELETE] DATA INFILE("hdfs://abc.com:8888/user/palo/test/ml/file1")
    INTO TABLE tbl1
    COLUMNS TERMINATED BY ","
    (tmp_c1,tmp_c2, label_c3)
    SET
    (
        id=tmp_c2,
        name=tmp_c1,
    )
    [DELETE ON label_c3=true]
)
WITH BROKER 'broker'
(
    "username"="user",
    "password"="pass"
)
PROPERTIES
(
    "timeout" = "3600"
);

Routine Load​

Routine Load的写法在 columns字段增加映射,映射方式同上,语法如下:

CREATE ROUTINE LOAD example_db.test1 ON example_tbl 
 [WITH MERGE|APPEND|DELETE]
 COLUMNS(k1, k2, k3, v1, v2, label),
 WHERE k1 > 100 and k2 like "%doris%"
 [DELETE ON label=true]
 PROPERTIES
 (
     "desired_concurrent_number"="3",
     "max_batch_interval" = "20",
     "max_batch_rows" = "300000",
     "max_batch_size" = "209715200",
     "strict_mode" = "false"
 )
 FROM KAFKA
 (
     "kafka_broker_list" = "broker1:9092,broker2:9092,broker3:9092",
     "kafka_topic" = "my_topic",
     "kafka_partitions" = "0,1,2,3",
     "kafka_offsets" = "101,0,0,200"
 );

注意事项​

  1. 由于除Stream Load 外的导入操作在doris 内部有可能乱序执行,因此在使用MERGE 方式导入时如果不是Stream Load,需要与 load sequence 一起使用,具体的 语法可以参照sequence列 相关的文档;
  2. DELETE ON 条件只能与 MERGE 一起使用。
  3. 如果在执行导入作业前按上文所述开启了SET show_hidden_columns = true的session variable来查看表是否支持批量删除, 按示例完成DELETE/MERGE的导入作业后, 如果在同一个session中执行select count(*) from xxx等语句时, 需要执行SET show_hidden_columns = false或者开启新的session, 避免查询结果中包含那些被批量删除的记录, 导致结果与预期不符.

使用示例​

查看是否启用批量删除支持​

mysql> SET show_hidden_columns=true;
Query OK, 0 rows affected (0.00 sec)

mysql> DESC test;
+-----------------------+--------------+------+-------+---------+---------+
| Field                 | Type         | Null | Key   | Default | Extra   |
+-----------------------+--------------+------+-------+---------+---------+
| name                  | VARCHAR(100) | No   | true  | NULL    |         |
| gender                | VARCHAR(10)  | Yes  | false | NULL    | REPLACE |
| age                   | INT          | Yes  | false | NULL    | REPLACE |
| __DORIS_DELETE_SIGN__ | TINYINT      | No   | false | 0       | REPLACE |
+-----------------------+--------------+------+-------+---------+---------+
4 rows in set (0.00 sec)

Stream Load使用示例​

  1. 正常导入数据:
curl --location-trusted -u root: -H "column_separator:," -H "columns: siteid, citycode, username, pv" -H "merge_type: APPEND"  -T ~/table1_data http://127.0.0.1:8130/api/test/table1/_stream_load

其中的APPEND 条件可以省略,与下面的语句效果相同:

curl --location-trusted -u root: -H "column_separator:," -H "columns: siteid, citycode, username, pv" -T ~/table1_data http://127.0.0.1:8130/api/test/table1/_stream_load

  1. 将与导入数据key 相同的数据全部删除
curl --location-trusted -u root: -H "column_separator:," -H "columns: siteid, citycode, username, pv" -H "merge_type: DELETE"  -T ~/table1_data http://127.0.0.1:8130/api/test/table1/_stream_load

假设导入表中原有数据为:

+--------+----------+----------+------+
| siteid | citycode | username | pv   |
+--------+----------+----------+------+
|      3 |        2 | tom      |    2 |
|      4 |        3 | bush     |    3 |
|      5 |        3 | helen    |    3 |
+--------+----------+----------+------+

导入数据为:

3,2,tom,0

导入后数据变成:

+--------+----------+----------+------+
| siteid | citycode | username | pv   |
+--------+----------+----------+------+
|      4 |        3 | bush     |    3 |
|      5 |        3 | helen    |    3 |
+--------+----------+----------+------+

  1. 将导入数据中与site_id=1 的行的key列相同的行
curl --location-trusted -u root: -H "column_separator:," -H "columns: siteid, citycode, username, pv" -H "merge_type: MERGE" -H "delete: siteid=1"  -T ~/table1_data http://127.0.0.1:8130/api/test/table1/_stream_load

假设导入前数据为:

+--------+----------+----------+------+
| siteid | citycode | username | pv   |
+--------+----------+----------+------+
|      4 |        3 | bush     |    3 |
|      5 |        3 | helen    |    3 |
|      1 |        1 | jim      |    2 |
+--------+----------+----------+------+

导入数据为:

2,1,grace,2
3,2,tom,2
1,1,jim,2

导入后为:

+--------+----------+----------+------+
| siteid | citycode | username | pv   |
+--------+----------+----------+------+
|      4 |        3 | bush     |    3 |
|      2 |        1 | grace    |    2 |
|      3 |        2 | tom      |    2 |
|      5 |        3 | helen    |    3 |
+--------+----------+----------+------+

  1. 当存在sequence列时,将与导入数据key 相同的数据全部删除
curl --location-trusted -u root: -H "column_separator:," -H "columns: name, gender, age" -H "function_column.sequence_col: age" -H "merge_type: DELETE"  -T ~/table1_data http://127.0.0.1:8130/api/test/table1/_stream_load

当unique表设置了sequence列时,在相同key列下,sequence列的值会作为REPLACE聚合函数替换顺序的依据,较大值可以替换较小值。 当对这种表基于__DORIS_DELETE_SIGN__进行删除标记时,需要保证key相同和sequence列值要大于等于当前值。

假设有表,结构如下

mysql> SET show_hidden_columns=true;
Query OK, 0 rows affected (0.00 sec)

mysql> DESC table1;
+------------------------+--------------+------+-------+---------+---------+
| Field                  | Type         | Null | Key   | Default | Extra   |
+------------------------+--------------+------+-------+---------+---------+
| name                   | VARCHAR(100) | No   | true  | NULL    |         |
| gender                 | VARCHAR(10)  | Yes  | false | NULL    | REPLACE |
| age                    | INT          | Yes  | false | NULL    | REPLACE |
| __DORIS_DELETE_SIGN__  | TINYINT      | No   | false | 0       | REPLACE |
| __DORIS_SEQUENCE_COL__ | INT          | Yes  | false | NULL    | REPLACE |
+------------------------+--------------+------+-------+---------+---------+
4 rows in set (0.00 sec)

假设导入表中原有数据为:

+-------+--------+------+
| name  | gender | age  |
+-------+--------+------+
| li    | male   |   10 |
| wang  | male   |   14 |
| zhang | male   |   12 |
+-------+--------+------+

当导入数据为:

li,male,10

导入后数据后会变成:

+-------+--------+------+
| name  | gender | age  |
+-------+--------+------+
| wang  | male   |   14 |
| zhang | male   |   12 |
+-------+--------+------+

会发现数据

li,male,10

被删除成功。

但是假如导入数据为:

li,male,9

导入后数据会变成:

+-------+--------+------+
| name  | gender | age  |
+-------+--------+------+
| li    | male   |   10 |
| wang  | male   |   14 |
| zhang | male   |   12 |
+-------+--------+------+

会看到数据

li,male,10

并没有被删除,这是因为在底层的依赖关系上,会先判断key相同的情况,对外展示sequence列的值大的行数据,然后在看该行的__DORIS_DELETE_SIGN__值是否为1,如果为1则不会对外展示,如果为0,则仍会读出来。

当导入数据中同时存在数据写入和删除时(例如Flink CDC场景中),使用seq列可以有效的保证当数据乱序到达时的一致性,避免后到达的一个旧版本的删除操作,误删掉了先到达的新版本的数据。

数据更新

本文主要讲述如果我们需要修改或更新Doris中的数据,如何使用UPDATE命令来操作。数据更新对Doris的版本有限制,只能在Doris Version 0.15.x + 才可以使用。

适用场景​

  • 对满足某些条件的行,修改他的取值;
  • 点更新,小范围更新,待更新的行最好是整个表的非常小的一部分;
  • update 命令只能在 Unique 数据模型的表中执行。

基本原理​

利用查询引擎自身的 where 过滤逻辑,从待更新表中筛选出需要被更新的行。再利用 Unique 模型自带的 Value 列新数据替换旧数据的逻辑,将待更新的行变更后,再重新插入到表中,从而实现行级别更新。

同步​

Update 语法在Doris中是一个同步语法,即 Update 语句执行成功,更新操作也就完成了,数据是可见的。

性能​

Update 语句的性能和待更新的行数以及 condition 的检索效率密切相关。

  • 待更新的行数:待更新的行数越多,Update 语句的速度就会越慢。这和导入的原理是一致的。 Doris 的更新比较合适偶发更新的场景,比如修改个别行的值。 Doris 并不适合大批量的修改数据。大批量修改会使得 Update 语句运行时间很久。
  • condition 的检索效率:Doris 的 Update 实现原理是先将满足 condition 的行读取处理,所以如果 condition 的检索效率高,则 Update 的速度也会快。 condition 列最好能命中索引或者分区分桶裁剪,这样 Doris 就不需要扫全表,可以快速定位到需要更新的行,从而提升更新效率。 强烈不推荐 condition 列中包含 UNIQUE 模型的 value 列

并发控制​

默认情况下,并不允许同一时间对同一张表并发进行多个 Update 操作。

主要原因是,Doris 目前支持的是行更新,这意味着,即使用户声明的是 SET v2 = 1,实际上,其他所有的 Value 列也会被覆盖一遍(尽管值没有变化)。

这就会存在一个问题,如果同时有两个 Update 操作对同一行进行更新,那么其行为可能是不确定的,也就是可能存在脏数据。

但在实际应用中,如果用户自己可以保证即使并发更新,也不会同时对同一行进行操作的话,就可以手动打开并发限制。通过修改 FE 配置 enable_concurrent_update,当配置值为 true 时,则对更新并发无限制。

注:开启配置后,会有一定的性能风险,可参考上面的性能小节部分,提升更新效率。

使用风险​

由于 Doris 目前支持的是行更新,并且采用的是读取后再写入的两步操作,则如果 Update 语句和其他导入或 Delete 语句刚好修改的是同一行时,存在不确定的数据结果。

所以用户在使用的时候,一定要注意用户侧自己进行 Update 语句和其他 DML 语句的并发控制。

使用示例​

假设 Doris 中存在一张订单表,其中 订单id 是 Key 列,订单状态,订单金额是 Value 列。数据状态如下:

订单id 订单金额 订单状态
1 100 待付款
+----------+--------------+--------------+
| order_id | order_amount | order_status |
+----------+--------------+--------------+
| 1        |          100 | 待付款       |
+----------+--------------+--------------+
1 row in set (0.01 sec)

这时候,用户点击付款后,Doris 系统需要将订单id 为 '1' 的订单状态变更为 '待发货',就需要用到 Update 功能。

mysql> UPDATE test_order SET order_status = '待发货' WHERE order_id = 1;
Query OK, 1 row affected (0.11 sec)
{'label':'update_20ae22daf0354fe0-b5aceeaaddc666c5', 'status':'VISIBLE', 'txnId':'33', 'queryId':'20ae22daf0354fe0-b5aceeaaddc666c5'}

更新后结果如下

+----------+--------------+--------------+
| order_id | order_amount | order_status |
+----------+--------------+--------------+
| 1        |          100 | 待发货       |
+----------+--------------+--------------+
1 row in set (0.01 sec)

用户执行 UPDATE 命令后,系统会进行如下三步:

  • 第一步:读取满足 WHERE 订单id=1 的行 (1,100,'待付款')

  • 第二步:变更该行的订单状态,从'待付款'改为'待发货' (1,100,'待发货')

  • 第三步:将更新后的行再插入回表中,从而达到更新的效果。

    订单id 订单金额 订单状态
    1 100 待付款
    1 100 待发货

由于表 test_order 是 UNIQUE 模型,所以相同 Key 的行,之后后者才会生效,所以最终效果如下:

订单id 订单金额 订单状态
1 100 待发货

更新Key列​

目前Update操作只支持更新Value列,Key列的更新可参考使用FlinkCDC更新Key列

更多帮助​

关于 数据更新 使用的更多详细语法,请参阅 update 命令手册,也可以在Mysql客户端命令行下输入 HELP UPDATE 获取更多帮助信息。

 

Delete 操作

Delete不同于其他导入方式,它是一个同步过程,与Insert into相似,所有的Delete操作在Doris中是一个独立的导入作业,一般Delete语句需要指定表和分区以及删除的条件来筛选要删除的数据,并将会同时删除base表和rollup表的数据。

语法​

delete操作的语法详见官网 DELETE 语法。

返回结果​

Delete命令是一个SQL命令,返回结果是同步的,分为以下几种:

  1. 执行成功

    如果Delete顺利执行完成并可见,将返回下列结果,Query OK表示成功

    mysql> delete from test_tbl PARTITION p1 where k1 = 1;
    Query OK, 0 rows affected (0.04 sec)
    {'label':'delete_e7830c72-eb14-4cb9-bbb6-eebd4511d251', 'status':'VISIBLE', 'txnId':'4005'}
    

  2. 提交成功,但未可见

    Doris的事务提交分为两步:提交和发布版本,只有完成了发布版本步骤,结果才对用户是可见的。若已经提交成功了,那么就可以认为最终一定会发布成功,Doris会尝试在提交完后等待发布一段时间,如果超时后即使发布版本还未完成也会优先返回给用户,提示用户提交已经完成。若如果Delete已经提交并执行,但是仍未发布版本和可见,将返回下列结果

    mysql> delete from test_tbl PARTITION p1 where k1 = 1;
    Query OK, 0 rows affected (0.04 sec)
    {'label':'delete_e7830c72-eb14-4cb9-bbb6-eebd4511d251', 'status':'COMMITTED', 'txnId':'4005', 'err':'delete job is committed but may be taking effect later' }
    

    结果会同时返回一个json字符串:

    affected rows:表示此次删除影响的行,由于Doris的删除目前是逻辑删除,因此对于这个值是恒为0;

    label:自动生成的 label,是该导入作业的标识。每个导入作业,都有一个在单 database 内部唯一的 Label;

    status:表示数据删除是否可见,如果可见则显示VISIBLE,如果不可见则显示COMMITTED

    txnId:这个Delete job对应的事务id;

    err:字段会显示一些本次删除的详细信息。

  3. 提交失败,事务取消

    如果Delete语句没有提交成功,将会被Doris自动中止,返回下列结果

    mysql> delete from test_tbl partition p1 where k1 > 80;
    ERROR 1064 (HY000): errCode = 2, detailMessage = {错误原因}
    

    示例:

    比如说一个超时的删除,将会返回timeout时间和未完成的(tablet=replica)

    mysql> delete from test_tbl partition p1 where k1 > 80;
    ERROR 1064 (HY000): errCode = 2, detailMessage = failed to delete replicas from job: 4005, Unfinished replicas:10000=60000, 10001=60000, 10002=60000
    

    综上,对于Delete操作返回结果的正确处理逻辑为:

    1. 如果返回结果为ERROR 1064 (HY000),则表示删除失败;
    2. 如果返回结果为Query OK,则表示删除执行成功;
      • 如果statusCOMMITTED,表示数据仍不可见,用户可以稍等一段时间再用show delete命令查看结果;
      • 如果statusVISIBLE,表示数据删除成功。

Delete操作相关FE配置​

TIMEOUT配置

总体来说,Doris的删除作业的超时时间限制在30秒到5分钟时间内,具体时间可通过下面配置项调整

  • tablet_delete_timeout_second

    delete自身的超时时间是可受指定分区下tablet的数量弹性改变的,此项配置为平均一个tablet所贡献的timeout时间,默认值为2。

    假设此次删除所指定分区下有5个tablet,那么可提供给delete的timeout时间为10秒,由于低于最低超时时间30秒,因此最终超时时间为30秒。

  • load_straggler_wait_second

    如果用户预估的数据量确实比较大,使得5分钟的上限不足时,用户可以通过此项调整timeout上限,默认值为300。

    TIMEOUT的具体计算规则为(秒)

    TIMEOUT = MIN(load_straggler_wait_second, MAX(30, tablet_delete_timeout_second * tablet_num))

  • query_timeout

    因为delete本身是一个SQL命令,因此删除语句也会受session限制,timeout还受Session中的query_timeout值影响,可以通过SET query_timeout = xxx来增加超时时间,单位是秒。

IN谓词配置

  • max_allowed_in_element_num_of_delete

    如果用户在使用in谓词时需要占用的元素比较多,用户可以通过此项调整允许携带的元素上限,默认值为1024。

查看历史记录​

用户可以通过show delete语句查看历史上已执行完成的删除记录。

语法如下

SHOW DELETE [FROM db_name]

使用示例

mysql> show delete from test_db;
+-----------+---------------+---------------------+-----------------+----------+
| TableName | PartitionName | CreateTime          | DeleteCondition | State    |
+-----------+---------------+---------------------+-----------------+----------+
| empty_tbl | p3            | 2020-04-15 23:09:35 | k1 EQ "1"       | FINISHED |
| test_tbl  | p4            | 2020-04-15 23:09:53 | k1 GT "80"      | FINISHED |
+-----------+---------------+---------------------+-----------------+----------+
2 rows in set (0.00 sec)

注意事项​

  • 不同于 Insert into 命令,delete 不能手动指定label,有关 label 的概念可以查看Insert Into 文档。

更多帮助​

关于 delete 使用的更多详细语法,请参阅 delete 命令手册,也可以在Mysql客户端命令行下输入 HELP DELETE 获取更多帮助信息。

 

 

sequence 列

Uniq模型主要针对需要唯一主键的场景,可以保证主键唯一性约束,但是由于使用REPLACE聚合方式,在同一批次中导入的数据,替换顺序不做保证,详细介绍可以参考数据模型。替换顺序无法保证则无法确定最终导入到表中的具体数据,存在了不确定性。

为了解决这个问题,Doris支持了sequence列,通过用户在导入时指定sequence列,相同key列下,REPLACE聚合类型的列将按照sequence列的值进行替换,较大值可以替换较小值,反之则无法替换。该方法将顺序的确定交给了用户,由用户控制替换顺序。

sequence列目前只支持Uniq模型。

适用场景​

Sequence列只能在Uniq数据模型下使用。

基本原理​

通过增加一个隐藏列__DORIS_SEQUENCE_COL__实现,该列的类型由用户在建表时指定,在导入时确定该列具体值,并依据该值对REPLACE列进行替换。

建表​

创建Uniq表时,将按照用户指定类型自动添加一个隐藏列__DORIS_SEQUENCE_COL__

导入​

导入时,fe在解析的过程中将隐藏列的值设置成 order by 表达式的值(broker load和routine load),或者function_column.sequence_col表达式的值(stream load),value列将按照该值进行替换。隐藏列__DORIS_SEQUENCE_COL__的值既可以设置为数据源中一列,也可以是表结构中的一列。

读取​

请求包含value列时需要额外读取__DORIS_SEQUENCE_COL__列,该列用于在相同key列下,REPLACE聚合函数替换顺序的依据,较大值可以替换较小值,反之则不能替换。

Cumulative Compaction​

Cumulative Compaction 时和读取过程原理相同。

Base Compaction​

Base Compaction 时读取过程原理相同。

使用语法​

Sequence列建表时有两种方式,一种是建表时设置sequence_col属性,一种是建表时设置sequence_type属性。

设置sequence_col(推荐)​

创建Uniq表时,指定sequence列到表中其他column的映射

PROPERTIES (
    "function_column.sequence_col" = 'column_name',
);

sequence_col用来指定sequence列到表中某一列的映射,该列可以为整型和时间类型(DATE、DATETIME),创建后不能更改该列的类型。

导入方式和没有sequence列时一样,使用相对比较简单,推荐使用。

设置sequence_type

创建Uniq表时,指定sequence列类型

PROPERTIES (
    "function_column.sequence_type" = 'Date',
);

sequence_type用来指定sequence列的类型,可以为整型和时间类型(DATE、DATETIME)。

导入时需要指定sequence列到其他列的映射。

Stream Load

stream load 的写法是在header中的function_column.sequence_col字段添加隐藏列对应的source_sequence的映射, 示例

curl --location-trusted -u root -H "columns: k1,k2,source_sequence,v1,v2" -H "function_column.sequence_col: source_sequence" -T testData http://host:port/api/testDb/testTbl/_stream_load

Broker Load

ORDER BY 处设置隐藏列映射的source_sequence字段

LOAD LABEL db1.label1
(
    DATA INFILE("hdfs://host:port/user/data/*/test.txt")
    INTO TABLE `tbl1`
    COLUMNS TERMINATED BY ","
    (k1,k2,source_sequence,v1,v2)
    ORDER BY source_sequence
)
WITH BROKER 'broker'
(
    "username"="user",
    "password"="pass"
)
PROPERTIES
(
    "timeout" = "3600"
);

Routine Load

映射方式同上,示例如下

   CREATE ROUTINE LOAD example_db.test1 ON example_tbl 
    [WITH MERGE|APPEND|DELETE]
    COLUMNS(k1, k2, source_sequence, v1, v2),
    WHERE k1 > 100 and k2 like "%doris%"
    [ORDER BY source_sequence]
    PROPERTIES
    (
        "desired_concurrent_number"="3",
        "max_batch_interval" = "20",
        "max_batch_rows" = "300000",
        "max_batch_size" = "209715200",
        "strict_mode" = "false"
    )
    FROM KAFKA
    (
        "kafka_broker_list" = "broker1:9092,broker2:9092,broker3:9092",
        "kafka_topic" = "my_topic",
        "kafka_partitions" = "0,1,2,3",
        "kafka_offsets" = "101,0,0,200"
    );

启用sequence column支持​

在新建表时如果设置了function_column.sequence_col或者function_column.sequence_type ,则新建表将支持sequence column。 对于一个不支持sequence column的表,如果想要使用该功能,可以使用如下语句: ALTER TABLE example_db.my_table ENABLE FEATURE "SEQUENCE_LOAD" WITH PROPERTIES ("function_column.sequence_type" = "Date") 来启用。 如果不确定一个表是否支持sequence column,可以通过设置一个session variable来显示隐藏列 SET show_hidden_columns=true ,之后使用desc tablename,如果输出中有__DORIS_SEQUENCE_COL__ 列则支持,如果没有则不支持。

使用示例​

下面以Stream Load为例为示例来展示使用方式:

  1. 创建支持sequence column的表

创建unique模型的test_table数据表,并指定sequence列映射到表中的modify_date列。

CREATE TABLE test.test_table
(
    user_id bigint,
    date date,
    group_id bigint,
    modify_date date,
    keyword VARCHAR(128)
)
UNIQUE KEY(user_id, date, group_id)
DISTRIBUTED BY HASH (user_id) BUCKETS 32
PROPERTIES(
    "function_column.sequence_col" = 'modify_date',
    "replication_num" = "1",
    "in_memory" = "false"
);

表结构如下:

MySQL > desc test_table;
+-------------+--------------+------+-------+---------+---------+
| Field       | Type         | Null | Key   | Default | Extra   |
+-------------+--------------+------+-------+---------+---------+
| user_id     | BIGINT       | No   | true  | NULL    |         |
| date        | DATE         | No   | true  | NULL    |         |
| group_id    | BIGINT       | No   | true  | NULL    |         |
| modify_date | DATE         | No   | false | NULL    | REPLACE |
| keyword     | VARCHAR(128) | No   | false | NULL    | REPLACE |
+-------------+--------------+------+-------+---------+---------+

  1. 正常导入数据:

导入如下数据

1       2020-02-22      1       2020-02-21      a
1       2020-02-22      1       2020-02-22      b
1       2020-02-22      1       2020-03-05      c
1       2020-02-22      1       2020-02-26      d
1       2020-02-22      1       2020-02-23      e
1       2020-02-22      1       2020-02-24      b

此处以stream load为例

curl --location-trusted -u root: -T testData http://host:port/api/test/test_table/_stream_load

结果为

MySQL > select * from test_table;
+---------+------------+----------+-------------+---------+
| user_id | date       | group_id | modify_date | keyword |
+---------+------------+----------+-------------+---------+
|       1 | 2020-02-22 |        1 | 2020-03-05  | c       |
+---------+------------+----------+-------------+---------+

在这次导入中,因sequence column的值(也就是modify_date中的值)中'2020-03-05'为最大值,所以keyword列中最终保留了c。

  1. 替换顺序的保证

上述步骤完成后,接着导入如下数据

1       2020-02-22      1       2020-02-22      a
1       2020-02-22      1       2020-02-23      b

查询数据

MySQL [test]> select * from test_table;
+---------+------------+----------+-------------+---------+
| user_id | date       | group_id | modify_date | keyword |
+---------+------------+----------+-------------+---------+
|       1 | 2020-02-22 |        1 | 2020-03-05  | c       |
+---------+------------+----------+-------------+---------+

在这次导入的数据中,会比较所有已导入数据的sequence column(也就是modify_date),其中'2020-03-05'为最大值,所以keyword列中最终保留了c。

再尝试导入如下数据

1       2020-02-22      1       2020-02-22      a
1       2020-02-22      1       2020-03-23      w

查询数据

MySQL [test]> select * from test_table;
+---------+------------+----------+-------------+---------+
| user_id | date       | group_id | modify_date | keyword |
+---------+------------+----------+-------------+---------+
|       1 | 2020-02-22 |        1 | 2020-03-23  | w       |
+---------+------------+----------+-------------+---------+

此时就可以替换表中原有的数据。综上,在导入过程中,会比较所有批次的sequence列值,选择值最大的记录导入Doris表中。

注意​

  1. 为防止误用,在StreamLoad/BrokerLoad等导入任务中,必须要指定sequence列,不然会收到以下报错信息:
Table test_tbl has sequence column, need to specify the sequence column

  1. 自版本2.0起,Doris对Unique Key表的Merge-on-Write实现支持了部分列更新能力,在部分列更新导入中,用户每次可以只更新一部分列,因此并不是必须要包含sequence列。若用户提交的导入任务中,包含sequence列,则行为无影响;若用户提交的导入任务不包含sequence列,Doris会使用匹配的历史数据中的sequence列作为更新后该行的sequence列的值。如果历史数据中不存在相同key的列,则会自动用null或默认值填充。

你可能感兴趣的:(数据库,mysql,数据仓库,数据分析,大数据)