GaussDB 运维案例集
目 录
1 DN动态内存使用满导致DN主备切换
2 iptables策略限制导致实例升级报错
3 修改bashrc导致om-agent升级失败
4 目录权限问题导致DN异常
5 2025-11
5.1 业务SQL包含自定义function,通过hint方式添加query_dop,smp不生效
5.2 JDBC导入和gsql导入在insert,replace,duplicate方式性能差异
5.3 主备切换时间长问题分析
5.4 主机流控导致大量业务超时并且无法新建连接
5.5 业务分布键选择不合理导致数据分布不均匀
5.6 DN发生主备切换问题定位
1、DN动态内存使用满导致DN主备切换
- 故障现象
客户反馈,在05:00--10:31之间GaussDB集中式在DN主备切换。
- 适用版本
GaussDB全部版本。
- 告警
DN主备切换。
- 业务影响
业务闪断,1min内恢复。
- 故障原因
l 主备切换问题,通常先找到主备切换的节点,即原主DN,新主DN分别是哪个DN。
l 然后先从cm_server主、cm_ctl等日志分析,确认主备切换的命令是由cm_server下发还是手动下发。
l 最后如果是cm_server下发主备切换的命令,通常是原主DN异常,则分析原主DN 异常原因。
- 处理办法
步骤 1 确认DN主备切换时间点。
查看cm_server主日志,搜索关键词“Failover message has sent to instance”,从日志中发现,2024-04-23 05:02:34,cm_server下发命令,将dn_6002切换为主DN。
cd $GAUSSLOG/cm/cm_server
vim cm_server-yyyy-mm-dd_******-current.log

继续查看cm_server主日志,搜索关键词“send switchover to instance”,发现在2024-04-23 10:31:21,手工下发了switchover命令,将dn_6001切换为主。

经以上分析,2024-04-23 05:02:34因为dn_6001的某些故障,cm_server将主DN切换为dn_6002,2024-04-23 10:31:21手动下发switchover命令将dn_6001切换为主。步骤 2 定位发生主备切换原因。
查看DN进程,发现进程在05:02发生重启。
ps ux|grep dn

步骤 3 查看是否生成ffic日志。
进入日志目录,发现存在ffic日志。
cd $GAUSSLOG/ffic_log
步骤 4 分析ffic日志。
查看ffic_log日志,发现dn_6001因为发生oom,被AI Watchdog杀掉进程,从而触发主备切换。

步骤 5 查看dn_6001日志。
搜索关键词,AI Watchdog,发现是因为动态内存上涨,触发AI Watchdog主备切换。
cd $GAUSSLOG/pg_log/dn_6001
vim postgresql-yyyy-mm-dd_******.log

步骤 6 查看动态内存使用情况。
查看监控指标,动态内存使用率瞬间冲高。
步骤 7 查看dn_6001动态内存上下文根。
发现hashBatchContext上下文根动态内存使用高,判断可能为大的查询,其中的hash关联占用动态内存高。
cd $GAUSSLOG/mem_log/dn_6001_6002_6003

步骤 8 查找动态内存占用高的SQL语句。
发现占用动态内存高的SQL语句为insert into语句,该语句存在大量的关联查询,在执行该语句时发生申请内存失败,报错信息为:memory is temporarily unavailable。
cd $GAUSSLOG/pg_log/dn_6001
zgrep -i debug_query_id postgresql-yyyy-mm-dd_******.log.zg | awk -F 'consuming about' '{print $2}'|sort -nk1


步骤 9 优化insert into语句。
----结束
2、iptables策略限制导致实例升级报错
- 故障现象
客户进行实例升级,任务流报错,升级失败,如下图所示。


- 适用版本
GaussDB全部版本。
- 业务影响
l 管控任务流报错,无影响。
l 内核任务流报错,自动回滚成功,无业务影响,运维功能、备份可能会受影响。
l 内核任务流报错,自动回滚失败,集群状态可能异常,影响业务。
- 故障原因
根据报错任务流时间,147秒报错,说明升级任务流可能还没有下发内核,可能在om_agent前置校验阶段报错。
- 处理方法
步骤 1 根据升级报错任务流,找到命令下发节点。如下图,单击“操作 > 修改context”,找到“NODE_ID”。

步骤 2 根据NODE_ID,找到对应节点,登录到节点后台。
步骤 3 切换到Ruby用户,加载环境变量,执行如下命令,查看升级日志。
ll $GAUSSLOG/om/gs_upgradectl_2025*.log

