MySQL 5.7&8.0 Bug 组提交参数设置不当导致事务提交hung住

2023年 8月 15日 82.5k 0

MySQL 5.7引入了组提交功能,组提交的两个参数binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count,如果设置不当,可能导致事务提交hung住。这个问题持续了多个版本,修改了两次源码,才最终完全修复。

第一次Bug:

见地址:https://bugs.mysql.com/bug.php?id=80652
根据Bug描述,当如下参数设置时,会触发事务提交hung住。

  • binlog_group_commit_sync_delay设置为1~9
  • binlog_group_commit_sync_no_delay_count设置为大于1

从官方提供的Release Note来看,这个Bug在5.7.17版本被修复。

对比一下 5.7.16 和 5.7.17 关于这个Bug的修改内容,源码文件位于 sql/binlog.cc 。

首先看5.7.16的这段代码,如下:

time_t Stage_manager::wait_count_or_timeout(ulong count, time_t usec, StageID stage)
{
time_t to_wait=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", LONG_MAX, usec);
/*
For testing purposes while waiting for inifinity
to arrive, we keep checking the queue size at regular,
small intervals. Otherwise, waiting 0.1 * infinite
is too long.
*/
time_t delta=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", 100000,
static_cast(to_wait * 0.1));

while (to_wait > 0 && (count == 0 || static_cast(m_queue[stage].get_size()) < count))
{
#ifndef DBUG_OFF
if (current_thd)
DEBUG_SYNC(current_thd, "bgc_wait_count_or_timeout");
#endif
my_sleep(delta);
to_wait -= delta;
}
return to_wait;
}

函数中的参数count就是binlog_group_commit_sync_no_delay_count,参数usec就是binlog_group_commit_sync_delay,当usec为1~9时,delta 为0,to_wait一直大于0,同时count 大于0,所以在循环中长时间出不来,表现为事务提交一直hung住。

下面来看看5.7.17版本如何修复的,看如下代码:

void Stage_manager::wait_count_or_timeout(ulong count, ulong usec, StageID stage)
{
ulong to_wait=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", LONG_MAX, usec);
/*
For testing purposes while waiting for inifinity
to arrive, we keep checking the queue size at regular,
small intervals. Otherwise, waiting 0.1 * infinite
is too long.
*/
ulong delta=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", 100000,
max(1, (to_wait * 0.1)));

while (to_wait > 0 && (count == 0 || static_cast(m_queue[stage].get_size()) < count))
{
#ifndef DBUG_OFF
if (current_thd)
DEBUG_SYNC(current_thd, "bgc_wait_count_or_timeout");
#endif
my_sleep(delta);
to_wait -= delta;
}
}

从代码对比来看,把数据类型 time_t 换成了 ulong,且delta取值必须大于等于1。貌似结解了问题,但是没有彻底解决。

第二次Bug:

分析上述代码的逻辑,如果usec>20,且不是10的整数倍,比如21,那么delta为2,多次循环后,to_wait会变成-1,是负数,我们看to_wait是ulong类型,无符号长整形,越界变成一个很大很大的无符号整数,while循环无法退出。

这个新的Bug见链接:
https://bugs.mysql.com/bug.php?id=91055

根据官方文档,新的Bug在 5.7.24 和 8.0.13版本彻底解决。

我们来看下5.7.24版本是如何彻底解决的,如下:

void Stage_manager::wait_count_or_timeout(ulong count, long usec, StageID stage)
{
long to_wait=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", LONG_MAX, usec);
/*
For testing purposes while waiting for inifinity
to arrive, we keep checking the queue size at regular,
small intervals. Otherwise, waiting 0.1 * infinite
is too long.
*/
long delta=
DBUG_EVALUATE_IF("bgc_set_infinite_delay", 100000,
max(1, (to_wait * 0.1)));

while (to_wait > 0 && (count == 0 || static_cast(m_queue[stage].get_size()) < count))
{
#ifndef DBUG_OFF
if (current_thd)
DEBUG_SYNC(current_thd, "bgc_wait_count_or_timeout");
#endif
my_sleep(delta);
to_wait -= delta;
}
}

仅仅修改了数据类型,将ulong改成了long,也就不存在to_wait 负数越界的问题了。

参考资料:
https://bugs.mysql.com/bug.php?id=80652
https://bugs.mysql.com/bug.php?id=91055
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-17.html
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-24.html

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论