目录
一、问题截图
二、排查思路
1、Gbase8a SQL有几种状态
2、问题导致原因猜想
3、观察服务端(集群端)网络情况
4、观察客户端网络情况
5、排查客户端程序处理数据慢
5.1、send
(1)声明
(2)作用
(3)参数
(4)返回值
(5)阻塞条件
5.2、recv
(1)声明
(2)作用
(3)参数
(4)返回值
(5)阻塞条件
5.3、优化思路
6、查看计算节点
7、gccli执行SQL
8、调整参数max_allowed_packet
一、问题截图
今天我发现一个有趣的现象,用户执行一条长达17287秒(差不多5小时)的SQL,这个SQL并没有特别复杂,我用红箭头指出了特殊点:
1、SQL处于Writing to net状态。(这个状态持续了4个多小时)
2、查询的是INFORMATION_SCHEMA.COLUMNS系统表。
3、没有加过滤条件。
二、排查思路
1、Gbase8a SQL有几种状态
状态 | 含义 |
init | 表示SQL进入准备执行阶段,开始执行计划。 |
deleting from main table/updating main table | 准备对主表进行删除或更新操作。 |
end/query endSQL | SQL到达结束状态,准备清理资源。 |
Creating tmp table | 查询过程中,正在创建临时表。 |
Sending data | 读取数据向发起段发送查询结果。 |
closing tables | 关闭打开的表。 |
Evaluating | 执行计划评估。 |
Executing by step | 执行计划中的每一步。 |
Preparing metadata | 取得本查询所涉及表的可用节点信息。 |
Sending task to gnodes | 发送任务给数据节点。 |
Clear tmp tables | 查询完成,开始清理临时表。 |
Writing to Net | 向客户端发送数据包。 |
checking permissions | 检查权限。 |
commit | 提交数据。 |
killed | 被杀死。 |
logging slow query | 审计日志在保存慢SQL信息。 |
Rolling back | 数据回滚。 |
2、问题导致原因猜想
我们看到了Writing to Net的意思是向客户端发送数据包,会不会是网络的问题导致。我们可以提出一下几个猜想。
猜想 | 是否为问题原因 |
服务端网络负载高 | 未验证 |
客户端网络负载高 | 未验证 |
客户端程序处理数据慢 | 未验证 |
3、观察服务端(集群端)网络情况
Writing to Net的意思是向客户端发送数据包。会不会是网络负载较高,我这边是万兆网卡,理论可以达到10000Mbit/s,1Mbit(兆位) = 0.125Mb(兆字节),也就是1250Mb/s,nmon观察网络情况在33Mb/s上下浮动,排除是服务端网络问题。
猜想 | 是否为问题原因 |
服务端网络负载高 | 否 |
客户端网络负载高 | 未验证 |
客户端程序处理数据慢 | 未验证 |
4、观察客户端网络情况
客户端服务器我这边没有权限,只能建议客户帮忙排查了,哈哈哈。
猜想 | 是否为问题原因 |
服务端网络负载高 | 否 |
客户端网络负载高 | 权限问题,建议客户帮忙验证。 |
客户端程序处理数据慢 | 未验证 |
5、排查客户端程序处理数据慢
为什么我会有这样的想法呢,最近在学习网络编程相关的知识,会不会客户端程序处理逻辑有关,相关概念可以参考之前的博客《Unix环境高级编程-学习-05-TCP/IP协议与套接字》,服务端send数据,客户端recv数据,我们来简单介绍一下这两个函数,以及他们的阻塞条件。
5.1、send
(1)声明
ssize_t send(int __fd, const void *__buf, size_t __n, int __flags)
(2)作用
向套接字__fd所指向的地址发送缓冲区__buf中长度为__n的数据。
(3)参数
参数名 | 描述 |
__fd | 套接字文件描述符。 |
__buf | 缓冲区。 |
__n | 发送的数据长度。 |
__flags | 标志,这个在具体在其他篇幅讲。 |
(4)返回值
名称 | 描述 |
成功 | 返回发送的字节数。 |
失败 | -1 |
(5)阻塞条件
当要发送的消息长度大于套接字当前可用缓冲区时, send将阻塞。
5.2、recv
(1)声明
ssize_t recv(int __fd, void *__buf, size_t __n, int __flags);
(2)作用
从套接字__fd接收长度为__n的数据放入缓冲区__buf中。
(3)参数
参数名 | 描述 |
__fd | 套接字文件描述符。 |
__buf | 缓冲区。 |
__n | 接收的数据长度。 |
__flags | 标志,这个在具体在其他篇幅讲。 |
(4)返回值
名称 | 描述 |
成功 | 返回接收的字节数。 |
失败 | -1 |
(5)阻塞条件
recv函数会一直阻塞到接收缓冲区里有一个字节或一个完整的UDP数据报为止。
5.3、优化思路
我们平时一般的开发思路是单进程单线程从缓冲区中接收到数据,开始处理数据,处理完发送消息给服务端我收到消息,发送端再发数据,如果处理数据时间较长,是不是可能会出现类似状况呢。
如果需要改进,我的思路是开启两个线程,一个线程用于接收数据并将数据放入一个队列中,放入之后和客户端说我收到数据了,另一个线程从队列中拿数据进行处理,这样就不会一直等待。
6、查看计算节点
之前查看了一下计算节点,发现没有类似任务,刚开始还觉得这是都算完了,后来想了想,计算节点就不会有这个任务,因为管理节点和计算节点的DDL是同步的,只需要在拿到任务的管理节点计算即可。也不是个别计算节点慢导致的。
7、gccli执行SQL
我们可以用Gbase8a自带的客户端工具gccli放到本地来执行SQL,gccli具体使用方法可以参考之前的博客《南大通用数据库-Gbase-8a-学习-32-gccli客户端》,这样可以屏蔽网络的问题。
我手动执行了一下,差不多两分钟左右,gccli没有额外的数据处理过程,只是将数据fetch出来进行展示,上面程序处理慢的问题概率又大了几分。
猜想 | 是否为问题原因 |
服务端网络负载高 | 否 |
客户端网络负载高 | 权限问题,建议客户帮忙验证。 |
客户端程序处理数据慢 | 有一定可能。 |
8、调整参数max_allowed_packet
参数适当调大后,效果不明显,已经调回原值。
说明 | 通信时最大的包长度。即服务器和客户端通讯时,发送和接收的数据包或字符串的最大长度。 |
默认值 | 64 * 1024 * 1024(单位:字节) |
最小值 | 1024(单位:字节) |
最大值 | 4L*1024L*1024L*1024L(单位:字节) |
详细介绍 | 1、设定单个报文或任何中间字符串(intermediate string)的最大长度,单位是字节。 2、报文消息缓冲由 net_buffer_length 参数进行设定,一般情况下,数据包的通讯缓冲区初始化为 8K字节 。 但其最终可以按需增长至max_allowed_packet 参数设定的大小。 3、这个参数值一般不需要设置的太大。较小的通讯缓冲区设置值可以捕获大的数据包,而那些大的数据包通常是由于异常引起的。 4、此参数的默认值较小,在使用了 BLOB 列或长字符串的场景中,应该增大其值至能容纳最大 BLOB 数据的长度。协议本身限定此值最大为 1G,参数只接受 1024 整数倍的数值,非 1024 的整数倍将会被自动圆整至离其最近的1024 整数倍的数值。 |