大数据Doris之_4_数据删除
前言
Github:https://github.com/HealerJean
一、事物
说明:事务是指一个操作,包含一个或多个
SQL语句,这些语句的执行要么完全成功,要么完全失败,是一个不可分割的工作单位。查询和
DDL单个语句是一个隐式事务,不支持多语句事务中包含查询和DDL。每个单独的写入默认是一个隐式的事务,多个INSERTINTOVALUES可以组成一个显式事务。目前Doris不支持嵌套事务。
1、显式事务
显式事务需要用户主动开启、提交或回滚事务,目前不支持 DDL 和查询语句。
开启事务:执行该语句时,当前 Session 正处于一个事务的中间过程,那么 Doris 会忽略该语句,也可以理解为事务是不能嵌套的。
BEGIN;
BEGIN WITH LABEL {user_label};
提交事务:用于提交在当前事务中进行的所有修改。
COMMIT;
2、隐式事务
隐式事务是指用户在所执行的一条或多条 SQL 语句的前后,没有显式添加开启事务和提交事务的语句。在 Doris 中,除[Group Commit] 外,每个导入语句在开始执行时都会开启一个事务,并且在该语句执行完成之后,自动提交该事务;或执行失败后,自动回滚该事务。每个查询或者 DDL 也是一个隐藏事务。
3、隔离级别
Doris 当前支持的唯一隔离级别是 READ COMMITTED。在 READ COMMITTED 隔离级别下,语句只能看到在该语句开始执行之前已经提交的数据,它不会看到未提交的数据。当一个语句在多语句事务中执行时:
-
目前看不到在同一事务中之前语句所做的更改:单个语句执行时,会在语句的开始捕获涉及到表的快照,即单个语句只能看见开始执行前其它事务的提交,单个语句执行期间不可见其它事务的提交。
-
只能看到在该语句开始执行之前已经提交的数据。如果在执行第一个和第二个语句之间有另一个事务提交,那么同一事务中的两个连续语句可能会看到不同的数据。
4、不重不丢
Doris有两个机制支持写入的不重不丢,使用Label机制提供了单个事务的不重,使用两阶段提交提供了协调多事务不重的能力。
1)Label 机制
-
Label 的基础特性与核心功能:
Doris的事务或者写入可以设置一个Label。这个Label通常是用户自定义的、具有一定业务逻辑属性的字符串,不设置时内部会生成一个UUID字符串。Label的主要作用是唯一标识一个事务或者导入任务,并且能够保证相同Label的事务或者导入仅会成功执行一次。 -
Label 与数据一致性保障:
Label机制可以保证导入数据的不丢不重,如果上游数据源能够保证At-Least-Once语义,则配合Doris的Label机制,能够保证Exactly-Once语义。Label在一个数据库下具有唯一性。 -
Label 的自动清理策略:
Doris会根据时间和数目清理Label,默认Label数目超过2000个就会触发淘汰,默认超过3天的Label也会被淘汰。Label被淘汰后相同名称的Label可以再次执行成功,即不再具有去重语义。 -
Label 命名规范:
Label通常被设置为业务逻辑+时间的格式。如my_business1_20220330_125000。这个Label通常用于表示:业务my_business1这个业务在2022-03-30 12:50:00产生的一批数据。通过这种Label设定,业务上可以通过Label查询导入任务状态,来明确的获知该时间点批次的数据是否已经导入成功。如果没有成功,则可以使用这个Label继续重试导入。
2)StreamLoad 2PC
StreamLoad2PC,主要用于支持Flink写入Doris时的 EOS 语义。
二、多条 SQL语句写入
目前
Doris中支持2种方式的事务写入。
1、单表多次INSERT INTO VALUES写入
这种写入方式不仅可以实现写入的原子性,而且在
Doris中,能提升INSERT INTO VALUES的写入性能。如果用户同时开启了
Group Commit和事务写,事务写生效。
假如表的结构为:
CREATE TABLE `dt` (
`id` INT(11) NOT NULL,
`name` VARCHAR(50) NULL,
`score` INT(11) NULL
) ENGINE=OLAP
UNIQUE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
);
写入:
mysql> BEGIN;
Query OK, 0 rows affected (0.01 sec)
{'label':'txn_insert_b55db21aad7451b-b5b6c339704920c5', 'status':'PREPARE', 'txnId':''}
mysql> INSERT INTO dt (id, name, score) VALUES (1, "Emily", 25), (2, "Benjamin", 35), (3, "Olivia", 28), (4, "Alexander", 60), (5, "Ava", 17);
Query OK, 5 rows affected (0.08 sec)
{'label':'txn_insert_b55db21aad7451b-b5b6c339704920c5', 'status':'PREPARE', 'txnId':'10013'}
mysql> INSERT INTO dt VALUES (6, "William", 69), (7, "Sophia", 32), (8, "James", 64), (9, "Emma", 37), (10, "Liam", 64);
Query OK, 5 rows affected (0.00 sec)
{'label':'txn_insert_b55db21aad7451b-b5b6c339704920c5', 'status':'PREPARE', 'txnId':'10013'}
mysql> COMMIT;
Query OK, 0 rows affected (1.02 sec)
{'label':'txn_insert_b55db21aad7451b-b5b6c339704920c5', 'status':'VISIBLE', 'txnId':'10013'}
2、Stream Load 2PC
1. 在 HTTP Header 中设置 two_phase_commit:true 启用两阶段提交。
curl --location-trusted -u user:passwd -H "two_phase_commit:true" -T test.txt http://fe_host:http_port/api/{db}/{table}/_stream_load
{
"TxnId": 18036,
"Label": "55c8ffc9-1c40-4d51-b75e-f2265b3602ef",
"TwoPhaseCommit": "true",
"Status": "Success",
"Message": "OK",
"NumberTotalRows": 100,
"NumberLoadedRows": 100,
"NumberFilteredRows": 0,
"NumberUnselectedRows": 0,
"LoadBytes": 1031,
"LoadTimeMs": 77,
"BeginTxnTimeMs": 1,
"StreamLoadPutTimeMs": 1,
"ReadDataTimeMs": 0,
"WriteDataTimeMs": 58,
"CommitAndPublishTimeMs": 0
}
2. 对事务触发 commit 操作(请求发往 FE 或 BE 均可)
-
可以使用事务
id指定事务curl -X PUT --location-trusted -u user:passwd -H "txn_id:18036" -H "txn_operation:commit" http://fe_host:http_port/api/{db}/{table}/_stream_load_2pc { "status": "Success", "msg": "transaction [18036] commit successfully." } -
也可以使用
label指定事务curl -X PUT --location-trusted -u user:passwd -H "label:55c8ffc9-1c40-4d51-b75e-f2265b3602ef" -H "txn_operation:commit" http://fe_host:http_port/api/{db}/{table}/_stream_load_2pc { "status": "Success", "msg": "label [55c8ffc9-1c40-4d51-b75e-f2265b3602ef] commit successfully." }
3. 对事务触发 abort 操作(请求发往 FE 或 BE 均可)
-
可以使用事务
id指定事务curl -X PUT --location-trusted -u user:passwd -H "txn_id:18037" -H "txn_operation:abort" http://fe_host:http_port/api/{db}/{table}/_stream_load_2pc { "status": "Success", "msg": "transaction [18037] abort successfully." } -
也可以使用
label指定事务curl -X PUT --location-trusted -u user:passwd -H "label:55c8ffc9-1c40-4d51-b75e-f2265b3602ef" -H "txn_operation:abort" http://fe_host:http_port/api/{db}/{table}/_stream_load_2pc { "status": "Success", "msg": "label [55c8ffc9-1c40-4d51-b75e-f2265b3602ef] abort successfully." }
3、Broker Load 多表事务
所有 Broker Load 导入任务都是原子生效的。并且在同一个导入任务中对多张表的导入也能够保证原子性。还可以通过 Label 的机制来保证数据导入的不丢不重。
下面例子是从 HDFS 导入数据,使用通配符匹配两批文件,分别导入到两个表中。
LOAD LABEL example_db.label2
(
DATA INFILE("hdfs://hdfs_host:hdfs_port/input/file-10*")
INTO TABLE `my_table1`
PARTITION (p1)
COLUMNS TERMINATED BY ","
(k1, tmp_k2, tmp_k3)
SET (
k2 = tmp_k2 + 1,
k3 = tmp_k3 + 1
)
DATA INFILE("hdfs://hdfs_host:hdfs_port/input/file-20*")
INTO TABLE `my_table2`
COLUMNS TERMINATED BY ","
(k1, k2, k3)
)
WITH BROKER hdfs
(
"username"="hdfs_user",
"password"="hdfs_password"
);
使用通配符匹配导入两批文件 file-10* 和 file-20*。分别导入到 my_table1 和 my_table2 两张表中。其中 my_table1 指定导入到分区 p1 中,并且将导入源文件中第二列和第三列的值 +1 后导入。
三、事物 和 Group Commit
1、 基本概念与区别
1)事务(Transaction)
-
定义:事务是保证数据一致性的基本单位,确保一组操作要么全部成功提交,要么完全回滚。
Doris支持显式事务(通过BEGIN; COMMIT;控制)和隐式事务(单条语句自动提交) - 特点:
-
支持原子性、隔离性(READ COMMITTED级别)和持久性
- 适用于多语句操作的原子性保证,例如批量插入或跨表更新
2)Group Commit
-
定义:
GroupCommit是一种针对高频小批量写入的优化机制,通过合并多个独立导入任务为单个事务提交,减少FE解析和版本生成的开销 -
特点:
- 适用于
INSERT INTO VALUES和StreamLoad等场景,提升高并发写入性能 - 提供三种模式:关闭(
off_mode)、同步(sync_mode)和异步(async_mode),后两者通过合并写入降低资源消耗
- 适用于
2、协同关系
-
优先级:当同时开启事务和
GroupCommit时,显式事务优先生效,Group Commit会被忽略。例如,在BEGIN; INSERT...; COMMIT;中,GroupCommit不参与写入合并 - 互补场景
-
事务:适合需要强一致性的复杂操作(如多表更新或条件写入)
GroupCommit:适合无事务要求的高频小批量写入(如日志或实时流数据),通过合并减少版本数和FE压力
| 机制 | 适用场景 | 性能优势 | 限制 |
|---|---|---|---|
| 事务 | 多语句原子操作、跨表写入 | 保证数据一致性 | 高并发下FE解析开销大 |
Group Commit |
高频小批量写入(如每秒数千条) | 减少版本数、降低FE和BE负载 | 不适用于需要即时可见性的场景 |


