kill 指令的执行原理
kill 指令有两种写法 " kill query + 线程 id "、" kill connection(可缺省) + 线程 id "。分别表示关闭指定线程正在执行的语句、断开指定线程连接的客户端(如果有正在执行的操作会先停止执行的操作再关闭连接)。但某些情况下使用 kill query 后使用 show processlist 查看 Command 列为 killed(表示 正在等待回收线程回收,还未回收),这是为什么呢?
在解答这个问题前,需要知道服务器端处理请求的线程是如何执行的,以及 kill 命令是如何作用的。
Kill 指令执行原理
指令执行特点
1、 一个语句执行过程中有多处 " 埋点 ",在这些 " 埋点 " 的地方判断线程状态,如果发现线程状态是 THD:KILL_QUERY,才开始进入语句终止逻辑;
2、如果处于等待状态,必须是一个可以被唤醒的等待,否则根本不会执行到“埋点”处;
3、语句从开始进入终止逻辑,到终止逻辑完全完成,是有一个过程的。
kill query 执行原理
kill query 主要进行了两步操作:
1、把线程的运行状态改成 THD::KILL_QUERY(将变量 killed 赋值为 THD::KILL_QUERY);
2、给会话的执行线程发一个信号,退出阻塞状态,处理这个状态。
Kill Connection 执行原理
1、把 12 号线程状态设置为 KILL_CONNECTION;
2、关掉 12 号线程的网络连接。
是否可以被中断判断
1、一般正常执行的语句在执行 kill query 后都会先将状态从 killed 改成 KILL_QUERY,然后执行到 " 埋点 " 处被判断中断执行。
2、如果是处于阻塞的语句,那么需要去查看当前阻塞等待的状态是否可以被唤醒,如果可以被唤醒才有机会中断当前语句。
可以被中断的场景:正常执行或者处于可以被唤醒的阻塞等待状态。
例子:因行锁阻塞。

因为等行锁时,使用的是 pthread_cond_timedwait 函数,所以这个等待状态可以被唤醒。可以被 kill query 直接唤醒继续执行直到 "埋点" 判断。
不可以被中断的场景:被阻塞且不能被唤醒。
例子:因并发线程被使用完而造成的阻塞。
将参数 innodb_thread_concurrency(MySQL 的并发线程数)设为 2。然后执行下面的操作:

在 sessionD 执行 kill query C 后 sessionC 并没有退出阻塞。
问题1:为什么使用 kill query 没有中断阻塞?
答:因为这种阻塞从微观上来看并不是阻塞,而是一种循环判断。每隔 10 毫秒判断一下是否可以进入 Innodb 执行,如果不行,就调用 nanosleep 函数进入 sleep 状态。也就是说,虽然线程的状态已经被设置成了 KILL_QUERY(THD::KILL_QUERY),但是在这个等待进入 InnoDB 的循环过程中,并没有执行到 "埋点",也就没有去判断线程的状态,因此根本不会进入终止逻辑阶段。所以也就不会中断。
问题2:如果此时使用 show processlist 来查看,会发现 Command 列为 killed,这是为什么?
答:kill query 语句会将线程状态设为 KILL_QUERY ,这时会因为这个状态而被判断为正在执行中断逻辑,所以 Command 值为 killed。
问题3:为什么使用 kill connection 可以中断阻塞?
答:因为 kill connection 会直接关闭线程的网络连接,强制关闭,所以这时候 session C 收到了断开连接的提示。
问题4:如果只是使用 kill query 什么时候才能中断阻塞?
答:只有等到会话被分配了线程后执行到 “ 埋点 ” 后判断然后执行中断逻辑才会退出。而被分配线程后并不是就一定会中断,如果在执行到 "埋点" 之前让出线程,那么就会再次等待。MySQL 的线程是多路复用的。
其他
1、其实除了上面使用 kill 命令来终止阻塞状态外,还可以直接在该会话中使用 “ ctrl+c ” 来中止阻塞,这又是什么原理呢?
答:首先要知道客户端操作服务端是客户端开启一个线程,让这个线程去处理,发送请求数据,通过网络传输到服务端,服务端再分配线程去处理。而 "ctrl +c " 是让客户端另开一个连接,并发送一个 kill query 的命令。所以虽然我们看来是中断了阻塞,但是处理上一个连接的服务端线程并一定就会被中断。
2、为什么在指定库名连接时会很慢?如下图:

