kill什么意思

更新时间:2022-12-31 18:25:42 阅读: 评论:0


2022年12月31日发(作者:在线快译)

32-MySQL语句⽆法kill浅析

在MySQL中有两个kill命令:⼀个是killquery+线程id,表⽰终⽌这个线程中正在执⾏的语句;⼀个是killconnection+线程id,这⾥

connection可缺省,表⽰断开这个线程的连接,当然如果这个线程有语句正在执⾏,也是要先停⽌正在执⾏的语句的。

不知道你在使⽤MySQL的时候,有没有遇到过这样的现象:使⽤了kill命令,却没能断开这个连接。再执⾏showprocesslist命令,看到这

条语句的Command列显⽰的是Killed。

你⼀定会奇怪,显⽰为Killed是什么意思,不是应该直接在showprocesslist的结果⾥看不到这个线程了吗?

其实⼤多数情况下,killquery/connection命令是有效的。⽐如,执⾏⼀个查询的过程中,发现执⾏时间太久,要放弃继续查询,这时我们就可

以⽤killquery命令,终⽌这条查询语句。

还有⼀种情况是,语句处于锁等待的时候,直接使⽤kill命令也是有效的。我们⼀起来看下这个例⼦:

killquery成功

可以看到,ssionC执⾏killquery以后,ssionB⼏乎同时就提⽰了语句被中断。这,就是我们预期的结果。

收到kill以后,线程做什么?

但是,这⾥你要停下来想⼀下:ssionB是直接终⽌掉线程,什么都不管就直接退出吗?显然,这是不⾏的。

在MySQL之全局锁&表锁中讲过,当对⼀个表做增删改查操作时,会在表上加MDL读锁。所以,ssionB虽然处于blocked状态,但还

是拿着⼀个MDL读锁的。如果线程被kill的时候,就直接终⽌,那之后这个MDL读锁就没机会被释放了。

这样看来,kill并不是马上停⽌的意思,⽽是告诉执⾏线程说,这条语句已经不需要继续执⾏了,可以开始“执⾏停⽌的逻辑了”。

其实,这跟Linux的kill命令类似,kill-Npid并不是让进程直接停⽌,⽽是给进程发⼀个信号,然后进程处理这个信

号,进⼊终⽌逻辑。只是对于MySQL的kill命令来说,不需要传信号量参数,就只有“停⽌”这个命令。

实现上,当⽤户执⾏killquerythread_id_B时,MySQL⾥处理kill命令的线程做了两件事:

1.把ssionB的运⾏状态改成THD::KILL_QUERY(将变量killed赋值为THD::KILL_QUERY);

2.给ssionB的执⾏线程发⼀个信号。

为什么要发信号呢?

因为像上图的我们例⼦⾥⾯,ssionB处于锁等待状态,如果只是把ssionB的线程状态设置THD::KILL_QUERY,线程B并不知

道这个状态变化,还是会继续等待。发⼀个信号的⽬的,就是让ssionB退出等待,来处理这个THD::KILL_QUERY状态。

上⾯的分析中,隐含了这么三层意思:

⼀个语句执⾏过程中有多处“埋点”,在这些“埋点”的地⽅判断线程状态,如果发现线程状态是THD::KILL_QUERY,才开始进⼊语

句终⽌逻辑;

如果处于等待状态,必须是⼀个可以被唤醒的等待,否则根本不会执⾏到“埋点”处;

语句从开始进⼊终⽌逻辑,到终⽌逻辑完全完成,是有⼀个过程的。

到这⾥你就知道了,原来不是“说停就停的”。

接下来,我们再看⼀个kill不掉的例⼦,innodb_thread_concurrency不够⽤的例⼦。

⾸先,执⾏tglobalinnodb_thread_concurrency=2,将InnoDB的并发线程上限数设置为2;然后,执⾏下⾯的序列:

killquery⽆效的例⼦

可以看到:

onC执⾏的时候被堵住了;

2.但是ssionD执⾏的killqueryC命令却没什么效果,

3.直到ssionE执⾏了killconnection命令,才断开了ssionC的连接,提⽰“LostconnectiontoMySQLrverduring

