一个Kill不掉的MySQL会话

作者:秦广飞
爱可生 dba  团队成员,负责项目日常问题处理及公司平台问题排查,对数据库有兴趣,对技术有想法。一入 it 深似海,从此节操是路人。
本文来源: 爱可生开源社区
1背景
照例要先讲下本文档背景的,不过在介绍背景之前,先简单说下 mysql 主从切换的过程。
正常来说,当要发生主从切换时,主库要做下面几个动作:
断开流量入口(解绑 vip)
设置只读
kill 掉数据库残留连接
而从库,要做下面几个动作:
补全与主库差异的 binlog 日志
关闭只读
清除复制信息
开启流量入口(绑定 vip)
我们公司自研的数据库集群管理平台 云树 dmp[1] 大概也是这么个切换过程,而这个切换过程跟本文的关联点,就在主库 kill 掉残留连接上。
偶然间发现,dmp 在切换过程中 kill 残留连接时,日志中有时会出现 warn 信息:[warn] kill process warning:error 1094:unknown thread id:4
后来观察到,mysql 5.7 的主从切换时,就不会出现这个 warning 信息,而 mysql 8.0 就会稳定复现。进一步测试验证后,终于发现了这个 unknown thread id 的真面目,就是 user 为 event_scheduler 的这个连接。
2什么是 event_scheduler?
event_scheduler 到底是什么呢?毕竟从 processlist 信息中可以看到,它与普通的会话似乎不太一样。
其实它是 mysql 中的一个特殊线程,主要负责执行 mysql 事件调度器所创建的事件。我们知道 mysql 是有 event 的,可以像 linux 中 crontab 一样,定时执行一些任务。
the mysql event scheduler manages the scheduling and execution of events, that is, tasks that run according to a schedule
当 mysql 事件调度器启用时 event_scheduler=on,mysql 就会在后台启动一个 event_scheduler 线程,并且 event_scheduler 线程将一直运行,直到 mysql 服务停止。该线程会负责检查当前时间和已定义的事件,如果事件需要执行,则 event_scheduler 线程将启动一个新的会话来执行事件。
需要注意的是,在 mysql 5.7中,event_scheduler 默认是关闭的,而 mysql 8.0 中则默认打开了,而这也就是为什么在 mysql 5.7 的切换过程中没有发现 warning 信息的原因。
3为什么 kill 不掉?
了解 event_scheduler 大概是什么之后,我们再来看看,为什么 kill 时,会报 unknown thread id。
注意看 processlist 信息,我们发现 event_scheduler 的 command 值为 daemon。从字面意思上看,daemon 为后台守护的意思,其实在 mysql 中,当在后台运行一些特殊的功能时,会话 command 可能被标记为 daemon(实际工作场景中,只注意到过 event_scheduler)。
因为这类会话并不是由用户直接发起的连接,而是 mysql 内部的线程,所以无法像普通会话一样被 kill 掉。
官方文档中,给出的信息较少,大家有兴趣的可以自己翻下代码。
4如何使用定时任务?
具体如何使用定时任务,其实网上也有很多资料,如果真有需要使用的,建议最好参考官方文档。下面我们简单使用下 event 看看效果。
启用/关闭/禁用
-- 修改变量 event_scheduler 来动态启用或者关闭 eventmysql> show variables like '%event_scheduler%';+-----------------+-------+| variable_name   | value |+-----------------+-------+| event_scheduler | on    |+-----------------+-------+1 row in set (0.00 sec)mysql> -- 关闭mysql> set global event_scheduler = 0;-- 启用mysql> set global event_scheduler = 1;-- 禁用 event_scheduler,只能在配置文件中设置 event_scheduler 为 disable 并重启服务,而不能动态修改[mysqld]event_scheduler=disabled  
创建
-- 准备测试表和数据mysql> create table logs(id int(11) primary key auto_increment,log_message varchar(255) not null,log_time timestamp not null);query ok, 0 rows affected, 1 warning (0.02 sec)mysql> insert into logs (log_message, log_time) values    -> ('君不见黄河之水天上来,奔流到海不复回', '2023-06-07 0900'),    -> ('君不见高堂明镜悲白发,朝如青丝暮成雪', '2023-06-07 2300'),    -> ('人生得意须尽欢,莫使金樽空对月', '2023-06-08 0100'),    -> ('天生我材必有用,千金散尽还复来', '2023-06-08 1800'),    -> ('烹羊宰牛且为乐,会须一饮三百杯', '2023-06-09 2300'),    -> ('钟鼓馔玉不足贵,但愿长醉不复醒', '2023-06-09 1100'),    -> ('古来圣贤皆寂寞,惟有饮者留其名', '2023-06-10 2300'),    -> ('陈王昔时宴平乐,斗酒十千恣欢谑', '2023-06-11 0100'),    -> ('主人何为言少钱,径须沽取对君酌', '2023-06-12 1800'),    -> ('五花马、千金裘', '2023-06-13 2300'),    -> ('呼儿将出换美酒,与尔同销万古愁', '2023-06-14 1100');query ok, 11 rows affected (0.01 sec)records: 11  duplicates: 0  warnings: 0mysql> -- 创建event,实现定时将该日志表中 7 天之前的数据删除-- 为了快速看到效果,我们每分钟执行一次,一次删除 1 行mysql> create event delete_logs_event    ->   on schedule every 1 minute starts '2023-06-19 0000'    ->   do    ->     delete from logs    ->     where log_time  use universemysql> show eventsg*************************** 1. row ***************************                  db: universe                name: delete_logs_event             definer: root@localhost           time zone: system                type: recurring          execute at: null      interval value: 1      interval field: minute              starts: 2023-06-19 0000                ends: null              status: enabled          originator: 1862993913character_set_client: utf8mb4collation_connection: utf8mb4_0900_ai_ci  database collation: utf8mb4_bin1 row in set (0.00 sec)mysql>-- 通过 information_schema.events 可以看到更详细的信息mysql> select * from information_schema.eventsg*************************** 1. row ***************************       event_catalog: def        event_schema: universe          event_name: delete_logs_event             definer: root@localhost           time_zone: system          event_body: sql    event_definition: delete from logs    where log_time  -- 查看表中是否被定时删除mysql> select * from logs;+----+--------------------------------------------------------+---------------------+| id | log_message                                            | log_time            |+----+--------------------------------------------------------+---------------------+|  2 | 君不见高堂明镜悲白发,朝如青丝暮成雪                   | 2023-06-07 2300 ||  3 | 人生得意须尽欢,莫使金樽空对月                         | 2023-06-08 0100 ||  4 | 天生我材必有用,千金散尽还复来                         | 2023-06-08 1800 ||  5 | 烹羊宰牛且为乐,会须一饮三百杯                         | 2023-06-09 2300 ||  6 | 钟鼓馔玉不足贵,但愿长醉不复醒                         | 2023-06-09 1100 ||  7 | 古来圣贤皆寂寞,惟有饮者留其名                         | 2023-06-10 2300 ||  8 | 陈王昔时宴平乐,斗酒十千恣欢谑                         | 2023-06-11 0100 ||  9 | 主人何为言少钱,径须沽取对君酌                         | 2023-06-12 1800 || 10 | 五花马、千金裘                                         | 2023-06-13 2300 || 11 | 呼儿将出换美酒,与尔同销万古愁                         | 2023-06-14 1100 |+----+--------------------------------------------------------+---------------------+10 rows in set (0.00 sec)mysql> -- 查看 show processlist 中 event_scheduler 的信息,可以看到 stats 为 waiting for next activationmysql> select * from information_schema.processlist where user='event_scheduler';+-------+-----------------+-----------+------+---------+------+-----------------------------+------+| id    | user            | host      | db   | command | time | state                       | info |+-------+-----------------+-----------+------+---------+------+-----------------------------+------+| 12869 | event_scheduler | localhost | null | daemon  |   58 | waiting for next activation | null |+-------+-----------------+-----------+------+---------+------+-----------------------------+------+1 row in set (0.00 sec)mysql>-- 我们在从库上看下 event 的信息,可以看到 status 为 slaveside_disabled,因此不用担心从库重复执行 eventmysql> select * from information_schema.eventsg*************************** 1. row ***************************       event_catalog: def        event_schema: universe          event_name: delete_logs_event             definer: root@localhost           time_zone: system          event_body: sql    event_definition: delete from logs    where log_time  alter event delete_logs_event    ->   on schedule every 1 day starts '2023-06-19 0000'    ->   do    ->     delete from logs    ->     where log_time -- 可以看到 definer 已经变成了修改的用户,且时间间隔也修改为了 1 天,delete 语句也去掉了 limitmysql> select * from information_schema.eventsg*************************** 1. row ***************************       event_catalog: def        event_schema: universe          event_name: delete_logs_event             definer: qin@%           time_zone: system          event_body: sql    event_definition: delete from logs    where log_time  drop event delete_logs_event;query ok, 0 rows affected (0.01 sec)mysql> show eventsgempty set (0.00 sec)  
切换时注意
当在主库上创建了 event,之后发生了主从切换。此时 event 并不会随着切换而变成在新主上执行,且状态也不会发生改变。
即原主 event 的状态还是 enabled,而新主 event 的状态还是 disabled。
5总结
show processlist 中看到的 user 为 event_scheduler 的会话为 mysql 内部线程,无法被 kill 掉。
在主库上创建的 event,定时执行的 sql 语句,在从库上会正常随着复制回放,但不会被重复执行。
主从切换后,原主上的 event 不会在新主上执行。


疫情期间国外网络大“塞车” 我国信息基础建设表现差别显而易见
如何安全删除硬盘文件
一文带您区分有源滤波器和无源滤波器
轻生活科技的“小语种离线语音模块”:打破语言壁垒,开启智能新纪元
什么是激光电视机_激光电视机有什么作用
一个Kill不掉的MySQL会话
L5级自动驾驶到底能给我们带来什么?
回顾手机摄像头的发展历程
人脸识别图像技术的发展与挑战
Vivo现在计划推出V20智能手机
荣耀畅玩20Pro上市6400万四摄22.5W快充1699元
基于基因信息的肺癌预后预测模型助力风险评估
西部地区解决投影机的应用及解决办法
北斗三号卫星定位系统组网成功,中国民用定位市场将迎来爆发式增长
环形天线工作原理
浅谈国内VR市场发展和市场规模
变频器在工业电机应用中如何实现一拖多的功能及注意事项
NOLO VR与爱奇艺达成合作,发布小阅悦Plus游戏定位套装NOLO版
七夕给男友送什么礼物?蓝牙耳机排行榜为爱助攻
组态王与2台欧姆龙PLC的Host-Link无线通讯实例