GP 生产问题:FATAL: DTM Initialization failure during recovery startup, cdbtm.c:1513
问题描述:
XX局点现网 GP 集群 X 月 X 号登录时报错,提示如下信息: FATAL: DTM Initialization failure during recovery startup, cdbtm.c:1513 。运维同事提示:重启 GP 能暂时解决,但是过一段时间还是会出现此问题。进一步咨询运维同事得知,故障发生的频率较频繁,大约1~2周就发生一次。GP 所在机房经常掉电,机器也肯定重启过。按照规范,机器防火墙都是关闭的。提示运维同事使用 gpstate -e 命令查看发现无 segment 节点故障,使用 gpstate -a 查看也无问题(运维人员应该已经重启过集群,所以此时查不出集群的故障)。
问题分析:
集群当前状态应该是正常的,所以只能从日志中获取一些信息。
首先咨询运维同事,了解一下 GP 集群的配置:6台机器均为 256 GB内存,swap 也有将近100 GB。
通过 select * from gp_segment_configuration 查询得知集群的拓扑结构是: 6 台机器(node1 ~ node6)均为 segment 节点,其中每台 segment 节点部署有 8 个 primary segment 实例,且未配置 mirror。此外,master 节点和 node6 上的 segment 实例合设,且未配置 standby master 实例。同时也获取了各实例的端口号信息。这里可知,由于 无 mirror, 集群中任意 segment 实例出问题,集群都会不可用。由于 node6 是集群内负载相对最重的(部署有一个 master 实例,8 个 segment 实例),因此咨询运维人员 node6 上有没有部署其他特别耗内存的应用,得到的回答是,除了 GP 还部署有 zookeeper, kafka, scala, spark, 关联任务,任务服务等。猜测这台节点的内存争用还是比较厉害的。使用 free -g 查询,发现当前内存还有较多富余, buffer 还有 228 GB,未用到 swap。
通过谷歌和百度,相关报错信息有可能是机器重启后防火墙没有关闭导致的,但是运维人员查询后确认防火墙全部关闭,排除这种可能。
请运维人员帮忙获取一些日志:首先是 master 节点 的 pg 日志($GPDATA/master/gpseg-1/pg_log), 搜索定位 cbdtm.c: 1513 相关的错误所在的具体日志报错,获取了问题大概发生在 7 月9 号凌晨 2:03 , 函数抛出的堆栈信息可以获得的信息有限,但是 cbdtm.c: 1513 这一条日志报错信息上下文却可以获取到一些重要的其他信息: “LOG:could not connect to server: connection refused (seg1 xx.xx.65.9: 40001 , cbdtm.c: 1491 ”, 且多次出现该信息。可以推测,很有可能是 xx.xx.65.9 上的 seg1 节点 -- 它的端口号是 40001-- 起不来,导致集群整体无法使用.
由于 xx.xx.65.9 这台机器正好是 node6 , 也就是 master 合设的机器。要求运维人员进一步查询 node6 的 seg1 的 pg 日志,在生产问题发生时刻点附近没有查到 PG 的报错信息,但是有大量的 LOG 级别的 "incomplete startup packet" 信息。
综合上面的分析,推测 GP 集群的内核参数相关设置需要优化,由于不恰当的内核参数设置导致个别 semgent 节点起不来,使得集群状态出现异常。进一步查询内核参数相关信息:
-
首先查询数据库内核参数:
通过 gpconfig -s shared_buffers 查询结果可知,master 和 segment 实例的 shared_buffers 都被设为 20GB,这是一个过大的值,一般来说不需要设置这么大。由于 node6 上实际有 9 个 PG 实例,光是 shared_buffers 就占去了 180GB,相对总 RAM 256 GB,推测这个值有点大了。
通过 gpconfig -s gp_vmem_protect_limit 查询,master 和 segment 上都设为了 8192, 也就是 8 GB,这个值大小正常,此参数限定单个 segment 实例在所有查询中能用到的最大总内存。node6 上所有 segment 实例加起来最多到 64 GB,大小差不多。当然这个参数的设置在 GP 官方是有公式计算推荐值的。大约算了一下 ,按照官方的计算方式,结合 node6 上占内存应该不止 GP 一个的实际,GP 占用总量大约 200 GB 的内存,给其他应用预留一部分内存 + swap, gp_vmem_protect_limit 计算下来应该在 10 GB 左右,所以这个值设为 8GB 基本合理。设置过大很有可能导致 OOM。
-
接着查询 OS 内核参数,GP 的内核参数最重要的有下面几个,都是配置在 /etc/sysctl.conf 中的:
kernel.shmmax (有推荐计算公式,大约是系统总 RAM 大小的一半,对应于系统总页数一半的内存大小,单位 Byte)
kernel.shmall (有推荐计算公式, 大约是系统总页数的一半)
vm.overcommit_memory (默认 95)
vm.overcommit_ratio (官方建议设置为 2 ,防止 OOM)
这四个参数查询下来发现:
kernel.shmmax 配的是 50000000000
kernel.shmall 配的是 40000000000
后两者没有配置。
这里,kernel.shmmax 显然配置太小,按照官方手册,kernel.shmmax 不应该低于一台机器上总 segment 数 * shared_buffers 的配置,按照当前 shared_buffers 是 20GB 的配置,这个值不能低于 160GB,而当前 50 GB都不到。再加上 node6 上还有 master 节点,所以这个值应该比 160 GB 更大,才能保证集群能够起来,且各节点不会 OOM。
-
-
问题的原因大概定位:
- 内核参数 kernel.shmmax 设置过小,kernel.shmall 设置和 kernel.shmmax 不匹配,shared_buffers 设置过大,导致集群有 OOM 的风险。
原因总结:
内核参数 kernel.shmmax 设置过小,kernel.shmall 设置和 kernel.shmmax 不匹配,shared_buffers 设置过大,导致集群有 OOM 的风险。需要对内核参数的取值进行优化,防止问题的再次产生。
解决对策:
-
重新计算 kernel.shmmax 和 kernel.shmall 的值,配置好 vm.overcommit_memory 和 vm.overcommit_ratio, 避免 OOM 的产生。
按照官方的推荐值,将 kernel.shmmax 设置为 128GB 对应的 Byte 值,同时 kernel.shmall 设置为 128 GB 对应的 OS 页面数(前者除以 4096)。同时配置好另外两个参数。所有节点全部修改 sysctl.conf, 并 sysctl -p 生效。
-
调低 shared_buffers
原来的 20 GB 的配置过大,首先使用 gp_config 将其调整到 15 GB,使用 gpstop -r -a 尝试重启集群。
发现集群重启失败,master 节点无法启动。故要求将 kernel.shmmax 和 kernel.shmall 的值调整为原来的 2 倍,尝试重启集群。此时发现master 节点启动成功,但是仍有一个 Segment 节点无法起来。
说明 shared_buffers 还是太大,故首先将 master 节点单独启动( gpstart -m ), 使用 gpconfig 将 shared_buffers 调整至 8 GB。停止 master 节点( gpstop -M fast)。再次尝试重启整个集群(gpstart -a) ,集群重启成功。
总结
调整了内核参数,将原来过小的 OS 内核参数 kernel.shmmax 和 kernel.shmall 的值调大,并将 GP 的 shared_buffers 调小,使得 GP 启动时能够从操作系统分配到足够多的内存,避免启动失败或者即使启动成功而后续因为内存分配不足导致 OOM 使得实例崩溃。