ITPub博客

首页 > 数据库 > NewSQL > tikv oom排查过程

tikv oom排查过程

原创 NewSQL 作者:贺子_DBA时代 时间:2022-08-07 17:32:13 0 删除 编辑

一、问题描述:

tikv实例OOM,并且所有的tikv实例内存使用率非常高,超过90%天天报警;参数都是默认的!,并且在看grafana可以看到tikv节点有oom现象!因为tikv oom后很快自动就重启了(因为tikv服务配置中设置:Restart = always 是指进程或服务意外故障的时候可以自动重启的模式。),所以报警没有报出来!

二、具体我的排查过程

1、查看Block cache 配置是否不合理

2、Raftstore Apply 速度跟不上 Raft Log 接收速度

3、coprocessor 响应过大引起的 OOM

4、网络问题或 gRPC 线程被打满导致 Coprocessor 响应堆积

5、协程 (Coroutine) 泄漏导致 OOM

6、需要 Apply 的日志过多导致 OOM

三、具体排查过程

1、查看Block cache 配置是否不合理

首先说下RocksDB 的内存占用:

为了提高读取性能以及减少对磁盘的读取,RocksDB 将存储在磁盘上的文件都按照一定大小切分成 block(默认是 64KB),读取 block 时先去内存中的 BlockCache 中查看该块数据是否存在,存在的话则可以直接从内存中读取而不必访问磁盘。

BlockCache 按照 LRU 算法淘汰低频访问的数据,TiKV 默认将系统总内存大小的 45% 用于 BlockCache,用户也可以自行修改storage.block-cache.capacity 配置设置为合适的值,但是不建议超过系统总内存的 60%。

写入 RocksDB 中的数据会写入 MemTable,当一个 MemTable 的大小超过 128MB 时,会切换到一个新的 MemTable 来提供写入。TiKV 中一共有 2 个 RocksDB 实例,合计 4 个 ColumnFamily(疑问不应该是六个吗),每个 ColumnFamily 的单个 MemTable 大小限制是 128MB,最多允许 5 个 MemTable 存在,否则会阻塞前台写入,因此这部分占用的内存最多为 4 x 5 x 128MB = 2.5GB。这部分占用内存较少,不建议用户自行更改。

storage.block-cache.shared:是否开启共享 block cache。默认值:true,RocksDB 多个 CF 之间共享 block cache 的配置选项。当开启时,为每个 CF 单独配置的 block cache 将无效。

storage.block-cache.capacity :共享 block cache 的大小。默认值:系统总内存大小的 45%,单位:KB|MB|GB

storage CF (all RocksDB column families) 内存自适应,配置 storage.block-cache.capacity 参数即可实现 CF 之间自动平衡内存使用。

storage.block-cache 默认开启 CF 自适应,无需修改。----rocksdb中的各个CF共享storage.block-cache.capacity设置的大小

storage.block-cache.shared: true

计算公式如下:

storage.block-cache.capacity = (MEM_TOTAL * 0.5 / TiKV 实例数量)

原理 默认情况下 TiKV 的 Block cache 会占用服务器上 45% 的内存,是否存在配置不合理,或节点上存在多个 TiKV 时,可能会由于 Block cache 过大而导致 OOM。

问题诊断和处理 检查各个节点 Block cache 大小的配置,单个节点上所有 TiKV Block cache 总和不宜超过 45% 的总内存

服务器内存是32G, 我们配置的是默认值,32*45%=14.4G,说明配置的是合理的;

具体如下所示:

mysql> show config where type='tikv' and name like 'storage.block-cache.capacity';

+------+---------------------+------------------------------+-------+

| Type | Instance | Name | Value |

+------+---------------------+------------------------------+-------+

| tikv | 10.100.40.117:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.116:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.115:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.113:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.118:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.114:20160 | storage.block-cache.capacity | 14.4GiB |

+------+---------------------+------------------------------+-------+

6 rows in set (0.02 sec)

这里需要注意如果你配置了tikv如下限制的话,那么此时tikv的storage.block-cache.capacity=25*45%=11.2G

global:

resource_control:

memory_limit: 25G

还需要注意这个配置只能设置到 global 或者具体的tikv_servers节点上,不能设置到server_configs中tikv模块---感觉有点别扭,因为如果你有1000个tikv需要设置这个memory_limit限制的时候,并且只想限制tikv节点的内存使用,这个时候你就不能设置global了,如果不能设置在server_configs中tikv模块,那就只能在各个tikv节点来执行了?那也太麻烦了!

小结:我设置的storage.block-cache.capacity参数是合理;

2、Raftstore Apply 速度跟不上 Raft Log 接收速度

如下图所示,tidb 数据写入的流程

committed 的 raft log 会由 store 线程通过一个 unbounded channel,如果 apply 卡住(可能由于 write stall 等等问题),channel 可能会占用大量内存