步骤 4 发现节点上没有产生内核升级日志,说明升级动作还没有到内核侧,返回查看om_agent的日志。
步骤 5 执行如下命令,查看om_agent的升级日志。
vim /home/Ruby/log/om_agent/agent.log
步骤 6 搜索报错时间点前后有没有“ERROR、failed”等报错信息。

步骤 7 根据报错日志分析,检查数据库连接失败:Check database connection failed。根据报错日志“could not connect to server: Operation now in progress Is the server running on host "127.0.0.1" and accepting TCP/IP connections on port 8000”分析,怀疑是gsql连接数据库报错。
步骤 8 在数据库节点执行如上命令,指定-h 127.0.0.1不通,不指定可以连上,怀疑是127.0.0.1不通。
l 指定-h 127.0.0.1

l 不指定-h 127.0.0.1

步骤 9 执行如下命令,测试127.0.0.1的连通性,连接失败。
curl -kv 127.0.0.1 8000

步骤 10 通过如下命令检查数据库连通性,是,指定-h 127.0.0.1。
gsql -d postgres -p $port -h 127.0.0.1 -U rdsAdmin -W $password -m -c 'select 1;'
步骤 11 127.0.0.1不通,怀疑是设置了iptable策略,root用户下执行如下命令查看iptable策略。
iptables -L

步骤 12 如上图,iptables策略存在一条策略限制了127.0.0.1访问。
步骤 13 执行如下命令删除限制127.0.0.1的策略。
iptables -D INPUT -p all -s 127.0.0.1/24 -j REJECT

步骤 14 执行如下命令,测试127.0.0.1的连通性,连接成功。
curl -kv 127.0.0.1 8000

步骤 15 通过gsql连接,也可以连接成功。

步骤 16 管控界面重试任务流,升级成功。


----结束
3、修改bashrc导致om-agent升级失败
- 故障现象
通过TPOPS平台,升级GaussDB实例,升级报错,查看升级任务流,升级子任务om-agent失败。

- 适用版本
GaussDB全部版本。
- 业务影响
升级失败。
- 故障原因
实例节点上,/home/Ruby/.bashrc 该文件为环境配置文件,该文件在实例安装成功后手动添加了 source ~/gauss_env_file 环境变量 ,导致升级om_agent时,自动加载了GAUSSHOME,使其获取到了GAUSSHOME/bin目录下的openssl,正常要求使用操作系统自带的openssl来生成客户端和服务端的证书和私钥。因为环境变量加载的的openssl路径不对,所以报错证书和私钥不匹配,无法正常启动om_agent进程,导致升级失败。
- 处理办法步骤 1 查看om_agent日志,提示om_agent拉起失败。
步骤 2 通过 ps -ux 查看 om_agent 进程是否拉起,未拉起,手动尝试拉起om_agent, 提示ssl证书相关错误。

步骤 3 查看.bashrc 环境变量,发现手动添加了 source ~/gauss_env_file。

步骤 4 登录实例每个节点,去掉 .bashrc 中手动添加的环境变量 source \~/gauss_env_file ,然后重做任务流。
----结束
4、目录权限问题导致DN异常
- 故障现象
复现案例:集群升级完后一个DN状态为down。
cm_ctl query -Cv

- 适用版本
GaussDB全部版本。
- 业务影响
所有CN/DN权限异常,会导致数据库无法正常启动。
- 故障原因
升级过程中需要重启DN,DN数据目录由于权限问题无法正常启动。
- 处理办法
步骤 1 查看DN异常节点om_agent、cm_agent、cm_server等守护进程无异常。
ps ux

