MySQL中2个select被阻塞场景的原因

来源:这里教程网 时间:2026-03-01 16:45: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

相关推荐