TiDB OOM 故障排查

    OOM 常见的故障现象包括(但不限于):

    • 客户端报错:

    • 查看 Grafana 监控,发现以下现象:

      • TiDB > Server > Memory Usage 显示 process/heapInUse 持续升高,达到阈值后掉零
      • TiDB > Server > Uptime 显示为掉零
      • TiDB-Runtime > Memory Usage 显示 estimate-inuse 持续升高
    • 查看 tidb.log,可发现如下日志条目:

      • OOM 相关的 Alarm:[WARN] [memory_usage_alarm.go:139] ["tidb-server has the risk of OOM. Running SQLs and heap profile will be recorded in record path"]。关于该日志的详细说明,请参考 。
      • 重启相关的日志条目:[INFO] [printer.go:33] ["Welcome to TiDB."]

    在排查 OOM 问题时,整体遵循以下排查思路:

    1. 确认是否属于 OOM 问题。

      执行以下命令查看操作系统日志。如果故障发生的时间点附近存在 oom-killer 的日志,则可以确定是 OOM 问题。

      下面是包含 oom-killer 的日志输出示例:

    2. 确认是 OOM 问题之后,可以进一步排查触发 OOM 的原因是部署问题还是数据库问题。

      • 如果是部署问题触发 OOM,需要排查资源配置、混合部署的影响。
      • 如果是数据库问题触发 OOM,常见原因有:
        • TiDB 处理较大的数据流量,如:大查询、大写入、数据导入等。
        • TiDB 的高并发场景,多条 SQL 并发消耗资源,或者算子并发高。
        • TiDB 内存泄露,资源没有释放。

      具体排查方法请参考下面的章节。

    根据 OOM 出现的原因,一般可以分为以下几种情况:

    • 操作系统内存容量规划偏小,导致内存不足。
    • TiUP 配置不合理。
    • 在混合部署的情况下(指 TiDB 和其他应用程序部署在同一台服务器上),其他应用程序抢占资源导致 TiDB 被 oom-killer 关闭。

    本节介绍由数据库问题导致的 OOM 问题和解决办法。

    注意

    如果 SQL 返回 ERROR 1105 (HY000): Out Of Memory Quota![conn_id=54],是由于配置了 导致,数据库的内存使用控制行为会触发该报错。此报错为正常行为。

    执行 SQL 语句时消耗太多内存

    可以根据以下不同的触发 OOM 的原因,采取相应的措施减少 SQL 的内存使用:

    • 如果 SQL 的执行计划不优,比如由于缺少合适的索引、统计信息过期、优化器 bug 等原因,会导致选错 SQL 的执行计划,进而出现巨大的中间结果集累积在内存中。这种情况下可以考虑采取以下措施:

      • 添加合适的索引
      • 使用
      • 调整表之间的 JOIN 顺序
      • 使用 hint 进行调优
    • 一些算子和函数不支持下推到存储层,导致出现巨大的中间结果集累积。此时可能需要改写业务 SQL,或使用 hint 进行调优,来使用可下推的函数或算子。

    • 执行计划中存在算子 HashAgg。HashAgg 是多线程并发执行,虽然执行速度较快,但会消耗较多内存。可以尝试使用 STREAM_AGG() 替代。

    • 调小同时读取的 Region 的数量,或降低算子并发度,以避免因高并发导致的内存问题。对应的系统变量包括:

    • 问题发生时间附近,session 的并发度过高,此时可能需要添加节点进行扩容。

    大事务或大写入消耗太多内存

    需要提前进行内存的容量规划,这是因为执行事务时 TiDB 进程的内存消耗相对于事务大小会存在一定程度的放大,最大可能达到提交事务大小的 2 到 3 倍以上。

    针对单个大事务,可以通过拆分的方式调小事务大小。

    收集和加载统计信息的过程中消耗太多内存

    TiDB 节点启动后需要加载统计信息到内存中。统计信息的收集过程会消耗内存,可以通过以下方式控制内存使用量:

    • 使用指定采样率、指定只收集特定列的统计信息、减少 ANALYZE 并发度等手段减少内存使用。
    • TiDB v6.1.0 开始引入了系统变量 ,可以对统计信息的内存使用进行限制。
    • TiDB v6.1.0 开始引入了系统变量 tidb_mem_quota_analyze,用于控制 TiDB 更新统计信息时的最大总内存占用。

    预处理语句 (Prepared Statement) 使用过量

    客户端不断创建预处理语句但未执行 deallocate prepare stmt 会导致内存持续上涨,最终触发 TiDB OOM。原因是预处理语句占用的内存要在 session 关闭后才会释放。这一点在长连接下尤需注意。

    要解决该问题,可以考虑采取以下措施:

    系统变量配置不当

    系统变量 tidb_enable_rate_limit_action 在单条查询仅涉及读数据的情况下,对内存控制效果较好。若还存在额外的计算操作(如连接、聚合等),启动该变量可能会导致内存不受 控制,加剧 OOM 风险。

    建议关闭该变量。从 TiDB v6.3.0 开始,该变量默认关闭。

    若客户端发生 OOM,则需要排查以下方面:

    • 观察 Grafana TiDB Details > Server > Client Data Traffic 的趋势和速度,查看是否存在网络阻塞。
    • 检查是否存在错误的 JDBC 配置参数导致的应用 OOM。例如流式读取的相关参数 defaultFetchSize 配置有误,会造成数据在客户端大量缓存。

    为定位 OOM 故障,通常需要收集以下信息:

    • 操作系统的内存相关配置:

      • TiUP 上的配置:
      • 操作系统的配置:
        • 相关内核参数:vm.overcommit_memory
      • NUMA 相关信息:
        • numactl --hardware
        • numactl --show
    • 数据库的版本和内存相关配置:

      • TiDB 版本
      • tidb_mem_quota_query
      • memory-usage-alarm-ratio
      • mem-quota-query
      • oom-action
      • tidb_enable_rate_limit_action
      • tidb_server_memory_limit
      • tmp-storage-path
      • tmp-storage-quota
      • tidb_analyze_version
    • 在 Grafana 查看 TiDB 内存的日常使用情况:TiDB > Server > Memory Usage

    • 查看内存消耗较多的 SQL 语句:

      • 可以从 TiDB Dashboard 中查看 SQL 语句分析、慢查询,查看内存使用量
      • 查看 INFORMATION_SCHEMA 中的 SLOW_QUERYCLUSTER_SLOW_QUERY
      • 各个 TiDB 节点的 tidb_slow_query.log
      • 执行 grep "expensive_query" tidb.log 查看对应的日志条目
      • 执行 EXPLAIN ANALYZE 查看算子的内存消耗
      • 执行 SELECT * FROM information_schema.processlist; 查看 SQL 对应的 列的值
    • 执行以下命令收集内存使用率高的时候 TiDB 的 Profile 信息: