当前位置: 首页 > news >正文

MySQL 32 为什么还有kill不掉的语句?

MySQL有两个kill命令:

  • kill query+线程id,表示终止该线程正在执行的语句;

  • kill (connection)+线程id,表示断开这个线程的连接,如果线程有语句正在执行,会先停止正在执行的语句。

有时候可能会遇到:使用了kill,却没能断开该连接,再执行show processlist时,看到这条语句的command列显示的是killed。

那这是什么意思呢?不是应该直接在show processlist结果里看不到这个线程了吗?本文就来讨论该问题。

收到kill后,线程做什么?

比如有一个场景:

session C执行kill query后,session B几乎同时提示了语句被中断,这是预期结果。那么session B是直接终止掉线程,什么都不管直接退出吗?

不是的,session在处于blocked状态时,还是拿着一个MDL读锁的,如果kill时直接终止,该读锁就没机会释放。这样看的话,kill并不是马上停止的意思,而是告诉执行线程该语句已经不需要继续执行,可以开始“执行停止逻辑”了。

当session C执行kill语句,MySQL里处理kill命令的线程会做两件事:

  • 将session B的运行状态改成THD::KILL_QUERY(将变量killed赋值为THD::KILL_QUERY);

  • 给session B的执行线程发一个信号,目的是让session B退出锁等待,来处理THD::KILL_QUERY状态。

上面的分析隐含了一些意思:

  • 一个语句执行过程中有多处埋点,在这些埋点地方判断线程状态,如果发现状态时THD::KILL_QUERY,才开始进入语句终止逻辑;

  • 如果处于等待状态,必须是一个可以被唤醒的等待,否则不会执行到埋点处;

  • 语句从开始进入终止逻辑,到终止逻辑完全完成,是有一个过程的。

接下来看一个kill不掉的例子。首先执行set global innodb_thread_concurrency=2,将InnoDB并发线程上限数设置为2,然后执行下面的序列:

  • session C执行时blocked;

  • session D的kill query C没产生效果;

  • session E执行kill connection,才断开session C的连接;

  • 但若此时在session E执行show processlist,结果如下:

此时id=12的command列显示killed,说明客户端虽然断开了连接,但实际服务端上这条语句还在执行过程中。

这里为什么和第一个例子不同呢?

在实现上,等待行锁使用的是pthread_cond_timedwait函数,该等待状态可以被唤醒。但这里12号线程的等待逻辑是每10毫秒判断是否可以进入InnoDB执行,如果不行就调用nanosleep函数进入sleep状态。也就是说,虽然12号线程状态已被设置为KILL_QUERY,但在等待进入InnoDB循环的过程中,并未判断线程的状态,因此不会进入终止逻辑阶段。

当session E执行kill connection时:

  • 将12号线程状态设置为KILL_CONNECTION;

  • 关掉12号线程的网络连接,因此session C会收到断开连接的提示。

那为什么show processlist时能看到command显示killed呢?是因为执行show processlist时有一个特别的逻辑:如果一个线程的状态是KILL_CONNECTION,就把command列显示为killed。所以即使客户端退出,该线程的状态仍然是在等待中。

只有等到满足进入InnoDB的条件后,session C的查询语句继续执行,然后才有可能判断到线程状态已经变成KILL_QUERY或KILL_CONNECTION,再进入终止逻辑阶段,线程才会退出。

该例子是kill无效的第一类情况,即线程没有执行到判断线程状态的逻辑。相同情况的还有因为IO压力过大,读写IO的函数一直无法返回,导致不能及时判断线程状态。

另一类情况是终止逻辑耗时较长,这时从show processlist结果上看也是command=killed,需要等到终止逻辑完成,语句才算真正完成,这类情况常见场景有以下几种:

  • 超大事务执行期间被kill,此时回滚需要对事务执行期间所有新数据版本做回收,耗时很长;

  • 大查询回滚,删除查询过程生成的大临时文件,加上此时文件系统压力大,该过程可能需要等待IO资源,导致耗时很长;

  • DDL命令执行到最后阶段被kill,需要删除中间过程的临时文件,也可能受IO资源影响耗时较久。