答:这是由于 MySQL 默认开启了自动补全功能(输入表名时可以使用 tab 自动补全)。其实现是在连接数据库多执行一些操作:
1、执行 show databases;
2、切到 db1 库,执行 show tables;
3、把这两个命令的结果用于构建一个本地的哈希表。(最耗时)
这个功能可以在命令中加上 -A 关闭。同时使用 -quick 也可以关闭。但是使用 -quick 可能会使客户端性能降低。这是为什么?这就要说到数据在服务器端与客户端发送的流程了。
服务器线程执行流程
客户端首先与服务器端验证用户名和密码,通过后正式建立连接,然后客户端发送请求,服务器端从线程池中取一个线程来处理。处理的过程:
1、获取一行,写到 net_buffer 中。这块内存的大小是由参数 net_buffer_length 定义的,默认是 16k。
2、重复获取行,直到 net_buffer 写满,调用网络接口发出去。
3、如果发送成功,就清空 net_buffer,然后继续取下一行,并写入 net_buffer。
4、如果发送函数返回 EAGAIN 或 WSAEWOULDBLOCK,就表示本地网络栈(socket send buffer)写满了,进入等待。直到网络栈重新可写,再继续发送。

从上面的流程可以知道,如果一次要发送的数据量超过 socket send buffer 空间,那么就会拆分开来发送,并不会发生 " 内存打爆 " 的情况。由此我们可以知道,MySQL 是边读边发的。
1、如果请求返回的数据量很大,那么在等待返回的过程中使用 show processlist 查看 State 列的值就会为 " Sending to client",表示服务器端的网络栈写满了。
这是因为 Sate 列值的变化是在查询请求到达开始执行就会变为 " Sending data ",如果网络栈写满发就会切换为 " Sending to client ",表示 " 正在等待客户端接收结果 "。" Sending data " 可能处于线程执行过程中的任意阶段,比如因为锁而阻塞的场景。