问题诊断和处理 首先通过 By instance 的监控 TiKV-Details -> Rocksdb-KV -> Stall reasons 确认是否存在 write stall:

如果有 write stall,可根据监控上的 stall 原因来调节相关 RocksDB 参数

如果没有 write stall,则考虑增加 apply 线程数,加快其消费 Raft Log 的速度

具体查看,没有write stall发生,如果cpu没有瓶颈,可以增加 apply 线程数,具体是raftstore.apply-pool-size的tikv配置文件参数,

小结:通过查看TiKV-Details ->Thread-CPU 下的 Raft store CPU,参考值:低于 raftstore.store-pool-size * 85%, 因为我的当时的raftstore.store-pool-size为默认值2,所以Raft store CPU应该小于2*85%=170%, 如下图所示符合这个规则,所以该参数可以不用调整! 也就是说明不是Raftstore Apply 速度跟不上 Raft Log 接收速度!

3、coprocessor 响应过大引起的 OOM

原理 TiKV 中 gRPC 内部的缓存没有大小限制,某些读取数据量比较大的 coprocessor 请求(比如扫表请求),可能会由于结果太大导致占用大量内存

问题诊断和处理 通常是由于大 region 或聚合查询的中间结果太大所导致:

快速的处理方法是先通过 TiKV 上的 coprocessor slow-query 日志(关键字 slow-query)找到读取数据量大、处理时间长的 query 并 kill 掉;

可通过 TiKV-Details -> Server -> Approximate Region size 或 pd 的 region 信息观察是否有大 region,暂停其上的 query 并使用 pd-ctl 强制分裂大 region;

可调节 TiKV 的 server.grpc-memory-pool-quota 参数来限制 gRPC 使用的总内存大小(超过这个大小可能会导致卡顿)

4、网络问题或 gRPC 线程被打满导致 Coprocessor 响应堆积

原理 在 TiKV 中 gRPC 内部的缓存没有大小限制,如果出现生成速度大于发送速度的情况,或者网络存在问题导致发包堆积,从而导致 OOM;Coprocessor接受大量大查询,返回的数据量很大,gRPC的发送速度跟不上Coprocessor往外输出的速度,导致数据堆积在tikv里面,内存溢出

问题诊断和处理:

1)判断gRPC的发送速度跟不上Coprocessor往外输出的速度;

TiKV Detail -> Coprocessor Overview -> Total Response Size ,该监控表示 所有 tikv 上 coprocessor 向上层返回 response 的大小。Node_exporter -> Network -> Network IN/OUT Traffice -> Outbound 指标,使用该监控检查每个 tikv 节点的出站流量。如果所有 TiKV 的出站流量之和比 response 速度小很多则说明 coprocessor 数据有积压。

如果是这个问题:

1、可以尝试找到慢sql,优化sql;

2、同时可以尝试增大server.grpc-concurrenc来提高grpc的并发度,同时可以考虑降低comprocessor的并发度(一般不建议)

readpool.coprocessor.high-concurrency

readpool.coprocessor.normal-concurrency

readpool.coprocessor.low-concurrency

3、可调节 TiKV 的 server.grpc-memory-pool-quota 参数来限制 gRPC 使用的总内存大小(超过这个大小会导致一些网络连接被强制中断)

2)观察 TiKV gRPC 线程是否出现瓶颈,考虑增加 gRPC 线程数;

TiKV Detail -> Thread CPU -> gRPC poll CPU,检查 gRPC 线程是否繁忙。

gRPC poll CPU:gRPC 线程的 CPU 使用率,通常应低于 80% * server.grpc-concurrency,如果高于这个值,可以尝试增到server.grpc-concurrenc的参数,

5、协程 (Coroutine) 泄漏导致 OOM

原理 如果出现由于硬件故障或者内核 bug 导致 TiKV 卡死,但网路服务还正常的情况下,协程可能会出现堆积,从而导致 OOM。

问题诊断和处理 该问题的复现概率非常低,按照现有监控不好排查。只影响版本 v5.1.0,v5.1.1,v5.2.0,v5.2.1 问题已在 v5.1.2 & v5.2.2 修复。

6、需要 Apply 的日志过多导致 OOM

原理 如果 TiKV Apply 速度过慢或者重启导致堆积了大量的需要 Apply 的日志时,就会导致 Log Entry 占用大量的 Entry Cache 资源

问题诊断和处理 v5.1 之前的版本尚无法从现有监控信息中判定根因。 v5.1 版本新增了针对 Entry Cache 使用情况的监控指标,可以通过 TiKV-Details->Server->Memory trace 来确认 Entry Cache 大小是否过高。

处理:可以调整下apply-pool-size的大小

三、最终处理办法:

1)再global 这里添加所有组件的最大使用内存数,重启生效!注意修改该参数后,所有的组件都会被加到服务的配置文件中了,例如tikv服务会写到/etc/systemd/system/tikv-20160.service服务配置文件中!

global:

resource_control:

memory_limit: 25G

