mysql8 log_status 查询pos与gtid不一致 bug解析

来源:这里教程网 时间:2026-03-01 16:45:53 作者:

bug:  102175 已经详细的介绍了mysql8 log_status 查询结果出现pos跟gtid 不匹配的情况,导致备份后设置的gtid可能不正确。下面根据bug的描述,去看下代码,熟悉下相关的代码,按步骤在分析下。 主要的代码就是ordered_commit, 这部分中有binlog的组提交,flush sync,commit三个阶段。 sync  阶段在syncbinlog 后,设置了sync_binlog=1会更新binlog的位置信息,代码如下

if (flush_error == 0 && total_bytes > 0) {
DEBUG_SYNC(thd, "before_sync_binlog_file");
std::pair<bool, bool> result = sync_binlog_file(false);
sync_error = result.first;
}
if (update_binlog_end_pos_after_sync) {
THD *tmp_thd = final_queue;
const char *binlog_file = nullptr;
my_off_t pos = 0;
while (tmp_thd->next_to_commit != nullptr)
tmp_thd = tmp_thd->next_to_commit;
if (flush_error == 0 && sync_error == 0) {
tmp_thd->get_trans_fixed_pos(&binlog_file, &pos);
update_binlog_end_pos(binlog_file, pos);
}
}

commit阶段中,调用process_commit_stage_queue进行commit队列的处理,下面的代码更新gtid_executed

 /*
    Handle the GTID of the threads.
    gtid_executed table is kept updated even though transactions fail to be
    logged. That's required by slave auto positioning.
  */
gtid_state->update_commit_group(first);
在update_commit_group 中我们看到需要先获取一个global_sid_lock的读锁
void Gtid_state::update_commit_group(THD *first_thd) {
DBUG_TRACE;
bool gtid_threshold_breach = false;
  /*
    We are going to loop in all sessions of the group commit in order to avoid
    being taking and releasing the global_sid_lock and sidno_lock for each
    session.
  */
DEBUG_SYNC(first_thd, "update_gtid_state_before_global_sid_lock");
global_sid_lock->rdlock();
DEBUG_SYNC(first_thd, "update_gtid_state_after_global_sid_lock");
update_gtids_impl_lock_sidnos(first_thd);
for (THD *thd = first_thd; thd != nullptr; thd = thd->next_to_commit) {
bool is_commit = (thd->commit_error != THD::CE_COMMIT_ERROR);
if (update_gtids_impl_do_nothing(thd) ||
(!is_commit && update_gtids_impl_check_skip_gtid_rollback(thd)))
continue;
bool more_trx_with_same_gtid_next = update_gtids_impl_begin(thd);
if (!gtid_threshold_breach)
gtid_threshold_breach = (thd->owned_gtid.gno > GNO_WARNING_THRESHOLD);
if (thd->owned_gtid.sidno == THD::OWNED_SIDNO_GTID_SET) {
update_gtids_impl_own_gtid_set(thd, is_commit);
} else if (thd->owned_gtid.sidno > 0) {
update_gtids_impl_own_gtid(thd, is_commit);
} else if (thd->owned_gtid.sidno == THD::OWNED_SIDNO_ANONYMOUS) {
update_gtids_impl_own_anonymous(thd, &more_trx_with_same_gtid_next);
} else {
update_gtids_impl_own_nothing(thd);
}
update_gtids_impl_end(thd, more_trx_with_same_gtid_next);
}
update_gtids_impl_broadcast_and_unlock_sidnos();
global_sid_lock->unlock();
if (gtid_threshold_breach)
LogErr(WARNING_LEVEL, ER_WARN_GTID_THRESHOLD_BREACH);
}

上面是组提交binlog 修改已经gtid修改的情况,下面看下select * from performance_schema.log_status;的情况 通过debug跟踪可以看到table_log_status::make_row()函数中有相关的锁情况

  /* Lock all resources */
for (it = resources.begin(); it != resources.end(); ++it) {
(*it)->lock();
}

通过跟踪,会执行下面函数,请求binlog log_lock

void Log_resource_binlog_wrapper::lock() {
mysql_mutex_lock(binlog->get_log_lock());
}

以及global_sid_lock的写锁

void Log_resource_gtid_state_wrapper::lock() { global_sid_lock->wrlock(); }

堆栈如下

mysqld!Log_resource_gtid_state_wrapper::lock() (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/log_resource.cc:89)
mysqld!table_log_status::make_row() (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/storage/perfschema/table_log_status.cc:256)
mysqld!table_log_status::rnd_next() (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/storage/perfschema/table_log_status.cc:93)
mysqld!ha_perfschema::rnd_next(unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/storage/perfschema/ha_perfschema.cc:1641)
mysqld!handler::ha_rnd_next(unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/handler.cc:2980)
mysqld!TableScanIterator::Read() (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/records.cc:361)
mysqld!SELECT_LEX_UNIT::ExecuteIteratorQuery(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_union.cc:1228)
mysqld!SELECT_LEX_UNIT::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_union.cc:1281)
mysqld!Sql_cmd_dml::execute_inner(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:827)
mysqld!Sql_cmd_dml::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:612)
mysqld!mysql_execute_command(THD*, bool) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_parse.cc:4407)
mysqld!dispatch_sql_command(THD*, Parser_state*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_parse.cc:4988)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_parse.cc:1836)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_parse.cc:1320)
mysqld!handle_connection(void*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/conn_handler/connection_handler_per_thread.cc:301)
mysqld!pfs_spawn_thread(void*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/storage/perfschema/pfs.cc:2900)
libsystem_pthread.dylib!_pthread_start (Unknown Source:0)
libsystem_pthread.dylib!thread_start (Unknown Source:0)

所以在sync后,更新了binlog的值,这个时候查询log_status,查询获取了写锁,组提交到commit阶段,无法获取读锁,导致没法更新gtid,所以查询的log_status是不匹配的,log_status应该在commit,更新gtid后,在获取锁资源读取,也就是bug中提到的扩大锁范围。

相关推荐