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中提到的扩大锁范围。
