//
MySQL8.0之Sending data和Sending to client的区别
//
日常的MySQL运维工作中,我们经常会使用到show processlist这样的语法,来查看当前数据库上面的连接情况。show processlist语法的返回过程中,经常会看到sending data和sending to client的状态。今天来看看这两个状态的区别。
首先查阅官方文档中的描述:(给出官网地址)
https://dev.mysql.com/doc/refman/8.0/en/general-thread-states.html
在MySQL中,将show processlist的结果状态分为8个大的类,分别是:
首先我们需要知道,我们所讨论的这两种状态,是数据通用线程状态里面的。现在我们看看这两个状态的解释:
sending data(或者叫executing)状态:
在 MySQL 8.0.17 之前:表示线程正在读取和处理 SELECT 语句的行,并将数据发送到客户端。由于在此状态期间发生的操作往往会执行大量磁盘访问(读取),因此它通常是给定查询生命周期中运行时间最长的状态。MySQL 8.0.17 及更高版本:此状态不再单独指示,而是
包含在 Executing 状态中。
从描述中不难看出来,Sending data这个状态,在后续的8.0.17版本之后,会自动并入Executing之中,它表示当前SQL查询已经进入了执行阶段,接下来要发送结果给客户端、然后继续执行语句。简单理解,就是Sending data状态,代表这个SQL处于执行阶段的任意时刻。即使在有锁等待的情况下,依旧会显示为Sending data。
8.0.19版本中的实验如下:
在会话1中执行:
[yeyz] 23:40:04> begin;
Query OK, 0 rows affected (0.00 sec)
[yeyz] 23:40:07> select * from a for update;
+------+------+
| f1 | f2 |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
+------+------+
6 rows in set (0.00 sec)
会话2中执行下面操作,并使用show processlist:
[yeyz] 23:23:38> begin;
Query OK, 0 rows affected (0.01 sec)
[yeyz] 23:35:18> select * from a lock in share mode;
+------+------+
| f1 | f2 |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
+------+------+
6 rows in set (0.03 sec)
[yeyz] 23:35:31> show processlist;
+----------+----------------+--------------------+------+------------------+----------+---------------------------------------------------------------+----------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----------+----------------+--------------------+------+------------------+----------+---------------------------------------------------------------+----------------------------+
| 18050523 | mysqlha_common | xxx:51542 | yeyz | Query | 0 | starting | show processlist |
| 18050534 | mysqlha_common | xxx:56822 | yeyz | Query | 6 | executing | select * from a for update |
+----------+----------------+--------------------+------+------------------+----------+---------------------------------------------------------------+----------------------------+
2 rows in set (0.01 sec)
可以看到,这个命令处于executing状态(如果是低版本的MySQL,会显示Sending data),但是很明显,会话2处于锁等待状态。但是给人的感觉像是在给客户端发送数据一样。
总结:Sending data状态或者Executing状态,代表这个语句正在执行,一旦执行完毕,进入数据发送阶段,就不再保持这个状态。
sending to client状态:
服务器正在向客户端写入数据包。
这个状态要说清楚,必须引入MySQL的查询流程,MySQL的数据发送给客户端,是要经过3个过程的:
1、MySQL把数据写入net_buffer,写满net_buffer之后调用接口发送到本地网络棧;
net buffer的相关变量如下:
[yeyz] 23:47:31> show variables like '%net_bu%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| net_buffer_length | 16384 |
+-------------------+-------+
1 row in set (0.01 sec)
2、本地网络棧也叫socket send buffer,它的默认值保存在Linux操作系统下面的路径中:
[root@ ~]# cat /proc/sys/net/core/wmem_default
212992
客户端会请求本地网络棧的内容,将数据发送到客户端的socket receive buffer中
3、客户端去读取socket receive buffer中的内容,如果客户端接收得慢,会导致MySQL服务端由于结果发不出去,这个事务的执行时间变长
总结:
如果show processlist看到的State的值一直处于“Sending to client”,说明SQL这个语句已经执行完毕,而此时由于请求的数据太多,MySQL不停写入net buffer,而net buffer又不停的将数据写入服务端的网络棧,服务器端的网络栈(socket send buffer)被写满了,又没有被客户端读取并消化,这时读数据的流程就被MySQL暂停了。直到客户端完全读取了服务端网络棧的数据,这个状态才会消失。
一个比较好缓解上面问题的方案是增大net_buffer_length的值,让MySQL将查询到的所有数据都缓存在net buffer里面,由于SQL执行完毕,没有新的数据写入net buffer,net buffer慢慢的发送给socket send buffer,即使客户端读取的速度慢,但是由于没有新数据,这个Sending to Client的状态自然就消失了。