GaussDB外部连接报错汇总

举报
Caesar.D 发表于 2021/01/27 22:07:27 2021/01/27
【摘要】 在使用客户端连接GaussDB时,可能会出现连接失败或者连接慢的问题,如果不能及时解决恢复很容易影响使用体验甚至业务运行。本文针对几种常见场景,结合原理快速分析定位,也可帮助客户进行合理的使用配置,从而在根源上减少问题的发生。文中涉及的参数、视图详情可参考产品文档。

1          问题背景

在使用客户端连接GaussDB时,可能会出现连接失败或者连接慢的问题,如果不能及时解决恢复很容易影响使用体验甚至业务运行。本文针对几种常见场景,结合原理快速分析定位,也可帮助客户进行合理的使用配置,从而在根源上减少问题的发生。文中涉及的参数、视图详情可参考产品文档。

2          场景一:too many clients already, active/non_active: xxxx/xxxx.

2.1         问题描述

客户端连接数据库、查询语句等,报错连接已满:too many clients already, active/non_active: xxxx/xxxx.


图1. dn上连接满

2.2         原理分析

max_connections决定了进程内的线程数量上限。

2.2.1        DN上连接满

DN上连接满通常是由于CN与此DN的连接数(一条连接即在DN上开启一个线程)和此DN上启动的Stream线程数量之和达到max_connections所致,报错信息一般会指明具体DN,如图1。当客户端与CN建连后,CN会根据连接的databaseusernameoptions匹配到pooler中对应的poolpool中会保留与其他DN的连接。当需要执行query时,pool会根据执行计划选择与对应DN的保留的连接,若没有,则进行建连。当query执行结束或客户端断开与CN连接后,CNDN间连接直接归还给相应的pool,便于其他query直接使用,节省建连所需的时间、资源成本。

然而,如果user1访问database1执行了100条并发语句后全部退出,那么相应的pool1中就会保留与其他每个DN100条连接,user2访问database1执行100条并发语句后,又会在pool2中保留100条连接,随着不同pool保留的累积,最后在DN上的连接数达到了max_connections,连接报错。


图2. pooler通信框架

为此,数据库设计了pooler复用技术,即客户端连接数据库只根据database进行匹配pool,不同的user可以访问同一个pool,从而减少pool的数量,进而减少了保留的连接。然而,随着database数量的增加,相应的pool数量同样也会增加,保留的连接数依然可能达到max_connections8.0版本增加自动clean connection功能,当CNDN建连时发现DN连接已满,会清理所有pool中四分之一的空闲连接,不足四个则清理一个,从而保证空闲连接不会产生影响。

影响DN上此参数的另一方面是Stream线程数量,其与相应业务的执行计划相关,在定位该问题时,优先排查CNDN间的连接数量,若数量偏差较大,再进一步考虑业务Stream情况。

2.2.2        CN上连接满

CN上连接满与DN上有着较大差别,是指CNCN之间(同DN,也是和pooler相关)和客户端与CN的连接数之和达到max_connections,通常此报错与业务侧相关。对于CNCN之间的连接,如果更多的是active状态,可能是业务中包含很多DDL操作;如果更多的是idle状态,那么则为pooler中保留的连接,可使用clean connection清理。对于客户端的连接,active状态表示正有业务运行,如果大部分连接都是此状态,那么很有可能是业务并发太高(存在特殊情况),需要降低并发。如果更多的是idle状态的话,则很有可能是对于idle状态连接管控的问题(详见2.3.2)。

2.3         定位方法

2.3.1        DN上连接满

    1. clean connection to [node (dn_xxxx_xxxx/cn_xxxx)] [all] for database dbname;

CN上提供了pg_pooler_status视图可以看到当前CN与所有DN的连接情况,对于某个报错的DN,需要分别在所有CN上执行:

1.        select in_use, count(*) from pg_pooler_status where node_name = 'dn_xxxx_xxxx' group by 1;  

可查看此DN的连接情况,其中in_usef表示此连接没有被占用,即保留在pool中,为t即为正在使用。

使用clean connection操作可清理in_usef的连接,指定node (dn_xxxx_xxxx)即清理指定DN的连接,指定node (cn_xxxx)即清理指定CN与所有DN的连接,指定all则清理集群所有的in_usef的连接。当指定all的时候可添加关键字force,这样in_uset的连接也同样会进行清理,详见产品文档。

    2. 如果开启了persistent_datanode_connections,释放掉CN上的idle连接或关闭此参数;

