Skip to content

Commit 57ca5f5

Browse files
authored
add content about nt-dml for insert and update (#12079)
1 parent 234cbc5 commit 57ca5f5

File tree

2 files changed

+94
-10
lines changed

2 files changed

+94
-10
lines changed

non-transactional-dml.md

+71-7
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,22 @@ summary: 以事务的原子性和隔离性为代价,将 DML 语句拆成多个
99

1010
非事务 DML 语句是将一个普通 DML 语句拆成多个 SQL 语句(即多个 batch)执行,以牺牲事务的原子性和隔离性为代价,增强批量数据处理场景下的性能和易用性。
1111

12-
非事务 DML 语句包括 `INSERT``UPDATE``DELETE`,TiDB 目前只支持非事务 `DELETE` 语句,详细的语法介绍见 [`BATCH`](/sql-statements/sql-statement-batch.md)
12+
通常,对于消耗内存过多的大事务,你需要在应用中拆分 SQL 语句以绕过事务大小限制。非事务 DML 语句将这一过程集成到 TiDB 内核中,实现等价的效果。非事务 DML 语句的执行效果可以通过拆分 SQL 语句的结果来理解,`DRY RUN` 语法提供了预览拆分后语句的功能。
13+
14+
非事务 DML 语句包括:
15+
16+
- `INSERT INTO ... SELECT`
17+
- `REPLACE INTO .. SELECT`
18+
- `UPDATE`
19+
- `DELETE`
20+
21+
详细的语法介绍见 [`BATCH`](/sql-statements/sql-statement-batch.md)
1322

1423
> **注意:**
1524
>
16-
> 非事务 DML 语句不保证该语句的原子性和隔离性,不能认为它和原始 DML 语句等价。
25+
> - 非事务 DML 语句不保证该语句的原子性和隔离性,不能认为它和原始 DML 语句等价。
26+
> - 在任意 DML 语句改写为非事务 DML 语句后,不应假设其行为与原来一致。
27+
> - 使用非事务 DML 前需要分析其拆分后的语句是否会互相影响。
1728
1829
## 使用场景
1930

@@ -31,8 +42,27 @@ summary: 以事务的原子性和隔离性为代价,将 DML 语句拆成多个
3142

3243
- 确保该语句不需要原子性,即允许执行结果中,一部分行被修改,而一部分行没有被修改。
3344
- 确保该语句具有幂等性,或是做好准备根据错误信息对部分数据重试。如果系统变量 `tidb_redact_log = 1``tidb_nontransactional_ignore_error = 1`,则该语句必须是幂等的。否则语句部分失败时,无法准确定位失败的部分。
34-
- 确保该语句将要操作的数据没有其它并发的写入,即不被其它语句同时更新。否则可能出现漏删、多删等非预期的现象
45+
- 确保该语句将要操作的数据没有其它并发的写入,即不被其它语句同时更新。否则可能出现漏写、多写、重复修改同一行等非预期的现象
3546
- 确保该语句不会修改语句自身会读取的内容,否则后续的 batch 读到之前 batch 写入的内容,容易引起非预期的情况。
47+
- 在使用非事务 `INSERT INTO ... SELECT` 处理同一张表时,尽量不要在插入时修改拆分列,否则可能因为多个 batch 读取到同一行,导致重复插入:
48+
- 不推荐使用 `BATCH ON test.t.id LIMIT 10000 INSERT INTO t SELECT id+1, value FROM t;`
49+
- 推荐使用 `BATCH ON test.t.id LIMIT 10000 INSERT INTO t SELECT id, value FROM t;`
50+
- 当 `id` 列具有 `AUTO_INCREMENT` 属性时,推荐使用 `BATCH ON test.t.id LIMIT 10000 INSERT INTO t(value) SELECT value FROM t;`
51+
- 在使用非事务 `UPDATE``INSERT ... ON DUPLICATE KEY UPDATE``REPLACE INTO` 时,拆分列不应该在语句中更新:
52+
- 例如,对于一条非事务 `UPDATE` 语句,拆分后的 SQL 依次执行,前一 batch 的修改提交后被后一 batch 读到,导致同一行数据被多次修改。
53+
- 这类语句不支持 `BATCH ON test.t.id LIMIT 10000 UPDATE t SET test.t.id = test.t.id-1;`
54+
- 不推荐使用 `BATCH ON test.t.id LIMIT 1 INSERT INTO t SELECT id+1, value FROM t ON DUPLICATE KEY UPDATE id = id + 1;`
55+
- 拆分列也不应该用于 Join key。例如,下面示例将拆分列 `test.t.id` 作为 Join key,导致一个非事务 `UPDATE` 语句多次更新同一行:
56+
57+
```sql
58+
CREATE TABLE t(id int, v int, key(id));
59+
CREATE TABLE t2(id int, v int, key(id));
60+
INSERT INTO t VALUES (1, 1), (2, 2), (3, 3);
61+
INSERT INTO t2 VALUES (1, 1), (2, 2), (4, 4);
62+
BATCH ON test.t.id LIMIT 1 UPDATE t JOIN t2 ON t.id = t2.id SET t2.id = t2.id+1;
63+
SELECT * FROM t2; -- (4, 1) (4, 2) (4, 4)
64+
```
65+
3666
- 确认该语句满足[使用限制](#使用限制)。
3767
- 不建议在该 DML 语句将要读写的表上同时进行并发的 DDL 操作。
3868

@@ -104,6 +134,35 @@ SELECT * FROM t;
104134
1 row in set
105135
```
106136

137+
以下示例说明多表 join 的使用方法。首先创建表 `t2` 并插入数据。
138+
139+
```sql
140+
CREATE TABLE t2(id int, v int, key(id));
141+
INSERT INTO t2 VALUES (1,1), (3,3), (5,5);
142+
```
143+
144+
然后进行涉及多表 join 的更新(表 `t``t2`)。需要注意的是,指定拆分列时需要完整的数据库名、表名和列名(`test.t._tidb_rowid`)。
145+
146+
```sql
147+
BATCH ON test.t._tidb_rowid LIMIT 1 UPDATE t JOIN t2 ON t.id = t2.id SET t2.id = t2.id+1;
148+
```
149+
150+
查看更新后表的数据:
151+
152+
```sql
153+
SELECT * FROM t2;
154+
```
155+
156+
```sql
157+
+----+---+
158+
| id | v |
159+
+----+---+
160+
| 1 | 1 |
161+
| 3 | 3 |
162+
| 6 | 5 |
163+
+----+---+
164+
```
165+
107166
### 查看非事务 DML 语句的执行进度
108167

109168
非事务 DML 语句执行过程中,可以通过 `SHOW PROCESSLIST` 查看执行进度,返回结果中的 `Time` 表示当前 batch 执行的耗时。日志、慢日志等也会记录每个拆分后的语句在整个非事务 DML 语句中的进度。例如:
@@ -183,16 +242,21 @@ BATCH ON id LIMIT 2 DELETE /*+ USE_INDEX(t)*/ FROM t where v < 6;
183242
建议按照以下步骤执行非事务 DML 语句:
184243

185244
1. 选择合适的[划分列](#参数说明)。建议使用整数或字符串类型。
186-
2. (可选)在非事务 DML 语句中添加 `DRY RUN QUERY`,手动执行查询,确认 DML 语句影响的数据范围是否大体正确。
187-
3. (可选)在非事务 DML 语句中添加 `DRY RUN`,手动执行查询,检查拆分后的语句和执行计划。需要关注索引选择效率。
245+
2. 在非事务 DML 语句中添加 `DRY RUN QUERY`,手动执行查询,确认 DML 语句影响的数据范围是否大体正确。
246+
3. 在非事务 DML 语句中添加 `DRY RUN`,手动执行查询,检查拆分后的语句和执行计划。需要关注:
247+
248+
- 一条拆分后的语句是否有可能读到之前的语句执行写入的结果,否则容易造成异常现象。
249+
- 索引选择效率。
250+
- 由 TiDB 自动选择的拆分列是否可能会被修改。
251+
188252
4. 执行非事务 DML 语句。
189253
5. 如果报错,从报错信息或日志中获取具体失败的数据范围,进行重试或手动处理。
190254

191255
## 参数说明
192256

193257
| 参数 | 说明 | 默认值 | 是否必填 | 建议值 |
194258
| :-- | :-- | :-- | :-- | :-- |
195-
| 划分列 | 用于划分 batch 的列,例如以上非事务 DML 语句 `BATCH ON id LIMIT 2 DELETE FROM t WHERE v < 6` 中的 `id`| TiDB 尝试自动选择 || 选择可以最高效地满足 `WHERE` 条件的列 |
259+
| 划分列 | 用于划分 batch 的列,例如以上非事务 DML 语句 `BATCH ON id LIMIT 2 DELETE FROM t WHERE v < 6` 中的 `id` 列 | TiDB 尝试自动选择(不建议) | 否 | 选择可以最高效地满足 `WHERE` 条件的列 |
196260
| Batch size | 用于控制每个 batch 的大小,batch 即 DML 操作拆分成的 SQL 语句个数,例如以上非事务 DML 语句 `BATCH ON id LIMIT 2 DELETE FROM t WHERE v < 6` 中的 `LIMIT 2`。batch 数量越多,batch size 越小 | N/A | 是 | 10001000000,过小和过大都会导致性能下降 |
197261

198262
### 划分列的选择
@@ -217,8 +281,8 @@ BATCH ON id LIMIT 2 DELETE /*+ USE_INDEX(t)*/ FROM t where v < 6;
217281

218282
非事务 DML 语句的硬性限制,不满足这些条件时 TiDB 会报错。
219283

220-
- 只可对单表进行操作,暂不支持多表连接。
221284
- DML 语句不能包含 `ORDER BY``LIMIT` 字句。
285+
- 不支持子查询或集合操作。
222286
- 用于拆分的列必须被索引。该索引可以是单列的索引,或是一个联合索引的第一列。
223287
- 必须在 [`autocommit`](/system-variables.md#autocommit) 模式中使用。
224288
- 不能在开启了 batch-dml 时使用。

sql-statements/sql-statement-batch.md

+23-3
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,40 @@ summary: TiDB 数据库中 BATCH 的使用概况。
77

88
BATCH 语句将一个 DML 语句拆成多个语句在内部执行,因此**不保证**事务的原子性和隔离性,是一个“非事务”语句。
99

10-
目前 BATCH 语句仅支持 `DELETE`
10+
目前 BATCH 语句支持 `INSERT``REPLACE``UPDATE``DELETE`
1111

1212
BATCH 语句在某一列将 DML 语句涉及的范围划分为多个区间,在每个区间执行一条 SQL。
1313

1414
详细的说明和使用限制见[非事务语句](/non-transactional-dml.md)
1515

16+
在涉及多表 join 时,`BATCH` 语法中指定拆分列时需要指明完整的路径以避免歧义,如:
17+
18+
```sql
19+
BATCH ON test.t2.id LIMIT 1 INSERT INTO t SELECT t2.id, t2.v, t3.v FROM t2 JOIN t3 ON t2.k = t3.k;
20+
```
21+
22+
上面这条语句的拆分列用 `test.t2.id` 指明,不具有歧义。如果写成如下 `id` 的形式,则会报错:
23+
24+
```sql
25+
BATCH ON id LIMIT 1 INSERT INTO t SELECT t2.id, t2.v, t3.v FROM t2 JOIN t3 ON t2.k = t3.k;
26+
27+
Non-transactional DML, shard column must be fully specified
28+
```
29+
1630
## 语法图
1731

1832
```ebnf+diagram
19-
NonTransactionalDeleteStmt ::=
20-
'BATCH' ( 'ON' ColumnName )? 'LIMIT' NUM DryRunOptions? DeleteFromStmt
33+
NonTransactionalDMLStmt ::=
34+
'BATCH' ( 'ON' ColumnName )? 'LIMIT' NUM DryRunOptions? ShardableStmt
2135
2236
DryRunOptions ::=
2337
'DRY' 'RUN' 'QUERY'?
38+
39+
ShardableStmt ::=
40+
DeleteFromStmt
41+
| UpdateStmt
42+
| InsertIntoStmt
43+
| ReplaceIntoStmt
2444
```
2545

2646
## MySQL 兼容性

0 commit comments

Comments
 (0)