mysql8 log_status 查詢pos與gtid不一致 bug解析

aoerqileng發表於2022-06-20

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中提到的擴大鎖範圍。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25719946/viewspace-2901660/,如需轉載,請註明出處,否則將追究法律責任。

相關文章