[转帖]TiDB 内存控制文档
https://docs.pingcap.com/zh/tidb/stable/configure-memory-usage
目前 TiDB 已经能够做到追踪单条 SQL 查询过程中的内存使用情况,当内存使用超过一定阈值后也能采取一些操作来预防 OOM 或者排查 OOM 原因。你可以使用系统变量 tidb_mem_oom_action 来控制查询超过内存限制后所采取的操作:
- 如果变量值为
LOG,那么当一条 SQL 的内存使用超过一定阈值(由 session 变量tidb_mem_quota_query控制)后,这条 SQL 会继续执行,但 TiDB 会在 log 文件中打印一条 LOG。 - 如果变量值为
CANCEL,那么当一条 SQL 的内存使用超过一定阈值后,TiDB 会立即中断这条 SQL 的执行,并给客户端返回一个错误,错误信息中会详细写明在这条 SQL 执行过程中占用内存的各个物理执行算子的内存使用情况。
如何配置一条 SQL 执行过程中的内存使用阈值
使用系统变量 tidb_mem_quota_query 来配置一条 SQL 执行过程中的内存使用阈值,单位为字节。例如:
配置整条 SQL 的内存使用阈值为 8GB:
配置整条 SQL 的内存使用阈值为 8MB:
配置整条 SQL 的内存使用阈值为 8KB:
如何配置 tidb-server 实例使用内存的阈值
自 v6.5.0 版本起,可以通过系统变量 tidb_server_memory_limit 设置 tidb-server 实例的内存使用阈值。
例如,配置 tidb-server 实例的内存使用总量,将其设置成为 32 GB:
设置该变量后,当 tidb-server 实例的内存用量达到 32 GB 时,TiDB 会依次终止正在执行的 SQL 操作中内存用量最大的 SQL 操作,直至 tidb-server 实例内存使用下降到 32 GB 以下。被强制终止的 SQL 操作会向客户端返回报错信息 Out Of Memory Quota!。
当前 tidb_server_memory_limit 所设的内存限制不终止以下 SQL 操作:
- DDL 操作
- 包含窗口函数和公共表表达式的 SQL 操作
- TiDB 在启动过程中不保证
tidb_server_memory_limit限制生效。如果操作系统的空闲内存不足,TiDB 仍有可能出现 OOM。你需要确保 TiDB 实例有足够的可用内存。 - 在内存控制过程中,TiDB 的整体内存使用量可能会略微超过
tidb_server_memory_limit的限制。 server-memory-quota配置项自 v6.5.0 起被废弃。为了保证兼容性,在升级到 v6.5.0 或更高版本的集群后,tidb_server_memory_limit会继承配置项server-memory-quota的值。如果集群在升级至 v6.5.0 或更高版本前没有配置server-memory-quota,tidb_server_memory_limit会使用默认值,即80%。
在 tidb-server 实例内存用量到达总内存的一定比例时(比例由系统变量 tidb_server_memory_limit_gc_trigger 控制), tidb-server 会尝试主动触发一次 Golang GC 以缓解内存压力。为了避免实例内存在阈值上下范围不断波动导致频繁 GC 进而带来的性能问题,该 GC 方式 1 分钟最多只会触发 1 次。
在混合部署的情况下,tidb_server_memory_limit 为单个 tidb-server 实例的内存使用阈值,而不是整个物理机的总内存阈值。
使用 INFORMATION_SCHEMA 系统表查看当前 tidb-server 的内存用量
要查看当前实例或集群的内存使用情况,你可以查询系统表 INFORMATION_SCHEMA.(CLUSTER_)MEMORY_USAGE。
要查看本实例或集群中内存相关的操作和执行依据,可以查询系统表 INFORMATION_SCHEMA.(CLUSTER_)MEMORY_USAGE_OPS_HISTORY。对于每个实例,该表保留最近 50 条记录。
tidb-server 内存占用过高时的报警
当 tidb-server 实例的内存使用量超过内存阈值(默认为总内存量的 70%)且满足以下任一条件时,TiDB 将记录相关状态文件,并打印报警日志。
- 第一次内存使用量超过内存阈值。
- 内存使用量超过内存阈值,且距离上一次报警超过 60 秒。
- 内存使用量超过内存阈值,且
(本次内存使用量 - 上次报警时内存使用量) / 总内存量 > 10%。
你可以通过系统变量 tidb_memory_usage_alarm_ratio 修改触发该报警的内存使用比率,从而控制内存报警的阈值。
当触发 tidb-server 内存占用过高的报警时,TiDB 的报警行为如下:
TiDB 将以下信息记录到 TiDB 日志文件
filename所在目录中。- 当前正在执行的所有 SQL 语句中内存使用最高的 10 条语句和运行时间最长的 10 条语句的相关信息
- goroutine 栈信息
- 堆内存使用状态
TiDB 将输出一条包含关键字
tidb-server has the risk of OOM以及以下内存相关系统变量的日志。
为避免报警时产生的状态文件累积过多,目前 TiDB 默认只保留最近 5 次报警时所生成的状态文件。你可以通过配置系统变量 tidb_memory_usage_alarm_keep_record_num 调整该次数。
下例通过构造一个占用大量内存的 SQL 语句触发报警,对该报警功能进行演示:
配置报警比例为
0.85:SET GLOBAL tidb_memory_usage_alarm_ratio = 0.85;创建单表
CREATE TABLE t(a int);并插入 1000 行数据。执行
select * from t t1 join t t2 join t t3 order by t1.a。该 SQL 语句会输出 1000000000 条记录,占用巨大的内存,进而触发报警。检查
tidb.log文件,其中会记录系统总内存、系统当前内存使用量、tidb-server 实例的内存使用量以及状态文件所在目录。[2022/10/11 16:39:02.281 +08:00] [WARN] [memoryusagealarm.go:212] ["tidb-server has the risk of OOM because of memory usage exceeds alarm ratio. Running SQLs and heap profile will be recorded in record path"] ["is tidb_server_memory_limit set"=false] ["system memory total"=33682427904] ["system memory usage"=22120655360] ["tidb-server memory usage"=21468556992] [memory-usage-alarm-ratio=0.85] ["record path"=/tiup/deploy/tidb-4000/log/oom_record]以上 Log 字段的含义如下:
is tidb_server_memory_limit set:表示系统变量tidb_server_memory_limit是否被设置system memory total:表示当前系统的总内存system memory usage:表示当前系统的内存使用量tidb-server memory usage:表示 tidb-server 实例的内存使用量memory-usage-alarm-ratio:表示系统变量tidb_memory_usage_alarm_ratio的值record path:表示状态文件存放的目录
通过访问状态文件所在目录(该示例中的目录为
/tiup/deploy/tidb-4000/log/oom_record),可以看到标记了记录时间的 record 目录(例:record2022-10-09T17:18:38+08:00),其中包括goroutinue、heap、running_sql3 个文件,文件以记录状态文件的时间为后缀。这 3 个文件分别用来记录报警时的 goroutine 栈信息,堆内存使用状态,及正在运行的 SQL 信息。其中running_sql文件内容请参考expensive-queries。
tidb-server 其它内存控制策略
流量控制
- TiDB 支持对读数据算子的动态内存控制功能。读数据的算子默认启用
tidb_distsql_scan_concurrency所允许的最大线程数来读取数据。当单条 SQL 语句的内存使用每超过tidb_mem_quota_query一次,读数据的算子就会停止一个线程。 - 流控行为由参数
tidb_enable_rate_limit_action控制。 - 当流控被触发时,会在日志中打印一条包含关键字
memory exceeds quota, destroy one token now的日志。
数据落盘
TiDB 支持对执行算子的数据落盘功能。当 SQL 的内存使用超过 Memory Quota 时,tidb-server 可以通过落盘执行算子的中间数据,缓解内存压力。支持落盘的算子有:Sort、MergeJoin、HashJoin、HashAgg。
- 落盘行为由参数
tidb_mem_quota_query、tidb_enable_tmp_storage_on_oom、tmp-storage-path、tmp-storage-quota共同控制。 - 当落盘被触发时,TiDB 会在日志中打印一条包含关键字
memory exceeds quota, spill to disk now或memory exceeds quota, set aggregate mode to spill-mode的日志。 - Sort、MergeJoin、HashJoin 落盘是从 v4.0.0 版本开始引入的,HashAgg 落盘是从 v5.2.0 版本开始引入的。
- 当包含 Sort、MergeJoin 或 HashJoin 的 SQL 语句引起内存 OOM 时,TiDB 默认会触发落盘。当包含 HashAgg 算子的 SQL 语句引起内存 OOM 时,TiDB 默认不触发落盘,请设置系统变量
tidb_executor_concurrency = 1来触发 HashAgg 落盘功能。
- HashAgg 落盘功能目前不支持 distinct 聚合函数。使用 distinct 函数且内存占用过大时,无法进行落盘。
本示例通过构造一个占用大量内存的 SQL 语句,对 HashAgg 落盘功能进行演示:
将 SQL 语句的 Memory Quota 配置为 1GB(默认 1GB):
SET tidb_mem_quota_query = 1 << 30;创建单表
CREATE TABLE t(a int);并插入 256 行不同的数据。尝试执行以下 SQL 语句:
[tidb]> explain analyze select /*+ HASH_AGG() */ count(*) from t t1 join t t2 join t t3 group by t1.a, t2.a, t3.a;该 SQL 语句占用大量内存,返回 Out of Memory Quota 错误。
ERROR 1105 (HY000): Out Of Memory Quota![conn_id=3]设置系统变量
tidb_executor_concurrency将执行器的并发度调整为 1。在此配置下,内存不足时 HashAgg 会自动尝试触发落盘。SET tidb_executor_concurrency = 1;执行相同的 SQL 语句,不再返回错误,可以执行成功。从详细的执行计划可以看出,HashAgg 使用了 600MB 的硬盘空间。
[tidb]> explain analyze select /*+ HASH_AGG() */ count(*) from t t1 join t t2 join t t3 group by t1.a, t2.a, t3.a;+---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+ | id | estRows | actRows | task | access object | execution info | operator info | memory | disk | +---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+ | HashAgg_11 | 204.80 | 16777216 | root | | time:1m37.4s, loops:16385 | group by:test.t.a, test.t.a, test.t.a, funcs:count(1)->Column#7 | 1.13 GB | 600.0 MB | | └─HashJoin_12 | 16777216.00 | 16777216 | root | | time:21.5s, loops:16385, build_hash_table:{total:267.2µs, fetch:228.9µs, build:38.2µs}, probe:{concurrency:1, total:35s, max:35s, probe:35s, fetch:962.2µs} | CARTESIAN inner join | 8.23 KB | 4 KB | | ├─TableReader_21(Build) | 256.00 | 256 | root | | time:87.2µs, loops:2, cop_task: {num: 1, max: 150µs, proc_keys: 0, rpc_num: 1, rpc_time: 145.1µs, copr_cache_hit_ratio: 0.00} | data:TableFullScan_20 | 885 Bytes | N/A | | │ └─TableFullScan_20 | 256.00 | 256 | cop[tikv] | table:t3 | tikv_task:{time:23.2µs, loops:256} | keep order:false, stats:pseudo | N/A | N/A | | └─HashJoin_14(Probe) | 65536.00 | 65536 | root | | time:728.1µs, loops:65, build_hash_table:{total:307.5µs, fetch:277.6µs, build:29.9µs}, probe:{concurrency:1, total:34.3s, max:34.3s, probe:34.3s, fetch:278µs} | CARTESIAN inner join | 8.23 KB | 4 KB | | ├─TableReader_19(Build) | 256.00 | 256 | root | | time:126.2µs, loops:2, cop_task: {num: 1, max: 308.4µs, proc_keys: 0, rpc_num: 1, rpc_time: 295.3µs, copr_cache_hit_ratio: 0.00} | data:TableFullScan_18 | 885 Bytes | N/A | | │ └─TableFullScan_18 | 256.00 | 256 | cop[tikv] | table:t2 | tikv_task:{time:79.2µs, loops:256} | keep order:false, stats:pseudo | N/A | N/A | | └─TableReader_17(Probe) | 256.00 | 256 | root | | time:211.1µs, loops:2, cop_task: {num: 1, max: 295.5µs, proc_keys: 0, rpc_num: 1, rpc_time: 279.7µs, copr_cache_hit_ratio: 0.00} | data:TableFullScan_16 | 885 Bytes | N/A | | └─TableFullScan_16 | 256.00 | 256 | cop[tikv] | table:t1 | tikv_task:{time:71.4µs, loops:256} | keep order:false, stats:pseudo | N/A | N/A | +---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+ 9 rows in set (1 min 37.428 sec)
其它
设置环境变量 GOMEMLIMIT 缓解 OOM 问题
Golang 自 Go 1.19 版本开始引入 GOMEMLIMIT 环境变量,该变量用来设置触发 Go GC 的内存上限。
对于 v6.1.3 <= TiDB < v6.5.0 的版本,你可以通过手动设置 Go GOMEMLIMIT 环境变量的方式来缓解一类 OOM 问题。该类 OOM 问题具有一个典型特征:观察 Grafana 监控,OOM 前的时刻,TiDB-Runtime > Memory Usage 面板中 estimate-inuse 立柱部分在整个立柱中仅仅占一半。如下图所示:

