0
4
6
1
专栏/.../

【SOP】最佳实践之 TiDB OOM 分析

 Jellybean  发表于  2023-11-19

OOM.png

大家可能经历过这样一个场景,在某个正在熟睡的凌晨突然收到告警电话有 tidb-server 节点频繁重启,可能还伴随有内存告警,然后赶紧起来排查确认。上线一看发现不少节点出现了 OOM ,类似上图,在找到对应的 SQL 和业务一确认后,确定是凌晨偷偷新上的统计任务导致了这次的问题,赶紧让业务先停掉完,告知业务完成优化且经过 DBA 确认后再另外排期上线,之后骂骂咧咧的再次滚回被窝去。这个案例起码至少说明了几个问题:

  • 开发能力和开发规范遵循问题
  • 上线流程控制问题
  • 数据库自身资源管控问题

在问题出现后数据库管理员总结排查方法,还可以针对性的做好开发手册指引和培训工作。

而我们在 TiDB 的使用过程中,尤其是访问大量数据的分析型业务场景,很难不会遇到 OOM 的问题。OOM 的问题一旦出现,集群必然会受到影响,所以,这就要求我们能够以尽快的速度完成排查和处理。本文这里总结一些日常管理 TiDB 集群的经验,方便后面遇到类似问题时可以作为一个排查思路和操作指引,协助快速处理 OOM 问题。

是不是OOM问题?

有收到内存告警、节点卡住或重启的情况,我们可以在脑海中有个初判会不会是 OOM 问题。TiDB 集群 OOM 通常出现在 tidb-server 节点上,也有少数情况出现在 tikv-server 节点。大致可以先通过下面的分析流程进行排查,确认是不是 OOM 的问题。

image.png

如上图所示,我们遇到问题后可以有下面的分析来确认是不是具体组件出现了 OOM 。

是不是 tidb server OOM 问题

  • 业务侧、用户端:

    • 部分访问有明显延迟 
    • 部分业务有lost connection 的报错
  • grafana监控:

    • uptime出现掉线重启 
    • Overview->TiDB->Memory Usage 出现高峰急剧掉线重启
  • tidb server日志:

    • tidb.log 在问题时段出现Welcome 重启关键字 
    • tidb_stderr.log有oom或can not allocate memory关键字
  • 系统日志

    • tidb server节点系统日志 dmesg -T| grep tidb-server 在问题时段附近有OOM Killer出现

image.png