2、如果 show processlist 的 State 列一直为 " Sending to Client ",那么可以
1)查看这条SQL,判断是否可以优化,减少返回值。
2)将 net_buffer_length 设的大一些,来避免或者减少发送阻塞的时间。
客户端执行流程
在开始客户端会创建线程去连接服务器端,然后接收服务端返回的数据,客户端接收服务器端返回的数据有两种方式:
1、本地缓存。在本地开一片内存,先把结果存起来。如果用 API 开发,对应的就是 mysql_store_result 方法。建议在客户端处理量大时使用本地缓存。可以使用 mysql -h$host -P$port -u$user -p$pwd -e "select * from db1.t" > $target_file 将返回的数据保存到指定文件。
2、不缓存,读一个处理一个。如果用 API 开发,对应的就是 mysql_use_result 方法。
回到上面的问题,为什么使用 -quick 可能会导致客户端性能下降?这是因为客户端默认使用缓存来接收,所以在客户端正在处理其他数据时就可以先进行缓存,等到后面直接读取缓存就可以了。而使用 quick 就会使客户端接收不使用缓存,那么如果客户端正在执行其他操作这个数据就会被阻塞,并且服务器端对应的线程也会因为没有收到客户端的反馈而没有中断这次事务,这次事务涉及到的资源锁也没有释放,造成并发问题,影响效率。除此之外, quick 还有三个效果。
1、就是前面提到的,跳过表名自动补全功能。
2、客户端接收数据使用不缓存的方式。而 mysql_store_result 方法需要申请本地内存来缓存查询结果,如果查询结果太大,会耗费较多的本地内存,可能会影响客户端本地机器的性能;
3、不会把执行命令记录到本地的命令历史文件。
kill 指令的执行原理的更多相关文章
- angular核心原理解析3:指令的执行过程
指令的执行过程分析. 我们知道指令的执行分两个阶段,一个是compile,一个是link. 我们可以在指令中自定义compile和link. 首先,我们来讲解如何自定义link函数 举个例子: < ...
- Python程序的执行原理(转载)
Python程序的执行原理 2013-09-17 10:35 佚名 tech.uc 1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令 ...
- erlang虚拟机代码执行原理
转载:http://blog.csdn.NET/mycwq/article/details/45653897 erlang是开源的,很多人都研究过源代码.但是,从erlang代码到c代码,这是个不小 ...
- MySQL执行原理,逻辑分层、更改数据库处理引擎
MySQL执行原理,逻辑分层.更改数据库处理引擎 作者:Stanley 罗昊 [转载请注明出处和署名,谢谢!] 用了那么长时间的MySQL,sql语句相信早已烂熟于心,于是,我就试着去了解它的执行原理 ...
- PHP执行原理
简单解释:PHP执行原理 客户端向服务器发送一个请求,如果请求的是一个HTML页面,服务器直接将HTML页面发送到客户端给浏览器解析,如果请求的是PHP页面,则服务器会运行PHP页面然后生成标准的HT ...
- python程序执行原理
Python程序的执行原理 1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后解释器一条一条执行字节码指令,从而完成程序的执行. 1.1python先把代码(.py ...
- IIS执行原理
IIS执行原理 服务器的监听(IIS6.0+版本) 当请求到达服务器时,请求最终会到达TCPIP.SYS驱动程序,TCPIP.SYS将请求转发给HTTP.SYS网络驱动程序的请求队列中(可以理解为 ...
- JVM基于栈的解释器执行原理
通过下面这段代码来解释JVM基于栈的执行原理 4. public static int add(int a, int b) { 5. int c = 0; 6. c = a + b; 7. retur ...
- redis源码学习之lua执行原理
聊聊redis执行lua原理 从一次面试场景说起 "看你简历上写的精通redis" "额,还可以啦" "那你说说redis执行lua脚本的原理&q ...
随机推荐
- 使用tkinter打造一个小说下载器,想看什么小说,就下什么
前言 今天教大家用户Python GUI编程--tkinter 打造一个小说下载器,想看什么小说,就下载什么小说 先看下效果图 Tkinter 是使用 python 进行窗口视窗设计的模块.Tkint ...
- sql server如何把退款总金额拆分到尽量少的多个订单中
一.问题 原来有三个充值订单,现在要退款450元,如何分配才能让本次退款涉及的充值订单数量最少?具体数据参考下图: 二.解决方案 Step 1:对可退金额进行降序排列,以便优先使用可退金额比较大的订单 ...
- SecureCRT无法退格删除
SecureCRT无法退格删除 securecrt无法退格删除问题解决: 如果想要全部会话都可以实现退格删除的功能,需要在全局选项设置. 最后选择全局应用即可.
- 精尽Spring MVC源码分析 - MultipartResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- cloudera集群开启kerberos认证后,删除zk中的/hbase目录
问题 在cdh集群中开启了kerberos认证,hbase集群出现一点问题,需要通过zookeeper-client访问zookeeper,删除/hbase节点时候报错:Authentication ...
- glances linux资源使用监控
安装 yum install glances -y 界面 介绍 命令选项 -b:显示网络连接速度 Byte/ 秒 -B @IP|host :绑定服务器端 IP 地址或者主机名称 -c @IP|host ...
- 高可用K8S构建3master+3node+keepalived+haproxy
视频地址:https://www.bilibili.com/video/BV1w4411y7Go?p=66 所需安装包在视频评论区 安装准备 系统: CentOS-7-x86_64-Minimal-1 ...
- Asp.Net WebApi使用Websocket
直接上代码 /// <summary> /// WebSocket Handler /// </summary> public class QWebSocketHandler ...
- Selenium switch_to方法
在web应用自动化测试中,点击一个链接或者按钮会打开一个新的浏览器窗口,会出现多个窗口实例.默认情况下的焦点在主窗口(父窗口),如果要对子窗口进行操作,就需要首先切换到子窗口. Selenium We ...
- C语言实现汉诺塔
汉诺塔 要把A柱子上的盘子移动到C柱子上,在移动过程中可以借助B柱子,但是要求小的盘子在上大的盘子在下. 解题思路: 1.把A柱子上的前N-1个盘子借助C柱子,全部移动到B柱子上(过程暂不考虑),再把 ...