步骤 2 查看cm_agent进程日志打印有拉起动作但是未正常拉起,排除CMA异常。
cd $GAUSSLOG/cm/cm_agent
vim cm_agent-*current.log
StartAndStop ASYN LOG: BuildStartCommand 0
StartAndStop ASYN LOG: DN START system(command:/usr/local/core/app/bin/gaussdb -D /var/lib/engine/data1/data/dn_6002 -M pending >> "/var/lib/engine/data1/log/Ruby/cm/cm_agent/system_call-current.log" 2>&1 &), try 4
StartAndStop ASYN LOG: the dn(id:6002) instance restarts counts: 66 in 10 min, 66 in hour.
DnStatus6002 ASYN ERROR: [get_connection: 1446]: fail to read pid file (/var/lib/engine/data1/data/dn_6002/postmaster.pid).
DnStatus6002 ASYN ERROR: failed to connect to datanode:/var/lib/engine/data1/data/dn_6002
DnStatus6002 ASYN ERROR: DatanodeStatusCheck failed, ret=-1
DnStatus6002 ASYN LOG: set 6002 on offline.
CheckProcess ASYN LOG: process (gaussdb) is not running, path is [[/var/lib/engine/data1/data/dn_6002]: [/var/lib/engine/data1/data/dn_6002]], haveFound is 0
CoreDumpCheck ASYN LOG: gaussdb state file "/var/lib/engine/data1/data/dn_6002/gaussdb.state" is not exist, could not get the build infomation: No such file or directory
StartAndStop ASYN ERROR: error.dn is 6002 ret=-1
TpNetReportDnGroupInfoMain ASYN LOG: tp net dn report dn group [0] to cms.
DnStatus6002 ASYN ERROR: [get_connection: 1446]: fail to read pid file (/var/lib/engine/data1/data/dn_6002/postmaster.pid).
DnStatus6002 ASYN ERROR: failed to connect to datanode:/var/lib/engine/data1/data/dn_6002
DnStatus6002 ASYN ERROR: DatanodeStatusCheck failed, ret=-1
DnStatus6002 ASYN LOG: set 6002 on offline.

步骤 3 查看system_call日志发现打印数据目录权限不足。
cd $GAUSSLOG/cm/cm_agent
vim system_call-current.log
LOG: assign g_instance_enable_dcf=0
LOG: configuration file "/var/lib/engine/data1/data/dn_6002/gaussdb.conf" contains errors; unaffected changes were applied
CAUSE: Error from config file occurs and unaffected changes were applied.
ACTION: Use correct config file.
FATAL: data directory "/var/lib/engine/data1/data/dn_6002" has group or world access
DETAIL: Permissions should be u=rwx (0700).
CAUSE: The data directory has group or world access.
ACTION: Add the user to the group or world of the data directory.

步骤 4 查看该DN数据目录权限为701异常。

步骤 5 手动将DN数据目录恢复为700权限。

步骤 6 集群状态恢复。

----结束
5、2025-11
5.1 业务SQL包含自定义function,通过hint方式添加query_dop,smp不生效
- 问题现象
业务sql执行时间较长,需要通过smp增加并行提升执行效率,通过hint 方式, 发现query_dop 不生效。以下是sql及对应执行计划、相关表表结构。
执行计划

select表结构

insert表结构

- 技术背景
针对上述SQL案例,smp使用有如下约束条件,如下:
1. 当function为易变函数,smp不生效, 需要将function调整为i函数。
2. 当function不能下推到dn执行,smp不生效,需要将function修改为SHIPPABLE。
3. 当业务表为分区表且创建了global索引,smp 不生效, 需要将业务表修改为local索引。
function创建规则如下
l 不主动指定 IMMUTABLE | STABLE , 默认创建 v 函数 | s 函数,即默认指定 VOLATILE | STABLE。
l 不主动指定 SHIPPABLE,默认指定 NOT SHIPPABLE, 即该函数不下推到dn上执行。
pg_proc 字段说明


