MySQL · 捉虫动态 · event_scheduler 慢日志记错
问题背景
最近遇到了 event_scheduler 在记录慢日志时的一个 bug,在这里分享给大家。
为了方便描述问题,先构造一个简单的 event,如下:
delimiter //
create event event1 on schedule every 5 second starts now() ends date_add(now(), interval 1 hour)
do begin
select sleep(1);
select * from t1;
select sleep(2);
end //
delimiter ;
其中的 t1 表中,有 2 条记录。
同时打开 event_scheduer 和 slow_log,并把慢日志的时间设置为 1s。
set global event_scheduler = on;
set global slow_query_log = on;
set global long_query_time = 1;
set global log_output = 'TABLE';
待 event 执行段时间后,查询 slow_log 会看到如下的结果:
+---------------------+------------+------------+-----------+-----------+---------------+------+----------------+-----------+-----------+------------------+-----------+
| start_time | user_host | query_time | lock_time | rows_sent | rows_examined | db | last_insert_id | insert_id | server_id | sql_text | thread_id |
+---------------------+------------+------------+-----------+-----------+---------------+------+----------------+-----------+-----------+------------------+-----------+
| 2017-01-14 16:15:33 | root[root] | 00:00:01 | 00:00:00 | 1 | 0 | test | 0 | 0 | 1 | select sleep(1) | 4 |
| 2017-01-14 16:15:33 | root[root] | 00:00:01 | 00:00:01 | 3 | 2 | test | 0 | 0 | 1 | select * from t1 | 4 |
| 2017-01-14 16:15:35 | root[root] | 00:00:03 | 00:00:01 | 4 | 0 | test | 0 | 0 | 1 | select sleep(2) | 4 |
| 2017-01-14 16:15:38 | root[root] | 00:00:01 | 00:00:00 | 1 | 0 | test | 0 | 0 | 1 | select sleep(1) | 5 |
| 2017-01-14 16:15:38 | root[root] | 00:00:01 | 00:00:01 | 3 | 2 | test | 0 | 0 | 1 | select * from t1 | 5 |
| 2017-01-14 16:15:40 | root[root] | 00:00:03 | 00:00:01 | 4 | 0 | test | 0 | 0 | 1 | select sleep(2) | 5 |
+---------------------+------------+------------+-----------+-----------+---------------+------+----------------+-----------+-----------+------------------+-----------+
可以看到,slow_log 中的 select * from t1
和 select sleep(2)
相关记录是有问题的:
select * from t1
不应该被记为慢日志,同时其中的 lock_time(应该为0) 和 rows_sent(应该为2) 都是错的;select sleep(2)
的 query_time(应该为2),lock_time(应该为0),和 rows_sent(应该为1) 也都是错的。
rows_sent 记错比较好确认,query_time 和 lock_time 记错我们可以从 start_time 和 thread_id 对照确认,另外select sleep(2)
是没有拿锁的,不应该有等锁的时间。
问题分析
为了搞清楚这个问题,我们需要了解 slow_log 是怎么记的。
slow_log 是在语句执行完后记录的,因为加锁时间和返回记录数这些信息,在执行之后才知道,general_log 记录是在语句解析完执行前。
slow_log 是否记录判读的逻辑在 log_slow_applicable()
中:
a) 是否没有用到索引,并且 log_queries_not_using_indexes
打开(这种情况下还可能触发 throttle 导致不记录)
b) 被标记为慢 SQL(thd->server_status & SERVER_QUERY_WAS_SLOW
)
我们这里只关心 b) 这种 case。
在 SQL 执行结束时,会先调用 update_server_status()
来判断是否是慢 SQL,逻辑如下:
void update_server_status()
{ulonglong end_utime_of_query= current_utime();if (end_utime_of_query > utime_after_lock + variables.long_query_time)server_status|= SERVER_QUERY_WAS_SLOW;
}
utime_after_lock 表示的是拿到锁的时间点,server 层通过 THD::set_time_after_lock()
设置,引擎层(目前只有 InnoDB 支持)如果有锁请求等待时间的话,会累加到这个变量上,通过 thd_storage_lock_wait()
函数。
因此一个 SQL 因为等锁而导致执行时间长的话,是不会记入慢 SQL 的。
select * from t1
语句执行结束,调用 update_server_status()
时,根据执行时间判断是不满足慢 SQL 的,但是因为 event 在执行前 server_status 没有重置,后面调用 log_slow_applicable()
时,SERVER_QUERY_WAS_SLOW
这个标志位还在,因此最终记到 slow_log 里了。
因此一个 event 在执行中,其中只要有一个语句是慢 SQL,那么后面所有的都会被记成慢SQL。
而其中时间记错的原因也是这样,每次执行语句前,start_utime 没有重置,而 utime_after_lock
会在执行 select * from t1
时拿锁被更新。
query_time 和 lock_time 的计算逻辑如下(LOGGER::slow_log_print()
):
if (thd->start_utime){query_utime= (current_utime - thd->start_utime);lock_utime= (thd->utime_after_lock - thd->start_utime);}
因此 event 中语句的 query_time 一直是增加的,lock_time 也不是 0。
需要注意的是,start_time 记录的并不是语句开始执行的时间,而是记入 slow_log 时的时间。
rows_sent 也是因为没有重置,一直都是累加的,而 rows_examined 会在 JOIN::exec()
中被重置,因此记的是对的。
问题影响和解决
出现这种问题的前提是:
- 用了 MySQL 的 event_scheduler,并且 event 有多个 SQL 语句;
- 其中有一个 SQL 是慢SQL。
这个 bug 目前最新的 5.6.35/5.7.17 都受影响,官方已经确认,详见 bug#84450。
知道原因后,fix 也就比较简单了,在 event 中每个 SQL 语句执行前,把 server_status, start_utime, m_sent_rows_count 重置掉就好了。
正常的用户 SQL 的执行逻辑就是这么干的,在 mysql_parse()
里会调用 THD::reset_for_next_command()
,但是 event 执行过程中并没有调用这个函数。
MySQL · 捉虫动态 · event_scheduler 慢日志记错相关推荐
- MySQL · 捉虫动态 · show binary logs 灵异事件
问题背景 最近在运维 MySQL 中遇到一个神奇的问题,分享给大家.现象是这样的,show binary logs 没有返回结果,flush binary logs 后也不行, 但是 binlog 是 ...
- MySQL · 捉虫动态 · UK 包含 NULL 值备库延迟分析
前言 在之前的月报 RDS 只读实例延迟分析 中,我们介绍了一些常见的备库延迟的场景,今天给大家分享一个比较少见的特殊场景. 简单的来说,就是在 UK 索引中存在大量 NULL 值情况下,如果备库选用 ...
- MySQL · 捉虫动态 · 信号处理机制分析
背景 在 AliSQL 上面有人提交了一个 bug,在使用主备的时候 service stop mysql 不能关闭主库,一直显示 shutting down mysql -,到底怎么回事呢,先来看一 ...
- MySQL · 捉虫动态 · 并行复制外键约束问题二
背景 并行复制可以大大提高备库的 binlog 应用速度,内核月报也多次对并行复制特性进行介绍,感兴趣的朋友可以回顾下:5.6 并行复制实现分析.5.6 并行复制恢复实现 和 5.6并行复制事件分发机 ...
- MySQL内核月报 2015.01-MySQL · 捉虫动态· replicate filter 和 GTID 一起使用的问题
问题描述 当单个 MySQL 实例的数据增长到很多的时候,就会考虑通过库或者表级别的拆分,把当前实例的数据分散到多个实例上去,假设原实例为A,想把其中的5个库(db1/db2/db3/db4/db5) ...
- mysql auto position_MySQL内核月报 2015.01-MySQL · 捉虫动态· 设置 gtid_purged 破坏AUTO_POSITION复制协议-阿里云开发者社区...
bug描述 Oracle 最新发布的版本 5.6.22 中有这样一个关于GTID的bugfix,在主备场景下,如果我们在主库上 SET GLOBAL GTID_PURGED = "some_ ...
- mysql数据库表删了重建error_数据库内核月报 - 2015 / 09-MySQL · 捉虫动态 · 建表过程中crash造成重建表失败-阿里云开发者社区...
问题描述 主库的create table语句传到备库,备库SQL线程执行过程中报错: Error 'Can't create table 'XXX.XX' (errno: -1)' on query. ...
- MySQL内核月报 2015.01-MySQL · 捉虫动态· InnoDB自增列重复值问题
问题重现 先从问题入手,重现下这个bug 这里我们关闭mysql,再启动mysql,然后再插入一条数据 我们看到插入了(2,2),而如果我没有重启,插入同样数据我们得到的应该是(4,2). 上面的测试 ...
- rds mysql 表被删了_MySQL · 捉虫动态 · 删除索引导致表无法打开
问题背景 最近线上遇到一个问题,用户重启实例后发现有张表打不开了,经调研后发现是用户之前的霸蛮操作导致的,下面给出复现步骤: create table t1 (id int not null prim ...
最新文章
- Fungal Diversity:70位作者共同修订真菌界担子菌门分类系统
- 重置MYSQL的root 密码
- 记一次内存泄漏问题的排查经历
- 右键快捷VsCode
- Chrome常用快捷键整理汇总
- java中join的使用
- 另一种Background job的 debug方法
- Text模式和PDU模式的区别
- 【招聘】阿里2022届春招实习生 - 机器学习/NLP/CV等
- 数论之勾股数组(毕达哥拉斯三元组)
- TLS 系统默认版本.NET Framework 3.5.1 Windows 7 SP1 和 Server 2008 R2 SP1 中包含的支持
- 各个电脑品牌BIOS快捷启动热键
- 个人大学生工作室面试前期准备
- python读word文档计算字数,Python统计字数并将结果输入到TXT文件中
- Android自定义控件之实现快速检索
- pycharm编写的py文件如何在linux下通过 ./ 运行,初识shebang
- 设计美好的服务器(6)--SEDA架构笔记
- 毒蛙(Vuze)执行文件Azureus.exe再分析
- OCRM与ACRM的区别
- so文件反汇编反编译到C源码