关于客户端的误解

第一个误解是,如果直接在客户端Ctrl+C,是否可以直接终止线程呢?

答案是不可以的,在客户端的操作只能操作到客户端的线程,而客户端和服务端只能通过网络交互,是不可能直接操作服务端线程的。实际执行Ctrl+C时,是MySQL客户端另外启动一个连接,然后发送一个kill query命令。

第二个误解是,如果库里面的表特别多,连接就会很慢。

很多人会认为是表的数目影响了连接性能,但从第一篇文章就知道,客户端和服务端建立连接的时候,需要做的就是TCP握手、用户校验、获取权限,这些操作跟表的个数无关。实际上,当使用默认参数连接时,MySQL客户端会提供本地库名和表名补全的功能,为实现这个功能,客户端连接成功后,需要多做一些操作:

  • 执行show databases;

  • 切到库执行show tables;

  • 将这两个命令的结果用于构建一个本地的哈希表。

当一个库中表个数非常多,第三步就会花很长时间。因此,感知到的连接过程慢,不是连接慢和服务端慢,而是客户端慢。

这里自动补全效果是在输入库名或表名时,输入前缀,可以使用Tab补全。在连接命令中加上-A,可以关掉这个自动补全功能,因此如果自动补全用得不多,建议关掉。

另外,除了加-A,加-quick(简写-q),也可以跳过这个阶段。

第三个误解,就是关于-quick这个参数。

从字面上看,会觉得这是一个让服务端加速的参数,但实际上设置这个参数可能会降低服务端的性能。

MySQL客户端发送请求后,接收服务端返回结果方式有两种:

  • 一种是本地缓存,即在本地开一片内存,先存结果;

  • 另一种是不缓存,读一个处理一个。

MySQL客户端默认采用第一种,加上-quick后就会使用第二种。此时如果本地处理得慢,就会导致服务端发送结果被阻塞,因此会让服务端变慢。

因此,-quick是让客户端变得更快,而不是让服务端变得更快。

http://www.hskmm.com/?act=detail&tid=10933

相关文章:

  • Axure RP 9 Mac 交互原型设计 - 实践
  • 深入解析:rook-ceph自定义添加osd流程
  • 1789:算24
  • 流行的 3D 文件格式及其用途指南
  • CentOS7.9上安装MySQL8.4
  • 铁头山羊stm32-HAL库 - 实践
  • 2025CSP-S初赛游记
  • JBoltAI框架:企业级AI开发的革新路径与行业实践 - 那年-冬季
  • JBoltAI:重塑视频创作,开启零门槛智能混剪新时代 - 那年-冬季
  • 深入解析:手搓一个 DELL EMC Unity存储系统健康检查清单
  • Vscode + Latex指南
  • 线程池未争取关闭导致的一个bug
  • kafka创建topic
  • WPS 2025最新版EXE
  • OpenCV-图像通道提取与处理
  • Mac环境安装Nginx指南实录
  • csp2025
  • Ai元人文:价值共生时代的技术哲学构想之宣言
  • 完整教程:TruckSim与Matlab-Simulink联合仿真(一)
  • N皇后问题(DFS)
  • 2025csp初赛
  • PostGIS 介绍(2)--PostGIS 参考
  • Java编译全过程解密:从源码到机器码的奇幻之旅
  • 第一节计算机硬件基本组成
  • PyTorch深度学习实战【11】之神经网络的学习和训练 - 详解
  • 深搜广搜(DFS、BFS)
  • android studio发现设备立刻就掉
  • 见证语音领域 GPT-3 时刻!小米开源端到端语音模型 MiMo Audio;Xbox上线游戏助手,实时游戏理解+语音交互丨日报
  • go语言学习之基本数据类型转字符串
  • DeepLearning-LoRA 及其先进变体技术指南