验证如下
l 不主动指定 IMMUTABLE | STABLE , 默认创建 v 函数,即默认指定 VOLATILE。
l不主动指定 SHIPPABLE,默认指定 NOT SHIPPABLE, 即该函数不下推到dn上执行。
gaussdb=> CREATE FUNCTION func1 RETURN integer
gaussdb-> AS
gaussdb$> BEGIN
gaussdb$> RETURN 2;
gaussdb$> END;
gaussdb$> /
CREATE FUNCTION
--创建function , 默认是v 函数
gaussdb=> select proname, provolatile, proshippable from pg_proc where proname = 'func1';
proname | provolatile | proshippable
---------+-------------+--------------
func1 | v | f
- 验证案例
1. select 查询结果中使用到了v函数(易变 volatile),smp将不生效,需调整为i (不可变的 immutable) 函数或者s(稳定的 stable)函数。
gaussdb=> CREATE FUNCTION func1 RETURN integer
gaussdb-> AS
gaussdb$> BEGIN
gaussdb$> RETURN 2;
gaussdb$> END;
gaussdb$> /
CREATE FUNCTION
--创建function , 默认是v 函数
gaussdb=> select proname, provolatile, proshippable from pg_proc where proname = 'func1';
proname | provolatile | proshippable
---------+-------------+--------------
func1 | v | f
gaussdb=> create table stu2 (
gaussdb(> id int ,
gaussdb(> sid int,
gaussdb(> sname int
gaussdb(> );
CREATE TABLE
--默认执行计划
gaussdb=> explain select *, func1 AS pk_id from stu2;
id | operation | E-rows | E-width | E-costs
----+----------------------+--------+---------+----------------
1 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..515.700
(1 row)
--smp 不生效
gaussdb=> explain insert /*+ set(query_dop 4) set(enable_force_smp on)*/ into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+-------------------------+--------+---------+----------------
1 | -> Insert on stu1 | 1945 | 12 | 0.000..515.700
2 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..515.700
(2 rows)
--将 func1 修改为 i 函数
gaussdb=> ALTER FUNCTION func1() IMMUTABLE ;
ALTER FUNCTION
gaussdb=> select proname, provolatile, proshippable from pg_proc where proname = 'func1';
proname | provolatile | proshippable
---------+-------------+--------------
func1 | i | f
(1 row)
--smp 生效
gaussdb=> explain select /*+ set(query_dop 4) set(enable_force_smp on)*/ *, func1 AS pk_id from stu2;
id | operation | E-rows | E-width | E-costs
----+--------------------------------------------+--------+---------+---------------
1 | -> Streaming(type: LOCAL GATHER dop: 1/4) | 1945 | 12 | 0.000..11.992
2 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..7.362
(2 rows)
2. insert 使用了自定义函数,当函数为STABLE/VOLATILE类型的函数,且函数的属性是NOT SHIPPABLE的时候(不能下推到dn执行),smp将不生效,需要调整函数属性为SHIPPABLE
gaussdb=> CREATE FUNCTION func2 RETURN integer
gaussdb-> AS
gaussdb$> BEGIN
gaussdb$> RETURN 2;
gaussdb$> END;
gaussdb$> /
CREATE FUNCTION
--创建function , 默认不下推dn执行gaussdb=> select proname, provolatile, proshippable from pg_proc where proname = 'func2';
proname | provolatile | proshippable
---------+-------------+--------------
func2 | v | f
gaussdb=> create table stu1 (
gaussdb(> id int ,
gaussdb(> sid int,
gaussdb(> sname int,
gaussdb(> pk_id int DEFAULT func2()
gaussdb(> );
CREATE TABLE
gaussdb=> create table stu2 (
gaussdb(> id int ,
gaussdb(> sid int,
gaussdb(> sname int
gaussdb(> );
CREATE TABLE
--默认执行计划
gaussdb=> explain insert into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+-------------------------+--------+---------+---------------
1 | -> Insert on stu1 | 1945 | 12 | 0.000..29.450
2 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..29.450
(2 rows)
--smp 不生效
gaussdb=> explain insert /*+ set(query_dop 4) set(enable_force_smp on)*/ into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+-------------------------+--------+---------+----------------
1 | -> Insert on stu1 | 1945 | 12 | 0.000..515.700
2 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..515.700
-- 调整 func2 函数属性为SHIPPABLE
gaussdb=> ALTER FUNCTION func2() SHIPPABLE;
ALTER FUNCTION
gaussdb=> select proname, provolatile, proshippable from pg_proc where proname = 'func2';
proname | provolatile | proshippable
---------+-------------+--------------
func2 | v | t
-- smp 生效
gaussdb=> explain insert /*+ set(query_dop 4) set(enable_force_smp on)*/ into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+--------------------------------------------+--------+---------+----------------
1 | -> Streaming(type: LOCAL GATHER dop: 1/4) | 0 | 0 | 0.000..493.613
2 | -> Insert on stu1 | 1945 | 12 | 0.000..493.613
3 | -> Seq Scan on stu2 | 1945 | 12 | 0.000..493.613
(3 rows)
3. 分区表创建global 索引不支持smp。需要将分区表调整成普通表或者将global索引修改为local索引。
gaussdb=> create table stu1(
gaussdb(> id int ,
gaussdb(> sid int,
gaussdb(> sname int);
CREATE TABLE
gaussdb=> create table stu2 (
gaussdb(> id int ,
gaussdb(> sid int,
gaussdb(> sname int
gaussdb(> ) PARTITION BY RANGE (id) (
gaussdb(> PARTITION p1 VALUES LESS THAN (200),
gaussdb(> PARTITION pmax VALUES LESS THAN (MAXVALUE)
gaussdb(> );
CREATE TABLE
--创建global 索引
gaussdb=> CREATE INDEX idx_st2 ON stu2(id) GLOBAL;
CREATE INDEX
--默认执行计划
gaussdb=> explain insert into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+----------------------------------------+--------+---------+---------------
1 | -> Insert on stu1 | 1945 | 12 | 0.000..29.450
2 | -> Partition Iterator | 1945 | 12 | 0.000..29.450
3 | -> Partitioned Seq Scan on stu2 | 1945 | 12 | 0.000..29.450
(3 rows)
Predicate Information (identified by plan id)
-----------------------------------------------
2 --Partition Iterator
Iterations: 2
3 --Partitioned Seq Scan on stu2
Selected Partitions: 1..2
(4 rows)
--smp 不生效
gaussdb=> explain insert /*+ set(query_dop 4) set(enable_force_smp on)*/ into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+----------------------------------------+--------+---------+---------------
1 | -> Insert on stu1 | 1945 | 12 | 0.000..29.450
2 | -> Partition Iterator | 1945 | 12 | 0.000..29.450
3 | -> Partitioned Seq Scan on stu2 | 1945 | 12 | 0.000..29.450
(3 rows)
Predicate Information (identified by plan id)
-----------------------------------------------
2 --Partition Iterator
Iterations: 2
3 --Partitioned Seq Scan on stu2
Selected Partitions: 1..2
(4 rows)
--修改索引为本地索引
gaussdb=> DROP INDEX idx_st2;
DROP INDEX
gaussdb=>
gaussdb=> CREATE INDEX idx_st2 ON stu2(id) LOCAL;
CREATE INDEX
--smp 生效
gaussdb=> explain insert /*+ set(query_dop 4) set(enable_force_smp on)*/ into stu1 select * from stu2;
id | operation | E-rows | E-width | E-costs
----+--------------------------------------------+--------+---------+--------------
1 | -> Streaming(type: LOCAL GATHER dop: 1/4) | 0 | 0 | 0.000..7.362
2 | -> Insert on stu1 | 1945 | 12 | 0.000..7.362
3 | -> Partition Iterator | 1945 | 12 | 0.000..7.362
4 | -> Partitioned Seq Scan on stu2 | 1945 | 12 | 0.000..7.362
(4 rows)
Predicate Information (identified by plan id)
-----------------------------------------------
3 --Partition Iterator
Iterations: 2
4 --Partitioned Seq Scan on stu2
Selected Partitions: 1..2
(4 rows)
结论
1. select 语句中使用的ks_auth.F_IS_IDNO为易变函数,smp不生效,需要调整为i函数。
2. insert 对应的表TMP_WATCHLIST_CLIENT_PRE使用的函数dsc_ora_ext.dsc_fn_sys_guid() 不能下推到dn执行,smp不生效,需要修改为SHIPPABLE。
3. T_NOR_CLIENT为分区表且创建了global索引,smp 不生效,需要修改为local索引。
5.2 JDBC导入和gsql导入在insert,replace,duplicate方式性能差异
- 问题现象
在insert插入数据,发现jdbc方式比gsql方式性能差。通过jdbc和gsql两种方式,对比以下三种插入数据方式,发现性能确实存在差异。
l insert into XXX;
l replace into XXX;
l insert into XXX on duplicate XXX;
- 案例验证
1. gsql方式插入数据并查看耗时
− 表结构
gaussdb=> create table stu1 (f1 varchar(100),
gaussdb(> f2 varchar(100),
gaussdb(> f3 varchar(100));
CREATE TABLE
− 插入方式
insert into stu1 values (generate_series(1,200000),generate_series(1,200000), random() *100::decimal(5,2));
replace into stu2 values (generate_series(1,200000),generate_series(1,200000), random() *100::decimal(5,2));
insert into stu3 values (generate_series(1,200000), generate_series(1,200000), random() *100::decimal(5,2)) on duplicate key update f2 = generate_series(1,200000) + 1, f3=generate_series(1,200000)+1;
−执行耗时

− 执行计划
insert into XXX

replace into XXX

insert into XXX on duplicate XXX

jdbc方式插入数据并查看耗时
l insert 方式

l replace方式

l insert ...duplicate 方式

l 执行耗时

− 执行计划
insert into XXX 执行计划


− replace into XXX

− insert into XXX on duplicate XXX

- 结果差异说明
l gsql方式:三种方式语义一样,在数据库内部只解析一次,走硬解析,一条语句插入20万数据,所以耗时短。
l jdbc方式:
a. insert into t_test (a,id,name) values(?,?,?) 走一次硬解析,20万次软解析,jdbc侧使用executeBatch接口调用,在数据库内部体现为执行了20万次insert,发送一次P报文,20万次UE报文,一个prepare语句多次调用,执行execute。
b. replace into t_test(id,name,a) values (?,?,?) 使用executeBatch接口批量插入20万数据,向内核发送pue报文,在数据库内部体现为执行了20万次insert,语句走一次硬解析,后边调用uE报文执行20万次,与1相同。replace into语句是先删除后更新,返回影响的结果有两行。

c. insert into t_test (id,name,a) values (?,?,?) on duplicate key update a = (a + ?),使用executeBatch接口批量插入20万数据,在数据库内部体现为执行了20万次insert。jdbc调用executeBatch接口向内核发送PUE报文,内部将此语句识别为upsert语句,此类语句不能走gplan计划复用,所以每次执行都会解析计划,耗时长.duplicate语句是原位更新,只影响一行。

5.3 主备切换时间长问题分析
- 问题描述
客户反馈DN主备切换时间长,需要分析原因
- 分析思路
主备切换时间长,通常是因为各个别DN日志回放未完成导致,可根据日志实际进行分析。
- 分析过程
1. 查看cm_server主日志,确认cm_server下发升主命令的时间以及下发升主的DN。
cd $GAUSSLOG/cm/cm_server
vim cm_server-yyyy-mm-dd_******-current.log
− 2025-06-20 09:31:13.416(cm_server日志时间差8小时),cm_server主收到上报的dn_6001(原主DN)状态异常,可搜索“ERROR”并根据时间点过滤。

− 2025-06-20 09:31:13.475(cm_server日志时间差8小时),cm_server主分别下发Lock1锁命令,对dn_6004,dn_6003,dn_6002进行加锁,可搜索“Lock1 message has sent to instance”关键词。

2.查看dn_6002,dn_6003,dn_6004所在节点的cm_agent日志,确认是否收到cm_server下发的加锁命令。
cd $GAUSSLOG/cm/cm_agent
vim cm_agent-yyyy-mm-dd_******-current.log
− 2025-06-20 09:31:13.478 (cm_agent日志时间差8小时),cm_agent对dn_6004加锁,但加锁失败,可搜索“set Lock1 to instance”关键词。

− 期间对dn_6003,dn_6002也在加锁,也是加锁失败,可搜索“set Lock1 to instance”关键词。


3. cm_agent加锁失败,进一步查看DN日志,确认DN在执行什么操作。
cd $GAUSSLOG/gs_log/dn_6004
vim gaussdb-yyyy-mm-dd_******.log
查看dn_6004日志,2025-06-20 17:31:13.477 dn_6004未完成回放,可根据时间点搜索。

dn_6002、dn_6003在同一时间点也是未完成回放,此处省略截图。
4继续查看DN运行日志,确认dn_6002,dn_6003,dn_6004在什么时间完成回放。
cd $GAUSSLOG/gs_log/dn_6004
vim gaussdb-yyyy-mm-dd_******.log
2025-06-20 17:32:31.517,dn_6004优先回放日志完成,可搜索“redo_done flag is:[true]”关键词。

dn_6002、dn_6003在同一时间点未完成回放,此处省略截图。
5. dn_6004优先完成回放,进一步查看cm_server主日志,确认是否下发升主命令以及下发时间。
cd $GAUSSLOG/cm/cm_server
vim cm_server-yyyy-mm-dd_******-current.log
2025-06-20 09:32:32.492(cm_server日志时间差8小时),cm_server主下发dn_6004升主命令,可搜索“Failover message has sent to instance”关键词。

6. 查看dn_6004节点的cm_agent日志,确认是否收到dn_6004升主命令以及接收时间。
cd $GAUSSLOG/cm/cm_agent
vim cm_agent-yyyy-mm-dd_******-current.log
2025-06-20 09:32:32.492 (cm_agent日志时间差8小时),cm_agent接受cm_server下发的命令,对dn_6004执行升主操作,可搜索“failover msg from cm_server”关键词。

7.查看dn_6004运行日志,确认是否开始升主以及时间点。
cd $GAUSSLOG/gs_log/dn_6004
vim gaussdb-yyyy-mm-dd_******.log
2025-06-20 17:32:32.529,dn_6004开始升主,可搜索“Instance to do failover”关键词。

8. 继续查看dn_6004运行日志,确认升主完成时间点。
cd $GAUSSLOG/gs_log/dn_6004
vim gaussdb-yyyy-mm-dd_******.log
2025-06-20 09:32:36.808,dn_6004开始接受新的连接,可搜索“database system is ready to accept connections”关键词。

9. 查看dn_6004所在节点的cm_agent日志,确认dn_6004解锁时间。
cd $GAUSSLOG/cm/cm_agent
vim cm_agent-yyyy-mm-dd_******-current.log
2025-06-20 09:32:39.510 (cm_agent日志时间差8小时),cm_agent解锁dn_6004成功,可搜索“process_unlock_no_primary_command succeed”关键词。

- 根本原因
综上分析,得出主备切换时间长的原因为:
l dn_6001故障以后,dn_6002,dn_6003,dn_6004日志均与主机存在差距,cm_server分别给dn_6002,dn_6003,dn_6004发下锁DN命令,由于DN回放未完成因此支持锁DN失败,直到2025-06-20 17:32:31.517,dn_6004优先回放完成,因此选择dn_6004升主。
l dn_6004回放耗时:2025-06-20 09:31:13.478--2025-06-20 17:32:31.517,持续78s。
l dn_6004升主耗时:2025-06-20 09:32:32.492--2025-06-20 17:32:39.655,持续7s。
因此,主备切换耗时长主要在于回放时间长,回放时间长是由于未开启流控,DN主备之间的RTO高。在升主时先要完成回放再执行升主操作。
- 解决方案
开启流控:gs_guc reload -Z datanode -N all -I all -c "recovery_time_target=60"
- 补充说明
流控原理说明:
在备机回放日志的能力跟不上主机时,通过在walsender线程中进行一定时间的sleep来降低日志发送速度,让备机来得及回放已接收的日志。也就是当备机接收日志或者回放日志的能力跟不上主机时,通过强制降低主机的业务性能,来保证主备机的日志差异在一定目标范围。
因此,开启流控,在主机业务压力过大时,会限制主机业务。

流控为主机业务压力与RTO两者之间的综合选择,需要根据实际业务特点慎重配置。
GaussDB关键日志目录介绍:
l GaussDB日志路径为$GAUSSLOG目录
l CN、DN运行日志路径:$GAUSSLOG/gs_log
l cm_server运行日志:$GAUSSLOG/cm/cm_server
l cm_agent运行日志:$GAUSSLOG/cm/cm_agent
l 升级、安装日志:$GAUSSLOG/om
5.4 主机流控导致大量业务超时并且无法新建连接
- 问题现象
客户反馈,15:25之前是大量SQL执行超时,从15:25开始无法创建连接。
- 分析思路
l 突然出现SQL超时,可能是因为并发突增和、IO打满、资源打满等,可优先通过监控查看资源使用情况以及并发数。
l 无法新建连接,可能是因为连接数满、连接不释放、中间件连接数满等情况。
- 问题分析
1.获取业务报错信息。
获取业务报错信息,根据业务连接池机制分析以下报错信息,业务连接池连接数为20,当连接被占满并长时间不释放,业务新建连接失败。因此怀疑业务连接池到数据库的连接未及时释放,继续分析数据库相关指标及日志。
2024-06-13 15:25:02 [traceId:40093804-1545-4937-ab7e-5246d06de9b7] [ERROR] [http-nio-8015-exec-227] [com.cmfchina.fis.capital.apply.job.CapitalJobController:72] -- capitalStatusRefresh>>>fail
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 20, maxActive 20, creating 0, createErrorCount 1
2. 查看异常时间段数据库监控指标。
− 首先查看资源使用情况,6月13日15:20分左右,cpu、内存使用率无显著突增,表明资源使用无异常。

− 其次查看会话连接情况,6月13日15:20分左右,在线会话数略有升高,活跃会话数有显著升高,由15增加到35,表明数据库连接数增加。


3. 查看SQL语句分布情况。
dn_6001为主DN,根据SQL语句对比,dn_6001上98%的SQL语句为insert语句。当insert语句占比高的时候,通常可能产生流控,因此继续分析流控情况。

4. 查看流控相关指标。
异常时间段产生主机流控,流控时间达到180000s。

5.查看DN运行日志。
dn_6002,dn_6003均产生流控,同时根据日志,以dn_6002为例,备机接收日志位置为:362E/20A4A000,备机回放位置为:362C/BF72D920,因此表明,备机回放慢导致产生主机流控。

6. 分析gs_asp采样数据以及wdr报告。
查询gs_asp采样数据以及wdr报告,发现主要的等待事件为:LOGCTRL_SLEEP、wait wal sync,该等待事件表明产生主机流控。


7.查找SQL语句。
跟踪WDR报告分析,执行次数最多的两条SQL语句为:3803763747、262602308。

进一步根据unique_sql_id,定位到产生流控的主要SQL语句为:
1、unique_sql_id为3803763747的INSERT INTO cmf_fis.wind_bondanninf语句;
2、unique_sql_id为262602308的INSERT INTO cmf_fis.t_fis_news_port_rel_dm_v4语句。
- 根本原因
综上分析:
由于大量的insert语句,主机产生xlog多,由于备机回放速率赶不上主机xlog产生速率,触发了主机流控,进而导致已有连接不释放,因此业务新建连接超时。
- 解决措施
l 临时解决方案:
通常当产生主机流控,执行以下命令关闭流控,进行应急恢复:
gs_guc reload -Z datanode -N all -I all -c "recovery_time_target=0"
l 长期解决方案:
a. 恢复流控参数阈值为30min。
gs_guc reload -Z datanode -N all -I all -c "recovery_time_target=1800"
b. 开启极致RTO,该参数设置后需要重启集群生效。
gs_guc set -Z datanode -N all -I all -c "recovery_parse_workers=2"
gs_guc set -Z datanode -N all -I all -c "recovery_redo_workers=4"
- 补充说明
l 流控原理说明
在备机回放日志的能力跟不上主机时,通过在walsender线程中进行一定时间的sleep来降低日志发送速度,让备机来得及回放已接收的日志。也就是当备机接收日志或者回放日志的能力跟不上主机时,通过强制降低主机的业务性能,来保证主备机的日志差异在一定目标范围。
因此,关闭流控,可能存在主备机之间的RTO增大,在主机故障时,主备机切换的时长可能增加。

流控为主机业务压力与RTO两者之间的综合选择,需要根据实际业务特点慎重配置。
l 极致RTO说明
普通串行回放:

极致RTO:
xLog日志回放建立多级流水线,提高并发度,提升日志回放速度。

问题实例CPU利用率均比较低,且产生流控根本原因为备机回放慢导致,因此评估开启极致RTO,可有效提高日志回放速率。
5.5 业务分布键选择不合理导致数据分布不均匀
- 问题现象
数据磁盘使用率不均衡

- 原因分析
客户环境为分布式环境3C3D,从监控指标分析,dn_6001、dn_6002、dn_6003分片的磁盘使用率比其他2个分片高。

怀疑是数据分布不均匀导致。
- 分析过程
1. 登录到数据库节点,执行如下命令查看数据盘使用率,数据目data1的使用率10%,其他数据目录均为2%。
gs_ssh -c "df -h"

2. 进入到数据目录data1下,执行如下命令查看文件大小,显示base目录占比最大,怀疑数据分布不均匀。
du -sh *

3. 执行如下SQL,查询倾斜率较大的表
select * from pgxc_get_table_skewness order by skewratio desc limit 10;

查询结果可以分析出表“t1”的数据倾斜率为100%,完全倾斜。(一般默认倾斜率大于10%,就代表数据已经倾斜)
4. 执行如下SQL,查看表的数据分布情况
select schemaname,tablename,nodename,pg_size_pretty(dnsize) dnsize,dnsize/(sum(dnsize) over ()) as percent from table_distribution('root','t1');

根据查询结果可以分析出表“t1”的数据全部分布在dn_6001所在的分片
5.查看表“t1”的分布列为“a”,分布方式为hash分布。

6. 查看表“t1”字段“a”的值,全部为固定值“1001”

综上分析,表“t1”的分布键为固定值,导致数据全部被分布到dn_6001节点,导致该分片所对应的数据目录用率比其他节点高。
处理方法
建议业务调整分布键,使数据均匀的分布到所有节点,参考分布式表的设计规范:GaussDB 文档中心
5.6 DN发生主备切换问题定位
- 问题现象
GaussDB 实例主备切换
- 问题分析
1. 16:53:14,DN6002与DN6001断联,同时查看DN 主节点,未发现进程异常重启相关文件生成(ffic_log,core文件)。

2. 16:53:15,cms主与dn主节点,网络检查失败。

3. 16:53:23,CMS主与DN主节点CMA断联,CMS与CMA的连接超时时间受wal_sender_timout参数控制,默认是超过6s连接不上,就会断开连接。

4. 同时满足如上两个条件,触发6002升主

5. 16:53:30,网络恢复,6001角色上报给cms为主,双主情况下留新主,CMS主下发重启6001命令

- 问题原因
网络闪断导致CMS主节点与 DN主节点CMA断联,dn6002与dn6001断联 触发了6002升主。网络恢复后,CMS留新主,重启原主。
- 点赞
- 收藏
- 关注作者
评论(0)