(图片来源https://tidb.net/blog/de9bf174

是不是 tikv server OOM 问题

  • 业务侧、用户端:

    • 集群访问延迟有突增
    • 业务QPS有明显抖动
  • grafana监控

    • tikv detail->cluster->uptime出现掉线重启
    • tikv detail->cluster->memory面板看到有tikv实例的内存剧烈上升后突然掉零再慢慢增加,可判断为tikv OOM重启了
  • tikv server日志

    • 排查tikv.log日志,在问题时段附近有Welcome实例重启标记
  • 系统日志

    • dmesg -T| grep tikv-server在问题时段有OOM-killer日志

OOM原因是什么

当遇到OOM的问题之后,排查触发原因时,大致可以分为两类原因:

  • 数据库原因导致的 OOM
  • 非数据库原因导致的OOM

数据库本身导致的 OOM 问题,通常是统计信息不准确导致执行计划问题、集群配置参数使用不当等引起情况。非数据库原因导致的 OOM 问题,通常可以归类为硬件资源不足、混合部署等原因,比如其他应用进程抢占了过多的内存资源导致 TiDB 被系统的 oom-killer 直接干掉。

OOM如何处理

针对不同的 OOM 触发根因,我们会有不同的处理策略。

如何处理tidb server的OOM问题

非数据库原因

  • 机器内存不足

  • 集群混部问题

    • 排查是不是部署过多的实例节点,如果节点个数太多也容易引起 OOM。生成环境下内存 128 GB 的机器,通常建议部署 2 个 tidb server 节点就够了。可以考虑通过增加cgroup策略来进行资源划分。
    • 排查tidb server 是不是和 tikv server 混合部署,如果是也有可能会有内存相互挤兑从而触发问题。可以考虑通过增加cgroup策略来进行资源划分。
    • 确认是否有业务进程混合部署,导致内存资源挤兑。应该专机专用,不部署业务程序。

数据库原因

如果发现内存正在上涨还没有出现 OOM 但是也有 OOM 的风险,可以根据下面的方式获取相关信息来分析:

  • 查看当前链接的内存使用情况

    • 执行 SELECT * FROM information_schema.processlist; 查看 SQL 对应的 MEM 列的值,定位到使用内存最多的语句。
  • 执行以下命令收集内存使用率高的 tidb server 的 Profile 信息

    • curl -G http://{TiDBIP}:10080/debug/zip?seconds=10" > tidb_server_profile.zip

原因判断和处理:

  • 问题 SQL 定位

    • 获取可能的问题 SQL,可以查看 INFORMATION_SCHEMA 中的 SLOW_QUERY 和 CLUSTER_SLOW_QUERY 。
    • 也可以通过 tidb dashboard的 慢SQL和 SQL语句找到问题附近的 SQL并分析,定位到具体的问题 SQL,从 SQL 语句分析、慢查询的具体内容,可以详细查看内存使用量。
    • 同时 grep expensive_query tidb.log 辅助定位问题时间点的 SQL 情况。
  • 如果确认是 SQL 的执行计划有问题,如缺少合适索引、统计信息过期、优化器 bug 等原因,很可能会导致选错执行计划而导致大量中间结果数据累积在内存,出现 OOM。

    • 可以添加合适索引、使用 analyze 重新收集统计信息等措施进行处理。
  • 如果确认 OOM 时是系统在收集和加载统计信息的过程中消耗太多内存。

    • 可以指定采样率、指定只收集特定列的统计信息、减少 ANALYZE 并发度等方式减少内存使用。
    •  v6.1.0 以后引入了系统变量 tidb_stats_cache_mem_quota对统计信息的内存使用进行限制,引入了系统变量 tidb_mem_quota_analyze控制 TiDB 更新统计信息时的最大总内存占用。
  • 如果OOM 问题发生时间附近,tidb session 的并发度过高或者某些节点的连接数过多。

    • 需要扩容或优化负载均衡策略。
  • 查看执行计划,如果发现存在算子 HashAgg 处理大量数据。

    • HashAgg 是多线程并发执行,虽然执行速度较快,但会消耗较多内存,可以尝试使用 STREAM_AGG() 替代。
  • 如果出现 OOM 问题时段附近,业务访问有大事务或大写入,导致消耗太多内存。

    • tidb server 在执行事务时 TiDB 进程的内存消耗相对于事务大小会存在一定程度的放大,最大可能达到提交事务大小的 2 到 3 倍以上。
    • 所以针对单个大事务,可以通过拆分的方式调小事务,可能需要和业务沟通调整程序。
  • 排查和调整 tidb server 的配置参数

    • 设置单条 SQL 使用的内存上限。

      • 可以调整 session 级别参数 tidb_memory_quota_query 参数限制单条SQL语句的内存使用量,默认值为 1GB
    • 设置单个 tidb-server 节点使用的内存上限。

      • v6.5.0以后可以通过系统变量 tidb_server_memory_limit 设置 tidb-server 实例的内存使用阈值,默认为总内存的80%。
      • TiDB 会在内存用量达到该限制时对当前内存用量最高的 SQL 语句 Cancel,然后会尝试调用 Golang GC 立刻回收内存,以最快速度缓解内存压力。
      • 一次只能 Cancel 一条 SQL 语句。如果 TiDB 完全 Cancel 掉一条 SQL 语句并回收资源后,内存使用仍然大于该变量所设限制,TiDB 会开始下一次 Cancel 操作。被强制终止的 SQL 操作会向客户端返回报错信息 Out Of Memory Quota!。
    • 使用系统变量 tidb_mem_oom_action 来控制单条查询超过内存限制后所采取的操作是 CANCEL 还是 LOG。

      • CANCEL 则会中断查询并打印相关日志。
      • LOG 则不会中断查询,仅仅记录相关内存使用信息。
    • 设置数据落盘

      • HashAgg 落盘功能目前不支持 distinct 聚合函数。使用 distinct 函数且内存占用过大时,无法进行落盘。
      • 当 SQL 的内存使用超过 Memory Quota 时,tidb-server 可以通过落盘执行算子的中间数据,缓解内存压力。可以启用临时磁盘 oom_tmp_storage ,指定路径tmp_storage_path 和使用外部磁盘的空间上限tmp_storage_size。
      • 当包含 HashAgg 算子的 SQL 语句引起内存 OOM 时,TiDB 默认不触发落盘,可以通过设置系统变量 tidb_executor_concurrency = 1 来触发 HashAgg 落盘的功能。

如何处理tikv server的OOM问题

非数据库原因

  • 机器内存不足

  • 集群混部问题

    • 排查是不是部署过多的实例节点,如果节点个数太多也容易引起 OOM。可以考虑通过增加cgroup策略来进行资源划分。
    • 排查 tikv server 是不是和 tidb server 混合部署,如果是也有可能会有内存相互挤兑从而触发 OOM 问题。可以考虑通过增加cgroup策略来进行资源划分。
    • 确认是否有业务进程混合部署,导致内存资源挤兑。其他进程占用过多内存,也可能会导致系统误杀 tikv 进程。应该专机专用,不部署业务程序。

数据库原因

  • 排查 block cache 参数配置是否合理。通过查看 TiKV Detail  -> RocksDB KV -> Block cache size 面板查看内存使用变化。

    • 如果是block cache的问题,则合理调整storage.block-cache.capacity参数大小,默认为系统内存的45%,通常可以设置范围为45%-60%,配置过高导致容易OOM。如果是多个节点同时部署的时候,尤其要注意该参数的配置,可以设置 storage.block-cache.capacity = (MEM_TOTAL * 0.5 / TiKV 实例数量)。

    • 在 v6 以后的版本支持在MySQL终端在线修改该参数set config tikv xxxx=xxxx (推荐);或者tiup cluster edit-confg xx 编辑修改再reload重启tikv实例。

image.png

  • 确认是否因为读取大量数据导致 OOM 。查看 tikv details 的copprocessor 面板,以及查看 node-exporter的network面板查看 gRPC的网络传输量,确认是不是远远小于copprocessor读取的数据量。

    • 如果是 copprocessor 读取大量数据而gRPC来不及消费传送到TiDB server,说明有大 SQL 出现导致 copprocessor 读取大量数据到 tikv 的 block cache 中而 gRPC 来不及消费传送到 tidb,会导致数据堆积过多而 OOM。此时可以采取下面的处理措施:

      • 优化SQL使得一次性不读取大量数据。
      • 如果机器使用的是千兆网卡,则建议升级为万兆网卡。

对于 TiKV OOM 的分析,社区里 h5n1 大佬的这篇文章也非常推荐阅读 https://tidb.net/blog/ab528ebf

总结

TiDB 出现 OOM 问题,最频繁的节点是在 tidb server 中,而引起问题的最多原因是因为大 SQL 或执行计划不准。

而 tikv 出现 OOM 的问题,大多数是因为 block cache 配置问题引起,在部署和排查时可以重点关注该方面问题。

我们在日常运维管理工作中,针对这些场景有一定的提前规划和准备,即可从容应对大部分的 OOM 问题。

社区也有不少很不错的 OOM 排查实践案例,在遇到问题时可以一并参考。

0
4
6
1

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论