Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: batch processing overview and pipelined dml #19021

Merged
merged 20 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@
- [Load Base Split 使用文档](/configure-load-base-split.md)
- [Store Limit 使用文档](/configure-store-limit.md)
- [DDL 执行原理及最佳实践](/ddl-introduction.md)
- [批量处理概览](/batch-processing.md)
qiancai marked this conversation as resolved.
Show resolved Hide resolved
- PD 微服务使用文档
- [PD 微服务概览](/pd-microservices.md)
- [使用 TiUP 扩容缩容 PD 微服务节点](/scale-microservices-using-tiup.md)
Expand Down Expand Up @@ -939,6 +940,7 @@
- [乐观事务](/optimistic-transaction.md)
- [悲观事务](/pessimistic-transaction.md)
- [非事务 DML 语句](/non-transactional-dml.md)
- [Pipelined DML](/pipelined-dml.md)
- [视图](/views.md)
- [分区表](/partitioned-table.md)
- [临时表](/temporary-tables.md)
Expand Down
82 changes: 82 additions & 0 deletions batch-processing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: 批量处理概览
summary: 介绍了 TiDB 为批量处理场景提供的功能,包括 Pipelined DML、非事务性 DML、IMPORT INTO 语句、被废弃的 batch dml 等
---

# 批量处理概览

批量处理场景对 TiDB 的主要挑战在于 TiDB 内存限制了事务的大小,以及对更快的处理速度的要求。

随着版本演进,TiDB 提供了多种批量数据处理功能,但它们之间的区别和适用场景容易混淆。本文将详细对比:
- Pipelined DML
- 非事务性 DML
- IMPORT INTO 语句
- 已废弃 batch dml

通过明确每个功能的特点,帮助在批量处理场景下做出正确选择。

## Pipelined DML

Pipelined DML 在 TiDB v8.1 LTS 作为实验性特性发布,在 v8.5 LTS 内核部分功能得到完善,性能大幅提升。

它的主要特点是:
1. 适合通用的批量数据处理场景,支持 INSERT、REPLACE、UPDATE、DELETE 操作。
2. 事务大小不再受到 TiDB 内存限制,支持处理超大规模数据。
3. 速度比标准 DML 更快。
4. 通过系统变量启用,无需修改 SQL 语句。

它的主要限制是:
1. 只适用于自动提交的语句。

它的适用场景是:通用的批量数据处理场景,例如大量数据的插入、更新、删除等。

更多信息见[Pipelined DML](/pipelined-dml.md)。

## 非事务 DML 语句

非事务 DML 语句是在 TiDB v6.1 LTS 首次引入的功能,支持 DELETE。在 v6.5 中开始支持 INSERT、REPLACE、UPDATE。

它的主要特点是:
- 通过将一条语句拆为多条语句执行,使得每个语句的事务更小,绕开内存限制。
- 处理速度比标准 DML 稍快或相当。

它的主要限制是:
- 只适用于自动提交的语句。
- 需要修改 SQL 语句。
- 对 SQL 语句本身限制较多,不符合条件的语句可能需要改写。
- 因为拆分执行,不具有完整的事务 ACID 性质,在失败时语句可能部分完成。

它的适用场景是:大量数据的插入、更新、删除等场景。由于限制较多,建议在 Pipelined DML 不适用的场景下考虑使用。

更多信息见[非事务 DML 语句](/non-transactional-dml.md)。

## IMPORT INTO 语句

IMPORT INTO 语句是在 TiDB v7.5 LTS 首次引入的功能,专为数据导入设计,以替代 TiDB Lightning。

它的主要特点是:
- 导入速度非常快。
- 易用性比 TiDB Lightning 更高。

它的主要限制是:
- 只适合数据导入场景。
- 不满足事务 ACID 性质。
- 使用限制较多。

它的适用场景是:数据导入场景,例如数据迁移、数据恢复等。建议在合适的场景下,使用 IMPORT INTO 代替 TiDB Lightning。

更多信息见[IMPORT INTO](/sql-statements/sql-statement-import-into.md)。

## 已被废弃的 batch dml

在 TiDB v4.0 以前用于批量处理的 batch dml 功能已被废弃,不再推荐使用。这个功能是由以下这组系统变量控制的:

