一 问题由来
这是一个朋友问我的(@成都--麦涩可),原问题如下:
数据库发送数据给客户端这个时间算是sql的执行时间嘛?
要解决问题我们需要知道MySQL何时将数据传输给了客户端,既然是要传输实际的数据给客户端那么肯定是select语句了,同时我们要明白一个正常select运行到底要经历哪些阶段。
二 一个简单SELECT语句经历的阶段
THD::enter_stage: 'checking permissions' THD::enter_stage: 'Opening tables' THD::enter_stage: 'init' THD::enter_stage: 'System lock' THD::enter_stage: 'optimizing' THD::enter_stage: 'statistics' THD::enter_stage: 'preparing' THD::enter_stage: 'executing' THD::enter_stage: 'Sending data' THD::enter_stage: 'end' THD::enter_stage: 'query end' THD::enter_stage: 'closing tables' THD::enter_stage: 'freeing items'
实际上整个阶段都算到了语句的实际消耗时间之中的,但是慢查询记录的是:
实际执行时间 = (freeing items 末端的时间) 实际消耗时间 - (System lock末端的时间 )比如MDL LOCK等待时间 - (Innodb层锁定的时间)行锁等待消耗时间
三 什么是Sending data状态
实际上这个状态是select语句才会有的,如果是DML则不同但是都有等同的阶段如下:
select:Sending datainsert语句:Updatedelete/update:Updating
这个阶段非常的巨大,它至少包含了:
Innodb 层数据的定位返回给MySQL 层Innodb 层数据的查询返回给MySQL 层Innodb 层数据的修改(如果是MDL)Innodb 层加锁以及等待等待进入Innodb层(innodb_thread_concurrency参数)MySQL 层发送数据给客户端
可以看到基本所有和Innodb层打交道的过程都包裹在这个状态下面,当然我只是列举了我想到的一些,其实可能还有很多很多,这里我也把 MySQL 层发送数据给客户端包含在 Sending data的答案给出了,下面我们进行分析。
四 如何证明Sending data状态包含了网络数据传输的时间
之前你可以参考一下我的文章:MySQL:Innodb Handler_read_*变量解释
(阅读原文可以访问)。
我们建立一个简单的表如下,并且填充数据如下:
Create Table: CREATE TABLE `ty` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idxa` (`a`)) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4mysql> select * from ty;+----+------+------+| id | a | b |+----+------+------+| 8 | 2 | 3 || 9 | 5 | 4 || 10 | 6 | 7 || 11 | 6 | 8 |+----+------+------+4 rows in set (4.14 sec)
我们执行如下语句:
mysql> desc select * from ty where a =6 and b=8;+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+| 1 | SIMPLE | ty | NULL | ref | idxa | idxa | 5 | const | 2 | 33.33 | Using where |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+mysql> select * from ty where a =6 and b=8;+----+------+------+| id | a | b |+----+------+------+| 11 | 6 | 8 |+----+------+------+1 row in set (7.22 sec)
注意:这里的语句执行时间很长是因为我打了GDB断点所以看起来很久而已 我做了语句的trace,这个语句大概需要如下步骤:
首先Innodb定位到索引 idxa数据6所在位置这是初次定位调用函数
所以总结整个流程一共经历了一次索引定位,两次索引顺序读取,一共读取了三条数据,但是返回给MySQL层的只有前面两条数据,通过MySQL层的过滤只发送给了客户端一条满足条件的数据。
五 总结
很显然数据返回给客户端的时间包含在了整个语句的实际消耗时间中,同时包含在了慢查询的实际执行时间(Query_time指标)中,它是在sending data状态下完成的。
作者:重庆八怪 链接:https://www.jianshu.com/p/46ad0aaf7ed7