为了验证 GOMEMLIMIT 在该类场景下的效果,以下通过一个对比实验进行说明:
在 TiDB v6.1.2 下,模拟负载在持续运行几分钟后,TiDB server 会发生 OOM(系统内存约 48 GiB):

在 TiDB v6.1.3 下,设置
GOMEMLIMIT为 40000 MiB,模拟负载长期稳定运行、TiDB server 未发生 OOM 且进程最高内存用量稳定在 40.8 GiB 左右:
[转帖]TiDB 内存控制文档的更多相关文章
- FreeRTOS内存管理文档
heap1.c:只能申请内存,不能释放内存.适合运行后不申请新内存的程序. heap2.c: 既能申请内存,也能释放内存,但释放内存后,相邻的空余内存不能合并.适合每次申请相同大小内存的变量的程序使用 ...
- sharepoint 2013 文档库eventhandle权限控制
记录一下如何在sharepoint server 2013文档库中,使用eventhandle控制文档库document library的条目item权限. ///<summary> // ...
- 在asp.net core2.1中添加中间件以扩展Swashbuckle.AspNetCore3.0支持简单的文档访问权限控制
Swashbuckle.AspNetCore3.0 介绍 一个使用 ASP.NET Core 构建的 API 的 Swagger 工具.直接从您的路由,控制器和模型生成漂亮的 API 文档,包括用于探 ...
- 段合并 segments merge 被删除的文档的删除时间
2.5 段合并 每个索引分为多个“写一次,读多次”的段 write once and read many times segments 建立索引时,一个段写入磁盘以后就不能更新:被删除的文档的信息存 ...
- DOM和SAX是应用中操纵XML文档的差别
查看原文:http://www.ibloger.net/article/205.html DOM和SAX是应用中操纵XML文档的两种主要API.它们分别解释例如以下: DOM.即Do ...
- vxWidgets(二):接口文档
第一章 介绍 在这一章中,我们会回答这样一些基本的问题:wxWidgets是什么,它和别的类似的开发库有什么不同.我们还会大概说一下这个项目的历史,以及wxWidgets社区的工作,它采用的许可协议, ...
- 四种生成和解析XML文档的方法详解(介绍+优缺点比较+示例)
众所周知,现在解析XML的方法越来越多,但主流的方法也就四种,即:DOM.SAX.JDOM和DOM4J 下面首先给出这四种方法的jar包下载地址 DOM:在现在的Java JDK里都自带了,在xml- ...
- JCarouselLite--帮助文档
jcarousellite是一款jquery插件,可以控制文档元素滚动,丰富的参数设置可以控制滚动的更多细节,是一款不可多得的滚动插件. ------------------ 官网地址:http:// ...
- Indri中的动态文档索引技术
Indri中的动态文档索引技术 戴维 译 摘要: Indri 动态文档索引的实现技术,支持在更新索引的同时处理用户在线查询请求. 文本搜索引擎曾被设计为针对固定的文档集合进行查询,对不少应用来说,这种 ...
- 浅谈用java解析xml文档(三)
接上一篇,本文介绍使用JDOM解析xml文档, 首先我们还是应该知道JDOM从何而来,是Breet Mclaughlin和Jason Hunter两大Java高手的创作成果,2000年初, JDOM作 ...
随机推荐
- P4928 [MtOI2018]衣服?身外之物! 题解
题意 gcd 共有 \(n\) 件衣服,编号为 \(A_1,A_2,\cdots A_n\). 每一件衣服分别拥有颜色值和清洗时间,他在每一件衣服穿完以后都会将其送去清洗,而这件衣服当天所拥有的舒适感 ...
- STM32CubeMX教程13 ADC - 单通道转换
1.准备材料 开发板(正点原子stm32f407探索者开发板V2.4) ST-LINK/V2驱动 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK ...
- 斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 01.基础AI与行为树
斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 前言&摘要 本篇文章是基于斯坦福UE4 C++课程的学习记录.因为B站用户surkea由于学业原因,暂停了课程 ...
- three.js中帧缓存的使用
目录 1. 概述 2. 示例 2.1. 代码 2.2. 解析 3. 参考 1. 概述 在网上查阅了一下three.js关于帧缓存的使用,感觉很多都是关于three.js中后处理通道的使用的.后处理通道 ...
- 华为云API Explorer重磅推出API编排,开发者0代码高效构建工作流
本文分享自华为云社区<华为云API Explorer重磅推出API编排,开发者0代码高效构建工作流(体验用户招募中)>,作者:华为云PaaS服务小智. 打破传统开发模式,API编排应运而生 ...
- DWS轻量化更新黑科技:宽表加工优化
本文分享自华为云社区<GaussDB(DWS)性能调优:宽表加工优化方案>,作者:譡里个檔 . 1. 业务背景 宽表加工性能慢,在Gauss(DWS)中可以使用DWS的轻量化更新的黑科技实 ...
- 云小课|MRS基础原理之Oozie任务调度
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:Oozie是一个基 ...
- Docker cp 将宿主机上的文件复制到容器中
[root@localhost ~]# docker cp /opt/web/docker_cp.txt tomcat9093:/usr/local/apache-tomcat-9.0.31/ [ro ...
- django基本流程
创建项目 django-admin startproject web cd web python manage.py startapp weblist 生成迁移文件 python manage.py ...
- let、var、const区别
1.var:传统的变量声明方式 在ES5及之前的JavaScript版本中,我们通常使用var关键字声明变量.var具有以下特点: 函数作用域:变量的作用域限制在声明的函数内部,如果在函数外部访问,将 ...