- `tidb_batch_insert`
- `tidb_batch_delete`
- `tidb_batch_commit`
- `tidb_enable_batch_dml`
- `tidb_dml_batch_size`

这组功能因为容易引起数据索引不一致,导致数据损坏或丢失,已被废弃。相关变量将被逐渐移除。

不建议在任何场景下使用已被废弃的 batch dml 功能。建议迁移到上面描述的其它方案。
152 changes: 152 additions & 0 deletions pipelined-dml.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
title: Pipelined DML
summary: Pipelined DML 增强了 TiDB 批量处理的能力,使得事务大小不再受到 TiDB 内存限制。batch processing, bulk, non-transactional DML
---

# Pipelined DML

> **警告:**
>
> 该功能目前为实验特性,不建议在生产环境中使用。该功能可能会在未事先通知的情况下发生变化或删除。语法和实现可能会在 GA 前发生变化。如果发现 bug,请在 GitHub 上提 [issue](https://github.com/pingcap/tidb/issues) 反馈。

本文档介绍 Pipelined DML 的使用场景、使用方法、使用限制和使用该功能的常见问题。

Pipelined DML 是 TiDB 的一项实验特性,用于优化大规模数据写入场景。通过持续将事务数据写入存储层而不是完全缓存在内存中,该特性可以显著降低内存占用,提升大规模数据写入性能。使用 Pipelined DML,你可以执行超大规模的 DML 操作而不必担心内存溢出问题。

## 使用场景和作用

Pipelined DML 在以下场景中具有明显优势:

- 需要执行大规模数据迁移或归档操作
- 进行批量数据导入或更新
- ETL (Extract, Transform, Load) 处理涉及大量数据写入
- 需要降低大规模写入时的内存占用

Pipelined DML 在这些场景主要有 3 点优势
- 事务使用内存可控,事务大小不受 TiDB 内存总量限制。事务部分内存使用限制在 1 GiB 以内。
- 延迟更低。
- CPU 和 IO 负载更平滑。

### 数据归档场景

当需要将历史数据从活跃表归档到归档表时,通常涉及大量数据迁移。例如:

```sql
INSERT INTO sales_archive SELECT * FROM sales WHERE sale_date < '2023-01-01';
```

使用 Pipelined DML 可以有效处理数百万行数据的迁移,避免 OOM 问题。

### 批量数据更新场景

当需要对大量数据进行更新操作时,例如批量调整商品价格或更新用户状态,Pipelined DML 可以高效完成操作:

```sql
UPDATE /*+ SET_VAR(tidb_dml_type='bulk') */ products
SET price = price * 1.1
WHERE category = 'electronics';
```

### 批量删除场景

当需要删除大量数据时,例如删除历史数据或清理过期数据,Pipelined DML 可以高效完成操作:

```sql
DELETE /*+ SET_VAR(tidb_dml_type='bulk') */ FROM logs WHERE log_time < '2023-01-01';
```

## 使用方法

### 启用 Pipelined DML

要使用 Pipelined DML,你需要将 `tidb_dml_type` 会话变量设置为 `"bulk"`:

```sql
SET tidb_dml_type = "bulk";
```

或者使用 SET_VAR hint,如:

```sql
INSERT /*+ SET_VAR(tidb_dml_type='bulk') */ INTO target_table SELECT * FROM source_table;
```

### 验证是否生效

执行语句后,可以通过检查 [`tidb_last_txn_info`](#tidb_last_txn_info-从-v409-版本开始引入) 变量来确认是否使用了 Pipelined DML:

```sql
SELECT @@tidb_last_txn_info;
```

如果返回结果中 `pipelined` 字段为 `true`,则表示成功使用了 Pipelined DML。

## 使用条件

1. 操作包含涉及 TiCDC、TiFlash 或 BR 的表时不可以使用。
> **警告:**
>
> 目前 Pipelined DML 尚不兼容,强行使用可能会引发阻塞和相关组件 OOM 等问题。未来版本会逐渐支持。

2. Pipelined DML 不适合在有写写冲突的场景使用。在这种场景下,Pipelined DML 性能可能大幅下降,或失败回滚。
3. 需要确保在语句执行过程中保持[元数据锁](/metadata-lock.md)处于开启状态。
4. 使用 Pipelined DML 时,TiDB 会检测以下条件是否符合,不符合时 TiDB 会拒绝使用 Pipelined DML 执行,并自动回退到普通 DML 执行,同时生成对应的 Warning 信息:
- 仅适用于自动提交的语句
- 仅适用于 `INSERT`、`UPDATE`、`REPLACE` 和 `DELETE` 语句
- 不可用于[临时表](/temporary-tables.md)和[缓存表](/cached-tables.md)
- 开启外键约束检查时 (`foreign_key_checks = ON`) ,不可包含外键相关表操作

特殊行为:
- `INSERT IGNORE ... ON DUPLICATE KEY UPDATE` 语句可能会在更新造成冲突时报出 `Duplicate entry` 的错误。

## 最佳实践

- 将 [`tidb_mem_quota_query`](/system-variables.md#tidb_mem_quota_query) 略微调大,以确保执行器等部分的内存使用不会超过限制。建议值为至少 2 GiB。对于 TiDB 内存充足的情况,可以适当调大。
- 在向新表进行插入的场景,Pipelined DML 易于受到热点影响。为实现最佳性能,建议尽可能先打散热点。可以参考[TiDB 热点问题处理](/troubleshoot-hot-spot-issues.md#tidb-热点问题处理)。

## 相关配置

- 系统变量 [`tidb_dml_type`](/system-variables.md#tidb_dml_type-从-v800-版本开始引入):用于控制是否启用 Pipelined DML。
- 配置项[`pessimistic-auto-commit`](/tidb-configuration-file.md#pessimistic-auto-commit):当使用 Pipelined DML 时,该配置项的效果等同于设置为 `false`。
- 以 Pipelined DML 方式执行超大事务时,事务耗时可能较长。对于这种模式的事务,其事务锁的最大 TTL 为 [`max-txn-ttl`](/tidb-configuration-file.md#max-txn-ttl) 与 24 小时中的较大值。
- 当事务执行时间超过 [`tidb_gc_max_wait_time`](/system-variables.md#tidb_gc_max_wait_time-从-v610-版本开始引入) 设定值后,GC 可能会强制回滚事务,导致事务失败。
- 以 `"bulk"` 方式执行事务时,事务的大小不受 TiDB 配置项 [`txn-total-size-limit`](/tidb-configuration-file.md#txn-total-size-limit) 的限制。

## 观测 Pipelined DML

Pipelined DML 的执行过程可以通过以下方式进行观测:
- 系统变量[`tidb_last_txn_info`](#tidb_last_txn_info-从-v409-版本开始引入),它可以查看上一个事务的执行信息,包括是否使用了 Pipelined DML。
- TiDB 日志中包含 "[pipelined dml]" 字样的日志行表示展示了 Pipelined DML 的执行过程和进度,包括当前阶段、已经写入的数据量等。
- TiDB 日志中的 ["expensive query" 日志](/identify-expensive-queries.md#expensive-query-日志示例) 包含的 affected rows 字段,可以查看耗时较长语句的当前进度。
- [`INFORMATION_SCHEMA.PROCESSLIST`](/information-schema/information-schema-processlist.md)表,会展示事务的执行进度。Pipelined DML 通常用于大事务,执行耗时较长,可以通过该表查看事务的执行进度。

## 常见问题

### 为什么我的查询没有使用 Pipelined DML?

当 TiDB 拒绝以 Pipelined DML 模式执行语句时,会生成对应的 warning,可以通过检查 warning 信息得知原因。

常见的原因:

1. 语句不是自动提交的
2. 使用了不支持的表类型
3. 涉及外键且外键检查开启

### Pipelined DML 会影响事务的隔离级别吗?

不会。Pipelined DML 仅改变了事务写入的实现机制,不影响 TiDB 的事务隔离保证。

### 为什么使用了 Pipelined DML 还是会内存不足?

即时开启了 Pipelined DML,仍然有可能碰到内存 quota 不足导致语句被 kill 的情况:
```
The query has been canceled due to exceeding the memory limit allowed for a single SQL query. Please try to narrow the query scope or increase the tidb_mem_quota_query limit, and then try again.
```

这是因为 Pipelined DML 功能仅控制了事务部分使用的内存,但语句使用的总内存还需要包括执行器等部分的内存。如果语句需要的总内存空间超过了 TiDB 的内存限制,仍然会出现内存不足的情况。
通常将 [`tidb_mem_quota_query`](/system-variables.md#tidb_mem_quota_query) 设置为更大的值可以解决这个问题。推荐设置为 2 GiB。对于算子复杂或数据量大的 SQL,可能需要更大的值。

## 探索更多

- [批量处理概览](/batch-processing.md)
- [TiDB 内存控制](/configure-memory-usage.md)
12 changes: 1 addition & 11 deletions system-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -1628,17 +1628,7 @@ mysql> SELECT job_info FROM mysql.analyze_jobs ORDER BY end_time DESC LIMIT 1;
- 该变量用来设置 DML 语句的执行方式。
- `"standard"` 表示使用标准的 DML 执行方式,TiDB 事务在提交前缓存在内存中。适用于处理高并发且可能存在冲突的事务场景,为默认推荐使用的执行方式。
- `"bulk"` 表示使用批量 DML 执行方式,适合于处理因大量数据写入导致 TiDB 内存使用过多的情况。
- 在 TiDB 事务执行过程中,数据不是完全缓存在 TiDB 内存中,而是持续写入 TiKV,以减少内存的占用,同时平滑写入压力。
- 只有 `INSERT`、`UPDATE`、`REPLACE` 和 `DELETE` 语句受 `"bulk"` 方式的影响。由于 `"bulk"` 模式流水线执行的方式,其中 `INSERT IGNORE ... ON DUPLICATE UPDATE ...` 的用法可能会在更新造成冲突时报出 `Duplicate entry` 的错误;而在 `"standard"` 模式下,由于设置了 `IGNORE` 关键字,该错误会被忽略,不会返回给用户。
- `"bulk"` 方式仅适用于大批量**无冲突数据写入**的场景,不能高效处理写入冲突的场景,写写冲突可能会导致大批量事务提交失败并被回滚。
- `"bulk"` 方式只对自动提交 (auto-commit) 的语句生效。当设置为 `"bulk"` 时,[`pessimistic-auto-commit`](/tidb-configuration-file.md#pessimistic-auto-commit) 配置项的效果等同于设置为 `false`。
- 使用 `"bulk"` 方式执行语句时,需要确保在语句执行过程中保持[元数据锁](/metadata-lock.md)处于开启状态。
- `"bulk"` 方式不可以在[临时表](/temporary-tables.md)、[缓存表](/cached-tables.md)上使用。
- `"bulk"` 方式不可以在开启外键约束检查时 (`foreign_key_checks = ON`) 对包含外键的表和被外键引用的表使用。
- 当遇到不支持或不兼容的情况时,`"bulk"` 方式会回退到 `"standard"` 方式执行,并返回一条警告信息。你可以通过 [`tidb_last_txn_info`](#tidb_last_txn_info-从-v409-版本开始引入) 查看 `pipelined` 字段,如果为 `true` 则表示是使用 `"bulk"` 方式执行。
- 以 `"bulk"` 方式执行超大事务时,事务耗时可能较长。对于这种模式的事务,其事务锁的最大 TTL 为 [`max-txn-ttl`](/tidb-configuration-file.md#max-txn-ttl) 与 24 小时中的较大值。此外,当事务执行时间超过 [`tidb_gc_max_wait_time`](#tidb_gc_max_wait_time-从-v610-版本开始引入) 设定值后,GC 可能会强制回滚事务,导致事务失败。
- 以 `"bulk"` 方式执行事务时,事务的大小不受 TiDB 配置项 [`txn-total-size-limit`](/tidb-configuration-file.md#txn-total-size-limit) 的限制。
- `"bulk"` 方式由 Pipelined DML 特性实现,详细设计和 GitHub issue 可见 [Pipelined DML](https://github.com/pingcap/tidb/blob/master/docs/design/2024-01-09-pipelined-DML.md) 和 [#50215](https://github.com/pingcap/tidb/issues/50215)。
- `"bulk"` 方式目前由 Pipelined DML 特性实现,可见 [Pipelined DML](/pipelined-dml.md)。

### `tidb_enable_1pc` <span class="version-mark">从 v5.0 版本开始引入</span>

Expand Down
Loading