两个 kill 命令
-
- kill query + 线程 id
- 终止这个线程中正在执行的语句
-
- kill connection + 线程 id
- connection 可以不写
- 断开这个线程的连接
- 会先停止正在执行的语句
收到 kill 以后,线程做什么?
-
告诉执行线程:这条语句已经不需要继续执行了,可以开始“执行停止的逻辑了”
- 跟 Linux 的 kill 命令类似,kill -N pid 并不是让进程直接停止,而是给进程发一个信号,然后进程处理这个信号,进入终止逻辑。只是对于 MySQL 的 kill 命令来说,不需要传信号量参数,就只有“停止”这个命令。
-
实现上,当用户执行 kill query thread_id_B 时,MySQL 里处理 kill 命令的线程做了两件事:
-
- 把 session B 的运行状态改成 THD::KILL_QUERY(将变量 killed 赋值为 THD::KILL_QUERY);
- 如果 session B 处于锁等待,并不能知道状态变化,还是会继续等待。
-
- 给 session B 的执行线程发一个信号。
- 发信号的目的:让 session B 退出等待,处理 1 设置的状态
-
kill 无效的两类情况
-
-
线程没有执行到判断线程状态的逻辑
- 相同的还有由于 IO 压力过大,读写 IO 的函数一直无法返回,导致不能及时判断线程的状态
- 语句执行到能判断到线程状态已经变成了 KILL_QUERY 或者 KILL_CONNECTION 的时候,再进入终止逻辑阶段。
- 如果一个线程的状态是KILL_CONNECTION,就把Command列显示成Killed。
-
-
-
终止逻辑耗时较长
-
- 超大事务执行期间被 kill,触发回滚操作
-
- 大查询回滚,查询过程生成了比较大的临时文件 + 此时文件系统压力大 ⇒ 删除临时文件可能需要等待 IO 资源
-
- DDL 命令执行到最后阶段,被 kill 需要删除中间过程的临时文件,同 2
-
客户端执行 Ctrl+C
- 是 MySQL 客户端另外启动一个连接发送一个 kill query 命令
另外两个关于客户端的误解
-
如果库里面的表特别多,连接就会很慢。
- 每个客户端在和服务端建立连接的时候,需要做的事情就是 TCP 握手、用户校验、获取权限。但这几个操作,跟库里面表的个数无关。(第一章)
- 我们感知到的连接过程慢,其实并不是连接慢,也不是服务端慢,而是客户端慢。
- 客户端在连接成功后:
- 执行 show databases
- 切到 db1 库,执行 show tables
- 把这两个命令的结果用于构建一个本地的哈希表(最耗时)
- 如果在连接命令中加上 -A,就可以关掉这个自动补全的功能,然后客户端就可以快速返回。
- –quick 也可以跳过
- 客户端在连接成功后:
-
–quick
-
是让客户端变快
-
MySQL 客户端发送请求后,接收服务端返回结果的方式有两种:
-
一种是本地缓存,也就是在本地开一片内存,先把结果存起来。如果你用 API 开发,对应的就是 mysql_store_result 方法。
-
另一种是不缓存,读一个处理一个。如果你用 API 开发,对应的就是 mysql_use_result 方法。
-
默认第一种
- 查询的返回结果不会很多的话,都推荐用这个
-
加上 -quick 参数后使用第二种
-
-
采用不缓存的方式,如果本地处理得慢,就会导致服务端发送结果被阻塞,因此会让服务端变慢
-
参数效果
-
- 跳过表名自动补全功能
-
- mysql_store_result 需要申请本地内存来缓存查询结果,如果查询结果太大,会耗费较多的本地内存
-
- 不会把执行命令记录到本地的命令历史文件
-
-
线程处于 Killed 状态
- 可以做的事情:通过影响系统环境,让 Killed 状态尽快结束
- 并发度问题,临时调大 innodb_thread_concurrency 的值或停掉别的线程,让出位子给这个线程执行
- 回滚逻辑由于 IO 资源限制,通过减少系统压力让它加速
思考题
-
如果你碰到一个被 killed 的事务一直处于回滚状态,你认为是应该直接把 MySQL 进程强行重启,还是应该让它自己执行完成呢?
- 让它自己结束
-
为什么呢?
- 因为重启之后该做的回滚动作不能少
- 可以先做主备切换,切到新主库提供服务
-
减少系统压力,加速终止逻辑
评论区
-
并非所有的 DDL 操作都可以通过主从切换来实现
- 改索引、 加最后一列、删最后一列
- 其他的大多数不行,比如删除中间一列
-
kill 的影响只有回滚,恢复到执行前的状态,没有其他
-
遇到错误时:pstack
> /tmp/pstack.1