Skip to content
98 changes: 98 additions & 0 deletions configure-memory-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,104 @@ TiDB 支持对执行算子的数据落盘功能。当 SQL 的内存使用超过
9 rows in set (1 min 37.428 sec)
```

## 全局内存管理架构

TiDB 从 v9.0.0 开始引入全局内存管理框架 `Global Memory Arbitrator`,可通过系统变量 [`tidb_mem_arbitrator_mode`](/system-variables.md#tidb_mem_arbitrator_mode-从-v900-版本开始引入) 开启。

`tidb_mem_arbitrator_mode` 默认为 `disable`。该模式下内存资源先使用后上报,[相关控制行为同上](#如何配置-tidb-server-实例使用内存的阈值)。

设置 `tidb_mem_arbitrator_mode` 为 `standard` 或 `priority` 启用先订阅后分配模式,由 TiDB 实例中唯一的仲裁者统筹内存资源。

仲裁者会通过终止 SQL 来回收内存资源,并向客户端返回编号为 `8180` 的错误。错误格式为:`Query execution was stopped by the global memory arbitrator [reason=?, path=?] [conn=?]`:

- `conn`:连接(会话)ID
- `reason`:终止 SQL 的具体原因
- `path`:SQL 被终止的阶段(无 `path` 字段则默认为执行阶段),例如:`ParseSQL`(解析);`CompilePlan`(编译执行计划);

### `standard` 模式

SQL 运行过程中会动态地向仲裁者订阅内存资源,仲裁者按先来先服务原则处理订阅请求。如果全局内存资源不足,仲裁者令请求失败并终止 SQL。返回错误中 `reason` 字段为 `CANCEL(out-of-quota & standard-mode)`。

### `priority` 模式

SQL 运行过程中会动态地向仲裁者订阅内存资源,仲裁者根据 SQL 的[资源组优先级](/information-schema/information-schema-resource-groups.md) (`LOW | MEDIUM | HIGH`) 处理订阅请求。

- 所有请求按照优先级从高到低排队等待资源,同级别请求按照发起顺序排序
- 当全局内存资源不足时,仲裁者按顺序(优先级从低到高,内存使用量从大到小)终止低优先级 SQL,回收资源来满足高优先级 SQL
- 如果所有 SQL 的优先级相同,仲裁者会调度资源直到所有 SQL 运行完,可能因此导致部分 SQL 延迟明显增大
- 返回错误中 `reason` 字段为 `CANCEL(out-of-quota & priority-mode)`

如果需要 SQL 避免因等待内存资源所带来的延迟开销,可以设置 session 变量 [`tidb_mem_arbitrator_wait_averse`](/system-variables.md#tidb_mem_arbitrator_wait_averse-从-v900-版本开始引入) 为 `1`。该参数令 SQL 自动绑定 `HIGH` 优先级。当全局内存资源不足时,仲裁者直接终止 SQL。返回错误中 `reason` 字段为 `CANCEL(out-of-quota & wait-averse)`。

### 内存风险控制

当 TiDB 实例的内存用量达到 [`tidb_server_memory_limit`](/system-variables.md#tidb_server_memory_limit-从-v640-版本开始引入) 的 95% 这一阈值时,仲裁者开始处理内存风险。如果内存用量短期无法低于到安全线或者内存使用速率过小,仲裁者会按顺序(优先级从低到高,内存使用量从大到小)强制终止 SQL,返回错误中 `reason` 字段为 `KILL(out-of-memory)`。

如果需要在内存资源不足时强制运行 SQL,可以设置 session 变量 [`tidb_mem_arbitrator_wait_averse`](/system-variables.md#tidb_mem_arbitrator_wait_averse-从-v900-版本开始引入) 为 `nolimit`。该参数令 SQL 使用内存不受框架限制,但可能导致 TiDB 实例内存风险。

### 手动保障内存安全

通过系统变量 [`tidb_mem_arbitrator_soft_limit`](/system-variables.md#tidb_mem_arbitrator_soft_limit-从-v900-版本开始引入) 可以设置 TiDB 实例的内存资源份额上限。上限越小,全局内存越安全,但内存资源利用率越低。该变量可用于手动快速收敛内存风险。

框架内部会缓存部分 SQL 的历史最大内存资源用量,并在 SQL 下次执行前预先订阅足量内存资源份额。如果已知 SQL 存在大量内存使用不受控制的问题,可通过 session 变量 [`tidb_mem_arbitrator_query_reserved`](/system-variables.md#tidb_mem_arbitrator_query_reserved-从-v900-版本开始引入) 指定 SQL 订阅数量的份额。该值越大,全局内存越安全,但内存资源利用率越低。预先订阅足量或超量的份额可以有效地保障 SQL 的内存资源隔离性。

### 监控和观测指标

`Grafana` 监控新增 `TiDB / Memory Arbitrator` 面板,包含以下指标:

- Work Mode:各个 TiDB 实例的内存管理模式
- Arbitration Exec:框架处理各类请求的统计
- Events:框架内各类事件的统计
- Mem Quota Stats:各类内存资源份额占用
- Mem Quota Arbitration:内存资源订阅请求处理耗时
- Mem Pool Stats:各类内存池的数量
- Runtime Mem Pressure:内存压力值(实际内存使用和内存资源份额使用的比率)
- Waiting Tasks:排队等待中的各类任务数量

[`SLOW_QUERY`](/information-schema/information-schema-slow-query.md) 新增 `Mem_arbitration` 字段表示 SQL 等待内存资源的总耗时。

[`PROCESSLIST`](/information-schema/information-schema-processlist.md) 新增以下字段:

- `MEM_ARBITRATION`:目前为止 SQL 等待内存资源的总耗时
- `MEM_WAIT_ARBITRATE_START`:当前内存资源订阅请求的开始时间(没有则为 NULL)
- `MEM_WAIT_ARBITRATE_BYTES`:当前内存资源订阅请求的申请字节数(没有则为 NULL)

[Expensive query](/identify-expensive-queries.md) 新增字段 `mem_arbitration`,用于记录 SQL 等待资源耗时和当前订阅请求的信息,例如:

- `cost_time 2.1s, wait_start 1970-01-02 10:17:36.789 UTC, wait_bytes 123456789123 Bytes (115.0 GB)`

[`STATEMENTS_SUMMARY`](/statement-summary-tables.md):表 `statements_summary`、`statements_summary_history`、`cluster_statements_summary`、`cluster_statements_summary_history` 新增字段以下字段:

- `AVG_MEM_ARBITRATION`:平均 SQL 等待内存资源耗时
- `MAX_MEM_ARBITRATION`:最大 SQL 等待内存资源耗时

### 实践

默认系统变量 [`tidb_server_memory_limit`](/system-variables.md#tidb_server_memory_limit-从-v640-版本开始引入) 较小,启用先订阅后分配模式后建议设置为 `95%`。

部署单节点 TiDB 场景

- 开启 `priority` 模式:绑定 OLTP 相关或重要 SQL 到高优先级,按需绑定其他 SQL 到中或低优先级
- 开启 `standard` 模式:遇到 `8180` 错误需等待并重试 SQL

部署多节点 TiDB 场景

- [1] 开启 `standard` 模式
- 遇到 `8180` 错误则重试 SQL 到其他 TiDB 节点

- [2] 开启 `priority` 模式
- 为 OLTP 相关或重要 SQL 绑定高优先级
- 按需绑定其他 SQL 到中/低优先级
- 通过 [`max_execution_time`](/system-variables.md#max_execution_time) 限制 SQL 最大执行时间
- 遇到超时或 `8180` 错误则重试 SQL 到其他 TiDB 节点
- 可通过设置 [`tidb_mem_arbitrator_wait_averse`](/system-variables.md#tidb_mem_arbitrator_wait_averse-从-v900-版本开始引入) 使 SQL 尽快重试到内存资源充足的节点

保障重要 SQL 执行

- 通过 [`tidb_mem_arbitrator_query_reserved`](/system-variables.md#tidb_mem_arbitrator_query_reserved-从-v900-版本开始引入) 保障 SQL 的内存资源隔离性
- 设置 [`tidb_mem_quota_query`](/system-variables.md#tidb_mem_quota_query) 为较大值以免 SQL 执行被中断
- 保底方案为设置 [`tidb_mem_arbitrator_wait_averse`](/system-variables.md#tidb_mem_arbitrator_wait_averse-从-v900-版本开始引入) 为 `nolimit` 并承担内存风险

## 其它

### 设置环境变量 `GOMEMLIMIT` 缓解 OOM 问题
Expand Down
65 changes: 65 additions & 0 deletions system-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -3328,6 +3328,71 @@ v5.0 后,用户仍可以单独修改以上系统变量(会有废弃警告)
- 单位:线程
- TiFlash 中 request 执行的最大并发度。默认值为 `-1`,表示该系统变量无效,此时最大并发度取决于 TiFlash 配置项 `profiles.default.max_threads` 的设置。`0` 表示由 TiFlash 系统自动设置该值。

### `tidb_mem_arbitrator_mode` <span class="version-mark">从 v9.0.0 版本开始引入</span>

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

- 作用域:GLOBAL
- 是否持久化到集群:是
- 是否受 Hint [SET_VAR](/optimizer-hints.md#set_varvar_namevar_value) 控制:否
- 类型:枚举型
- 默认值:`disable`
- 可选值:`disable`,`standard`, `priority`
- 该变量用于设置 TiDB 实例的全局内存管理模式,详见 [TiDB 内存控制](/configure-memory-usage.md#全局内存管理架构):
- `disable` 为默认模式,[控制行为同上](/system-variables.md#tidb_server_memory_limit-从-v640-版本开始引入):内存资源先使用后上报
- `standard` 和 `priority` 为先订阅后分配模式:
- `standard`:SQL 订阅内存资源失败后终止执行
- `priority`:TiDB 根据 SQL 的[资源组优先级](/information-schema/information-schema-resource-groups.md) 处理内存资源订阅任务

### `tidb_mem_arbitrator_query_reserved` <span class="version-mark">从 v9.0.0 版本开始引入</span>

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

- 作用域:SESSION
- 是否受 Hint [SET_VAR](/optimizer-hints.md#set_varvar_namevar_value) 控制:是
- 类型:整数型
- 默认值:`0`
- 单位:字节
- 范围:`[0, 9223372036854775807]`
- 当通过 [`tidb_mem_arbitrator_mode`](/system-variables.md#tidb_mem_arbitrator_mode-从-v900-版本开始引入) 启用先订阅后分配的全局内存管理模式后,该变量设置 SQL 执行前预先订阅指定数量的内存资源份额,详见 [TiDB 内存控制](/configure-memory-usage.md#全局内存管理架构)。

### `tidb_mem_arbitrator_soft_limit` <span class="version-mark">从 v9.0.0 版本开始引入</span>

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

- 作用域:GLOBAL
- 是否持久化到集群:是
- 是否受 Hint [SET_VAR](/optimizer-hints.md#set_varvar_namevar_value) 控制:否
- 类型:字符串
- 默认值:`0`
- 可选值:`0`,浮点数 `(0, 1]`,整数 `(1, 9223372036854775807]`
- 当通过 [`tidb_mem_arbitrator_mode`](/system-variables.md#tidb_mem_arbitrator_mode-从-v900-版本开始引入) 启用先订阅后分配的全局内存管理模式后,该变量设置 TiDB 实例的内存资源份额上限,详见 [TiDB 内存控制](/configure-memory-usage.md#全局内存管理架构)。
- `0`:默认资源份额上限为 [`tidb_server_memory_limit`](/system-variables.md#tidb_server_memory_limit-从-v640-版本开始引入) 的值的 `95%`
- 浮点数 `(0, 1]`:指定比率,资源份额上限为 `tidb_mem_arbitrator_soft_limit *` [`tidb_server_memory_limit`](/system-variables.md#tidb_server_memory_limit-从-v640-版本开始引入)
- 整数 `(1, 9223372036854775807]`:指定字节数

### `tidb_mem_arbitrator_wait_averse` <span class="version-mark">从 v9.0.0 版本开始引入</span>

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

- 作用域:SESSION
- 是否受 Hint [SET_VAR](/optimizer-hints.md#set_varvar_namevar_value) 控制:否
- 类型:枚举型
- 默认值:`0`
- 可选值:`0`,`1`,`nolimit`
- 当通过 [`tidb_mem_arbitrator_mode`](/system-variables.md#tidb_mem_arbitrator_mode-从-v900-版本开始引入) 启用先订阅后分配的全局内存管理模式后,该变量控制 SQL 避免阻塞式等待内存资源的相关属性,详见 [TiDB 内存控制](/configure-memory-usage.md#全局内存管理架构)。
- `0`:无相关属性
- `1`:`priority` 模式下,SQL 订阅内存资源时自动绑定高优先级,全局内存资源不足时就执行取消 (Cancel) 操作而非等待
- `nolimit`:SQL 使用内存资源不受限制,可能导致 TiDB 实例内存风险

### `tidb_mem_oom_action` <span class="version-mark">从 v6.1.0 版本开始引入</span>

- 作用域:GLOBAL
Expand Down