2)配置tikv的grpc最大内存使用限制,提高grpc的线程数,提高apply raft的线程数,

server_configs:

tikv:

raftstore.apply-pool-size: 4

server.grpc-concurrency: 8

server.grpc-memory-pool-quota: 12884901888

关于参数server.grpc-concurrency

gRPC 线程池的大小默认配置(server.grpc-concurrency)是 5。由于 gRPC 线程池几乎不会有多少计算开销,它主要负责网络 IO、反序列化请求,因此该配置通常不需要调整。

如果部署的机器 CPU 核数特别少(小于等于 8),可以考虑将该配置(server.grpc-concurrency)设置为 2。

如果机器配置很高,并且 TiKV 承担了非常大量的读写请求,观察到 Grafana 上的监控 Thread CPU 的 gRPC poll CPU 的数值超过了 server.grpc-concurrency 大小的 80%,那么可以考虑适当调大server.grpc-concurrency 以控制该线程池使用率在 80% 以下(即 Grafana 上的指标低于 80% * server.grpc-concurrency的值)

mysql> show config where type='tikv' and name like '%server.grpc-memory-pool-quota%';

限制每个组件的最大使用内存数!注意resource_control是写近了tikv-20160.service这个服务配置文件中了(重启才能生效), 他不是写进tikv配置文件

vim /etc/systemd/system/tikv-20160.service [Unit] Description=tikv service After=syslog.target network.target remote-fs.target nss-lookup.target [Service] MemoryLimit=2G ###resource_control memory_limit: 2G 配置对应的参数 LimitNOFILE=1000000 LimitSTACK=10485760 User=tidb ExecStart=/data/tidb/tidb-deploy/tikv-20161/scripts/run_tikv.sh Restart=always RestartSec=15s [Install] WantedBy=multi-user.target

注意这个配置只能设置到 global 或者具体的tikv_servers节点上,不能设置到server_configs中tikv模块统一设置

global 配置示例:

global: user: "tidb" resource_control: memory_limit: "2G"

上述配置指定使用 tidb 用户启动集群,同时限制每个组件运行时最多只能使用 2GB 内存。

如果配置到了server_configs中tikv模块,在尝试reload的时候报错了,无法生成tikv的配置文件,然后没有重启tikv节点

[root@B-JS25-BASE79-00 pd-2379]# tiup cluster reload tidb-test -R tikv

tiup is checking updates for component cluster ...

A new version of cluster is available:

The latest version: v1.10.2

Local installed version: v1.9.4

Update current component: tiup update cluster

Update all components: tiup update --all

Starting component `cluster`: /root/.tiup/components/cluster/v1.9.4/tiup-cluster /root/.tiup/components/cluster/v1.9.4/tiup-cluster reload tidb-test -R tikv

Will reload the cluster tidb-test with restart policy is true, nodes: , roles: tikv.

Do you want to continue? [y/N]:(default=N) y

+ [ Serial ] - SSHKeySet: privateKey=/root/.tiup/storage/cluster/clusters/tidb-test/ssh/id_rsa, publicKey=/root/.tiup/storage/cluster/clusters/tidb-test/ssh/id_rsa.pub

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [ Serial ] - UpdateTopology: cluster=tidb-test

+ Refresh instance configs

- Generate config pd -> 172.16.197.167:2379 ... Done

- Generate config tikv -> 172.16.197.167:20160 ... Error

- Generate config tikv -> 172.16.197.167:20161 ... Error

- Generate config tikv -> 172.16.197.167:20162 ... Error

- Generate config tidb -> 172.16.197.167:4000 ... Done

- Generate config tidb -> 172.16.197.167:4001 ... Done

- Generate config tiflash -> 172.16.197.167:9000 ... Done

- Generate config prometheus -> 172.16.197.167:9090 ... Done

- Generate config grafana -> 172.16.197.167:3000 ... Done

Error: init config failed: 172.16.197.167:20162: executor.ssh.execute_failed: Failed to execute command over SSH for 'tidb@172.16.197.167:22' {ssh_stderr: unknown configuration options: resource_control

, ssh_stdout: , ssh_command: export; PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin /data/tidb/tidb-deploy/tikv-20162/bin/tikv-server --config-check --config=/data/tidb/tidb-deploy/tikv-20162/conf/tikv.toml --pd "" --data-dir "/data/tidb/tidb-data/tikv-20162"}, cause: Process exited with status 1: check config failed

Verbose debug logs has been written to /root/.tiup/logs/tiup-cluster-debug-2022-07-14-13-23-01.log.

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29654823/viewspace-2909337/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
10年数据库管理员从业经历,熟悉Oracle、mysql、tidb、oceanbase、redis、 mongodb 、python等技术栈,持有11g ocm 、mysql 5.7 ocp 、tidb 5.0 ptcp、tidb 6.0 ptca、oceanbase obcp等

注册时间:2014-05-12

  • 博文量
    327
  • 访问量
    2151609