query”,

4.但是这时候,如果在ssionE中执⾏showprocesslist,你就能看到下⾯这个图。

killconnect之后的结果

这时候,id=12这个线程的Commnad列显⽰的是Killed。也就是说,客户端虽然断开了连接,但实际上服务端上这条语句还在执⾏过程

中。

为什么在执⾏killquery命令时,这条语句不像第⼀个例⼦的update语句⼀样退出呢?

在实现上,等⾏锁时,使⽤的是pthread_cond_timedwait函数,这个等待状态可以被唤醒。但是,在这个例⼦⾥,12号线程的等待逻辑是

这样的:每10毫秒-判断⼀下是否可以进⼊InnoDB执⾏,如果不⾏,就调⽤nanosleep函数进⼊sleep状态。

也就是说,虽然12号线程的状态已经被设置成了KILL_QUERY,但是在这个等待进⼊InnoDB的循环过程中,并没有去判断线程的状态,

因此根本不会进⼊终⽌逻辑阶段。

⽽当ssionE执⾏killconnection命令时,是这么做的:

1.把12号线程状态设置为KILL_CONNECTION;

2.关掉12号线程的⽹络连接。因为有这个操作,所以你会看到,这时候ssionC收到了断开连接的提⽰。

那为什么执⾏showprocesslist的时候,会看到Command列显⽰为killed呢?其实,这就是因为在执⾏showprocesslist的时候,有

⼀个特别的逻辑:

如果⼀个线程的状态是KILL_CONNECTION,就把Command列显⽰成Killed。

所以其实,即使是客户端退出了,这个线程的状态仍然是在等待中。那这个线程什么时候会退出呢?

答案是,只有等到满⾜进⼊InnoDB的条件后,ssionC的查询语句继续执⾏,然后才有可能判断到线程状态已经变成了KILL_QUERY

或者KILL_CONNECTION,再进⼊终⽌逻辑阶段。

到这⾥,我们来⼩结⼀下。

这个例⼦是kill⽆效的第⼀类情况,即:线程没有执⾏到判断线程状态的逻辑。跟这种情况相同的,还有由于IO压⼒过⼤,读写IO的函

数⼀直⽆法返回,导致不能及时判断线程的状态。

另⼀类情况是,终⽌逻辑耗时较长。这时候,从showprocesslist结果上看也是Command=Killed,需要等到终⽌逻辑完成,语句才算

真正完成。这类情况,⽐较常见的场景有以下⼏种:

1.超⼤事务执⾏期间被kill。这时候,回滚操作需要对事务执⾏期间⽣成的所有新数据版本做回收操作,耗时很长。

2.⼤查询回滚。如果查询过程中⽣成了⽐较⼤的临时⽂件,加上此时⽂件系统压⼒⼤,删除临时⽂件可能需要等待IO资源,导致耗时较

长。

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

如果直接在客户端通过Ctrl+C命令,是不是就可以直接终⽌线程呢?

答案是,不可以。

这⾥有⼀个误解,其实在客户端的操作只能操作到客户端的线程,客户端和服务端只能通过⽹络交互,是不可能直接操作服务端线程的。

⽽由于MySQL是停等协议,所以这个线程执⾏的语句还没有返回的时候,再往这个连接⾥⾯继续发命令也是没有⽤的。实际上,执⾏

Ctrl+C的时候,是MySQL客户端另外启动⼀个连接,然后发送⼀个killquery命令。

所以,你可别以为在客户端执⾏完Ctrl+C就万事⼤吉了。因为,要kill掉⼀个线程,还涉及到后端的很多操作。

另外两个关于客户端的误解

第⼀个误解是:如果库⾥⾯的表特别多,连接就会很慢。

有些线上的库,会包含很多表(我见过最多的⼀个库⾥有6万个表)。这时候,你就会发现,每次⽤客户端连接都会卡在下⾯这个界⾯上。

连接等待

⽽如果db1这个库⾥表很少的话,连接起来就会很快,可以很快进⼊输⼊命令的状态。因此,有同学会认为是表的数⽬影响了连接性能。

