32-为什么还有kill不掉的语句?
# Kill 命令
- KILL QUERY 线程 ID:这个命令会终止指定连接 ID 当前正在执行的语句,但是连接本身会保持不变。这意味着,如果一个长时间运行的查询阻塞了你的数据库,你可以使用 KILL QUERY 来只终止那个查询,而不影响到其他的操作。
- KILL CONNECTION 线程 ID:它会在终止任何该连接正在执行的语句后,终止与给定的 processlist_id 关联的连接。这意味着,不仅当前正在执行的查询会被终止,整个连接也会被关闭。
# 收到 Kill 后线程做什么?
Session A | Session B | Session C |
---|---|---|
begin; update t set c=c+1 where id =1 ; | ||
update t set c=c+1 where id =1 ; // blocking | ||
ERROR Query execution was interrupter | kill query thread_id_X; |
当对一个表进行增删改查操作时,会在表上加一个 DML 读锁,如果说执行了 kill 就直接杀死的话,这个 MDL 的读锁就无法释放了。
所以 Kill 不是立刻停止的意思,而是这个语句已经不需要执行继续执行了,就相当于一个信号。MySQL 里的 kill 执行做了两件事
- 把 B 的运行状态改成了 THD::KILL_QUERY,即将变量 killed 设置为 THD::KILL_QUERY。
- 给 B 的执行线程发一个信号,将其从阻塞状态唤醒,然后检查 killed 如果为 THD::KILL_QUERY,停止查询当前操作。
发信号的目的是,让线程退出等待来处理 THD::KILL_QUERY 的状态。这个过程中有多处埋点,在埋点处处理 THD::KILL_QUERY 状态,如果处于等待状态,必须是一个可以被唤醒的线程,否则根本不会执行到埋点处。
kill 不了的例子
- 线程没有执行到判断自己状态的逻辑。
- 终止逻辑耗时较长。
案例一
首先设置并发线程为 2,set global innodb_thread_concurrency=2。
Session A | Session B | Session C | Session D | Session E |
---|---|---|---|---|
select sleep(100) from t; | select sleep(100) from t; | |||
select * from t ;// blocking | ||||
kill query C | ||||
kill C | ||||
ERROR LOST Connection.... |
通过 show processlist 查看,这个命令还在执行,但是状态变成了 killed。
一个线程在等行锁的过程中使用 pthread_cond_timedwait 参数,表示的是每 10 毫秒进行一个判断看是否可以进入执行状态,如果不行就调用 nanosleep 进入 sleep 状态。虽然执行了 kill query 但是在循环过程中并没有判断状态。
而 kill connection 则是,先把状态设置为 KILL_CONNECTION 然后关掉线程的网络连接,所以这个时候出现了 LOST connection 的字样。show processlist 有个特性为:如果一个线程的状态是 KILL_CONNECTION,就把 Command 列显示成 Killed。
所以即便是客户端退出了,线程依旧是在等待中,因为它在等待状态,没有机会检查其状态并发现它已经被杀死了。
案例二
- 超大事务执行期间被 kill。这个时候回滚操作需要对所有的产生的数据进行回收。耗时很长。
- 大查询回滚。查询过程中生成了较大的临时文件,加上 IO 资源紧张,删除临时文件需要等待 IO。
- DDL 命令执行到最后阶段,如果被 kill,需要删除中间过程的临时文件,也可能受 IO 资源影响耗时较久。
笔记
MySQL 在以下情况下可能会生成临时文件:
排序操作:当你执行一个包含
ORDER BY
子句的查询时,如果排序的结果集不能被完全放入内存中,MySQL 会使用临时文件来帮助完成排序操作。分组操作:类似地,当你执行一个包含
GROUP BY
子句的查询时,如果分组的结果集不能被完全放入内存中,MySQL 也会使用临时文件来帮助完成分组操作。联接操作:当你执行一个大型的联接操作时,MySQL 可能会使用临时文件来存储联接的中间结果。
子查询或派生表:MySQL 在处理子查询或派生表时,可能会使用临时文件来存储中间结果。
UNION 操作:当你执行一个
UNION
操作时,MySQL 可能会使用临时文件来存储UNION
的结果。大型的 INSERT...SELECT 操作:当你执行一个大型的
INSERT...SELECT
操作时,MySQL 可能会使用临时文件来存储SELECT
的结果。
# 另外两个关于客户端的误解
mysql -uroot -p123456 db1
- 如果库里的表很多,连接就会很慢。因为在测试时候也会发现,如果一个库里有比如 6 万张表那么连接速度很慢,如果很少就很快。
- 但是每个客户端在和服务端建立连接的时候,需要做的事情就是 TCP 握手、用户校验、获取权限。但这几个操作,显然跟库里面表的个数无关。
- 因为在默认连接的时候,MySQL 客户端会提供一个本地库名和表名补全的功能。需要多做一些操作。
- show databases;
- use db1;
- show tables;
- 将上面的命令构建到一个本地 hash 表中,用来做 tab 键补全。
- 所以会发现库多了连接就慢了。
- 如果加上一个参数 -A 会关闭这个补全功能,就会快很多。
注意
除了加 -A 以外,加–quick (或者简写为 -q) 参数,也可以跳过这个阶段。但是,这个–quick 是一个更容易引起误会的参数,也是关于客户端常见的一个误解。但实际上恰恰相反,设置了这个参数可能会降低服务端的性能。
一旦使用了 - q 就会使用到 mysql_use_result 方式这个方式就是说读一行向客户端发送一行,如果说客户端处理的慢了。那么就会导致服务端处理被阻塞。
- mysql_store_result 需要申请本地内存来缓存查询结果,如果查询结果太大,会耗费较多的本地内存,可能会影响客户端本地机器的性能。
- 是不会把执行命令记录到本地的命令历史文件。