//
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个大的类,分别是:
Thread Command ValuesGeneral Thread StatesReplication Source Thread StatesReplication I/O Thread StatesReplication SQL Thread StatesReplication Connection Thread StatesNDB Cluster Thread StatesEvent Scheduler Thread States首先我们需要知道,我们所讨论的这两种状态,是数据通用线程状态里面的。现在我们看看这两个状态的解释:
sending data(或者叫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的状态自然就消失了。