在SQL查询语句执⾏过程讲到过,每个客户端在和服务端建⽴连接的时候,需要做的事情就是TCP握⼿、⽤户校验、获取权限。但这⼏个操

作,显然跟库⾥⾯表的个数⽆关。

但实际上,正如图中的⽂字提⽰所说的,当使⽤默认参数连接的时候,MySQL客户端会提供⼀个本地库名和表名补全的功能。为了实现这个

功能,客户端在连接成功后,需要多做⼀些操作:

1.执⾏showdatabas;

2.切到db1库,执⾏showtables;

3.把这两个命令的结果⽤于构建⼀个本地的哈希表。

在这些操作中,最花时间的就是第三步在本地构建哈希表的操作。所以,当⼀个库中的表个数⾮常多的时候,这⼀步就会花⽐较长的时间。

也就是说,我们感知到的连接过程慢,其实并不是连接慢,也不是服务端慢,⽽是客户端慢。

图中的提⽰也说了,如果在连接命令中加上-A,就可以关掉这个⾃动补全的功能,然后客户端就可以快速返回了。

这⾥⾃动补全的效果就是,你在输⼊库名或者表名的时候,输⼊前缀,可以使⽤Tab键⾃动补全表名或者显⽰提⽰。

实际使⽤中,如果你⾃动补全功能⽤得并不多,建议你每次使⽤的时候都默认加-A。

其实提⽰⾥⾯没有说,除了加-A以外,加–quick(或者简写为-q)参数,也可以跳过这个阶段。但是,这个–quick是⼀个更容易引起误会

的参数,也是关于客户端常见的⼀个误解。

你看到这个参数,是不是觉得这应该是⼀个让服务端加速的参数?但实际上恰恰相反,设置了这个参数可能会降低服务端的性能。为什么这么

说呢?

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

1.⼀种是本地缓存,也就是在本地开⼀⽚内存,先把结果存起来。如果你⽤API开发,对应的就是mysql_store_result⽅法。

2.另⼀种是不缓存,读⼀个处理⼀个。如果你⽤API开发,对应的就是mysql_u_result⽅法。

MySQL客户端默认采⽤第⼀种⽅式,⽽如果加上–quick参数,就会使⽤第⼆种不缓存的⽅式。

采⽤不缓存的⽅式时,如果本地处理得慢,就会导致服务端发送结果被阻塞,因此会让服务端变慢。关于服务端的具体⾏为,下⽂再展开说

明。

那你会说,既然这样,为什么要给这个参数取名叫作quick呢?这是因为使⽤这个参数可以达到以下三点效果:

1.第⼀点,就是前⾯提到的,跳过表名⾃动补全功能。

2.第⼆点,mysql_store_result需要申请本地内存来缓存查询结果,如果查询结果太⼤,会耗费较多的本地内存,可能会影响客户端本

地机器的性能;

3.第三点,是不会把执⾏命令记录到本地的命令历史⽂件。

所以你看到了,–quick参数的意思,是让客户端变得更快。

⼩结

本⽂介绍了MySQL中,有些语句和连接“kill不掉”的情况。

这些“kill不掉”的情况,其实是因为发送kill命令的客户端,并没有强⾏停⽌⽬标线程的执⾏,⽽只是设置了个状态,并唤醒对应的线程。⽽

被kill的线程,需要执⾏到判断状态的“埋点”,才会开始进⼊终⽌逻辑阶段。并且,终⽌逻辑本⾝也是需要耗费时间的。

所以,如果你发现⼀个线程处于Killed状态,你可以做的事情就是,通过影响系统环境,让这个Killed状态尽快结束。

⽐如,如果是第⼀个例⼦⾥InnoDB并发度的问题,你就可以临时调⼤innodb_thread_concurrency的值,或者停掉别的线程,让出位⼦

给这个线程执⾏。

⽽如果是回滚逻辑由于受到IO资源限制执⾏得⽐较慢,就通过减少系统压⼒让它加速。

做完这些操作后,其实你已经没有办法再对它做什么了,只能等待流程⾃⼰完成。

本文发布于:2022-12-31 18:25:42,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/67076.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:gray
下一篇:large
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图