MySQL中2個select被阻塞場景的原因

aoerqileng發表於2022-06-10

第一種情況: 執行select,慢查詢,然後對錶alter,被阻塞,然後其他會話執行select,被阻塞


當最後的select被阻塞時,在

open_table_get_mdl_lock中會去獲取鎖,先 try_acquire_lock_impl獲取,成功返回,不成功向等待佇列加個ticket,進入等待
mysqld!open_table_get_mdl_lock(THD*, Open_table_context*, TABLE_LIST*, unsigned int, MDL_ticket**) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:2680)
mysqld!open_table(THD*, TABLE_LIST*, Open_table_context*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:3058)
mysqld!open_and_process_table(THD*, LEX*, TABLE_LIST*, unsigned int*, Prelocking_strategy*, bool, Open_table_context*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:5018)
mysqld!open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:5805)
mysqld!open_tables_for_query(THD*, TABLE_LIST*, unsigned int) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:6678)
mysqld!Sql_cmd_dml::prepare(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:363)
mysqld!Sql_cmd_dml::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:559)
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)



第二種場景

第一個會話執行慢查詢,第二個執行flush tables with read lock,第三個會話繼續執行查詢


原因是第三個被阻塞在下面的函式

tdc_wait_for_old_version
mysql_mutex_lock(&LOCK_open);
if ((share = get_cached_table_share(db, table_name)) &&
share->has_old_version()) {
struct timespec abstime;
set_timespec(&abstime, wait_timeout);
res = share->wait_for_old_version(thd, &abstime, deadlock_weight);
}
mysql_mutex_unlock(&LOCK_open);
return res;


下面的version()是在table share中獲取的version,第一個select執行後沒有變,refresh_version是在函式close_cached_tables中增加的,每次flush tables with read lock,都會呼叫。所以導致下面的判斷認為存在老版本,導致第三個回話進入share->wait_for_old_version 等待中

bool has_old_version() const { return version() != refresh_version; }


等待的堆疊如下:

mysqld!tdc_wait_for_old_version(THD*, char const*, char const*, unsigned long, unsigned int) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:2717)
mysqld!open_table(THD*, TABLE_LIST*, Open_table_context*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:3303)
mysqld!open_and_process_table(THD*, LEX*, TABLE_LIST*, unsigned int*, Prelocking_strategy*, bool, Open_table_context*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:5018)
mysqld!open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:5805)
mysqld!open_tables_for_query(THD*, TABLE_LIST*, unsigned int) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_base.cc:6678)
mysqld!Sql_cmd_dml::prepare(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:363)
mysqld!Sql_cmd_dml::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-8.0.23/sql/sql_select.cc:559)
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)


refresh_version的重新整理是不是能放到後面,獲取鎖成功後在刷,這樣就不會阻塞select了。

有興趣學習原始碼的加群一起學習啊 QQ:  700072075

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

相關文章