当无可执行的query时,CNDN的连接将归还pool,即in_uset变为f,但是若此参数开启,则会一直保留连接。关闭此参数或清理CNidle状态的连接(详见2.3.2)是一种有效的手段。

    3. GUC参数max_connections

CNDN上的max_connections设置是有依据的,理论上,DN上应设置为CN上的值 * CN的个数。这是由于不同的CN都会与同一个DN进行建连,DN的连接上限要满足大于所有CN连接上限之和。核查一下,说不定真的是DN上不够了!

    4. 神级视图:pgxc_thread_wait_status

1.  查询当前DN上所有线程数量:select count(*) from pgxc_thread_wait_status where thread_name like 'cn%' and node_name = 'dn_xxxx_xxxx';  

2.  查询当前DN上所有Stream线程数量:select count(*) from pgxc_thread_wait_status where thread_name like 'cn%' and tlevel <> 0 and node_name = 'dn_xxxx_xxxx';  

3.  查询所有DN上的线程数量:select node_name, count(*) from pgxc_thread_wait_status where thread_name like 'cn%' and node_name like 'dn%' group by 1 order by 2 desc;  

4.  查询所有DNCN的连接和Stream线程数量:select node_name, sum(level0) cnt0, sum(level1) cnt1, cnt0 + cnt1 sum from (select node_name, case tlevel when 0 then 1 else 0 end as level0, case tlevel when 0 then 0 else 1 end as level1 from pgxc_thread_wait_status where thread_name like 'cn%' and node_name like 'dn%'group by 1 order by 1, 2, 3 desc;  

5.  查询所有query在每个DN上的Stream线程数量:select query_id, node_name, count(*) from pgxc_thread_wait_status where query_id <> 0 and tlevel <> 0 and thread_name like 'cn%' and node_name like 'dn%' group by 1, 2 order by 1, 3 desc;  

6.  ... 

使用-rm工程模式登录数据库,根据pgxc_thread_wait_status视图查看所需信息。然而,存在一些情况是场景丢失、连接数已恢复正常,此时可以部署脚本等待问题再次复现。


图3. general脚本用法

    5. gstack打堆栈;

若前几种方案均无法排查出问题,可以使用gstack收集堆栈信息,逐一线程分析为何没有正常退出(线程数量可能很庞大,建议打印的堆栈重定向至文件中)。

    6. kill dn_xxxx_xxxx;(应急)

1.        DN目录:cm_ctl query -Cvd | grep dn_xxxx_xxxx  

2.        DN进程:ps ux | grep 上一步找到的目录  

3.        统计连接数量:netstat -anpt | grep pid | wc -l  

4.        Kill DN进程:kill -9 pid  

kill连接数满的DN进程是一种快速、有效的规避手段,然而此方法会对正在执行的业务有短暂影响,且不利于定位根因,使用时需慎重考虑。

    7. cm_ctl switchover –n 备机序号 –D 备机目录;(应急)

对于一些特殊情况,例如仅在个别节点应急后连接数仍频繁涨满、其他节点均正常且暂时无法定位根因,使用主备切换方法可进行应急。

2.3.2        CN上连接满

    1. 设置session_timeout;

当业务侧未配备连接池管控idle连接时,需要数据库进行管控,否则idle连接一直不释放,很容易造成连接满的问题。对此,设置session_timeout不为0,当达到此时间没有任何操作,则数据库会自动断开连接。值得注意的是,若业务侧有进行对idle的管控,数据库同时设置session_timeout,当超时后数据库自动断开连接,业务侧再获取idle状态的连接,此时连接已断开,会报错:An I/O error occurred while sending to the backend.

    2. 排查业务侧哪些连接没有释放;

1.        select usename, application_name, client_addr, count(*) from pgxc_stat_activity where state = 'idle' and coorname = 'cn_xxxx' group by 1, 2, 3 order by 4 desc;  

2.        select usename, application_name, query_start from pgxc_stat_activity where state = 'idle' and coorname = 'cn_xxxx' group by 1, 2, 3 order by 3 desc;  

3.        ...  

根据pgxc_stat_activity视图可以看到报错CN上的所有连接情况,按照相应的客户端IP地址、用户名、应用名称等信息可定位到连接来源,从而分析根因。此视图可在任意CN上查询,根据需求选择字段查询,详见产品文档。

    3. 神级视图:pgxc_thread_wait_status

1.  select * from pgxc_thread_wait_status where node_name = 'cn_xxxx';  

DN连接满,此视图可查每个连接对应的线程状态,进而分析根因。

    4. gstack打堆栈;

若所有CN均出现此报错、无法正常登录数据库或前几种方案均无法排查出问题,可以使用gstack收集堆栈信息,逐一线程分析为何没有正常退出(线程数量可能很庞大,建议打印的堆栈重定向至文件中)。

    5. kill连接线程;(应急)

1.        select pid, state from pgxc_stat_activity;  

2.        select pg_terminate_backend(pid);  

对于CN上的每一条连接,都有一个线程相对应,将对应线程kill则会将连接断开。然而此方法同样不利于分析根因,日后还是容易复发。不过尝试kill少量空闲连接对定位影响较小,且也有助于业务恢复。

    6. kill cn_xxxx;(应急)

同样,kill CN进程应急快、效果好,但是难以分析根因,且执行中的业务会受到影响。

3          场景二:An I/O error occurred while sending to the backend.

3.1         问题描述

客户端使用与数据库建好的连接报错:An I/O error occurred while sending to the backend.

图4. 场景二报错截图

3.2         原理分析

正如2.3.2中所说,当数据库侧由于某种原因将连接断开,应用侧再次使用此连接就会产生这个报错。而数据库侧连接异常断开主要有四种原因:1. CN进程异常重启;2. session超时;3. 人为kill session;4. LVS中virtual router id冲突。

3.3         定位方法

    1. 查看CN进程是否异常重启

1.  ps -eo pid,lstart,etime,cmd | grep coo  

如果连接在CN进程启动之前就存在,那么CN进程重启之后,连接就会断开,业务侧继续使用则会报错。

    2.  未达到session_timeout时间就报错 

  如果不存在CN重启,且时间未到设置的session_timeout,或者业务正在运行中就产生此报错,可排查LVS中的virtual router id是否冲突。

   查看OS日志,搜索Keepalive字段,若存在如下图字样,即为冲突,可修改主备LVS配置文件后,重启LVS即可:.

   /etc/keepalived/keepalived.conf:文件中virtual_router_id字段,同一子网下不可冲突

   /etc/init.d/gs_keepalived restart:重启LVS

    3. 排查CN日志是否含有due to

非常清晰的两种情况,根据日志中相应时间分析;

调用select pg_terminate_backend(pid); 终止了会话,多数是用户操作产生,个别情况是由于cm_agent在检测磁盘超过阈值(默认90%)场景下,会调用此方法终止当前CN上所有非omm用户的SQL,同时会打印调用日志“cancel session ok”,可排查cm_agent日志。

会话设置了session_timeout时间,当超过此时间,数据库会自动清理连接。对于需要保持长连接的场景。可通过客户端对此session设置session_timeout为0或预期时长。

4          场景三:客户端连接数据库很慢.

4.1         问题描述

客户端可以连接数据库,但是耗时长。(可能连接个别CN出现此现象)

4.2         原理分析

已知的问题是GaussDB提供GUC参数log_hostname,此参数默认开启。当客户端来连接数据库时,会根据log_hostname来选择调用操作系统函数getnameinfo()时是否进行DNS解析域名,即根据客户端ip获取主机名。由于DNS的配置问题,解析可能耗时较长甚至失败,因此对于客户端来说,连接数据库很慢。

4.3         定位方法

    1. show log_hostname;

登录连接慢的CN,查看log_hostname的值,若为on,可根据线程号打印堆栈;

1.  select application_name, lwtid from pg_stat_activity a, pg_thread_wait_status b where a.pid = b.tid and client_addr = 'xxxx' and datname = 'xxxx' and usename = 'xxxx' and backend_start between 'xxxx' and 'xxxx';  

2.  gstack lwtid  

图5. getnameinfo()堆栈信息

若堆栈中存在明显的getnameinfo(),则关闭此参数即可;没有打印堆栈的工具也可尝试关闭。

    2. log_hostname = on?

若log_hostname为on,可使用ping命令排查网络时延等问题;如果网络正常,那么还是根据线程号打堆栈进一步分析。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200