Cherry-picking a patch from Bug 12828477 from mysql-5.5
to mysql-5.5.16-release. Original revision: # revision-id: dmitry.lenev@oracle.com-20110811155849-feyt3h7tj48padiu # parent: tatjana.nuernberg@oracle.com-20110811120945-c6x9a5d2du8s9oj2 # committer: Dmitry Lenev <Dmitry.Lenev@oracle.com> # branch nick: mysql-5.5-12828477 # timestamp: Thu 2011-08-11 19:58:49 +0400 # message: # Fix for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD # FOR CERTAIN QUERIES TO INFORMATION_SCHEMA". # # The problem was that metadata locking subsystem introduced # too much overhead for queries to I_S which were processed by # opening only .FRM or .TRG files and had to scanned a lot of # tables (e.g. SELECT COUNT(*) FROM I_S.TRIGGERS was affected). # The same effect was not observed for similar queries which # performed full-blown table open in order to fill I_S table. # # The problem stemmed from the fact that in case when I_S # implementation opened only .FRM or .TRG file for each table # processed it didn't release metadata lock it has acquired on # the table after finishing its processing. As result, list # of acquired metadata locks were growing until the end of # statement. Since acquisition of each new lock required # search in the list of already acquired locks performance # degraded. # # The same effect is not observed when I_S implementation # performs full-blown table open for each table being # processed, as in the latter cases metadata lock on the # table is released right after table processing. # # This fix addressed the problem by ensuring that I_S # implementation releases metadata lock after processing # the table in both cases of full-blown table open and in # case when only .FRM or .TRG file is read.
This commit is contained in:
parent
6e5bbf5138
commit
457cbab0d6
@ -1849,5 +1849,119 @@ unlock tables;
|
||||
drop table t1;
|
||||
drop view v1;
|
||||
#
|
||||
# Test for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR
|
||||
# CERTAIN QUERIES TO INFORMATION_SCHEMA".
|
||||
#
|
||||
# Check that metadata locks which are acquired during the process
|
||||
# of opening tables/.FRMs/.TRG files while filling I_S table are
|
||||
# not kept to the end of statement. Keeping the locks has caused
|
||||
# performance problems in cases when big number of tables (.FRMs
|
||||
# or .TRG files) were scanned as cost of new lock acquisition has
|
||||
# increased linearly.
|
||||
drop database if exists mysqltest;
|
||||
create database mysqltest;
|
||||
use mysqltest;
|
||||
create table t0 (i int);
|
||||
create table t1 (j int);
|
||||
create table t2 (k int);
|
||||
#
|
||||
# Test that we don't keep locks in case when we to fill
|
||||
# I_S table we perform full-blown table open.
|
||||
#
|
||||
# Acquire lock on 't2' so upcoming RENAME is
|
||||
# blocked.
|
||||
lock tables t2 read;
|
||||
#
|
||||
# Switching to connection 'con12828477_1'.
|
||||
#
|
||||
# The below RENAME should wait on 't2' while
|
||||
# keeping X lock on 't1'.
|
||||
rename table t1 to t3, t2 to t1, t3 to t2;
|
||||
#
|
||||
# Switching to connection 'con12828477_2'.
|
||||
#
|
||||
# Wait while the above RENAME is blocked.
|
||||
# Issue query to I_S which will open 't0' and get
|
||||
# blocked on 't1' because of RENAME.
|
||||
select table_name, auto_increment from information_schema.tables where table_schema='mysqltest';
|
||||
#
|
||||
# Switching to connection 'con12828477_3'.
|
||||
#
|
||||
# Wait while the above SELECT is blocked.
|
||||
#
|
||||
# Check that it holds no lock on 't0' so it can be renamed.
|
||||
rename table t0 to t4;
|
||||
#
|
||||
# Switching to connection 'default'.
|
||||
#
|
||||
#
|
||||
# Unblock the first RENAME.
|
||||
unlock tables;
|
||||
#
|
||||
# Switching to connection 'con12828477_1'.
|
||||
#
|
||||
# Reap the first RENAME
|
||||
#
|
||||
# Switching to connection 'con12828477_2'.
|
||||
#
|
||||
# Reap SELECT to I_S.
|
||||
table_name auto_increment
|
||||
t0 NULL
|
||||
t1 NULL
|
||||
t2 NULL
|
||||
#
|
||||
# Switching to connection 'default'.
|
||||
#
|
||||
#
|
||||
# Now test that we don't keep locks in case when we to fill
|
||||
# I_S table we read .FRM or .TRG file only (this was the case
|
||||
# for which problem existed).
|
||||
#
|
||||
rename table t4 to t0;
|
||||
# Acquire lock on 't2' so upcoming RENAME is
|
||||
# blocked.
|
||||
lock tables t2 read;
|
||||
#
|
||||
# Switching to connection 'con12828477_1'.
|
||||
#
|
||||
# The below RENAME should wait on 't2' while
|
||||
# keeping X lock on 't1'.
|
||||
rename table t1 to t3, t2 to t1, t3 to t2;
|
||||
#
|
||||
# Switching to connection 'con12828477_2'.
|
||||
#
|
||||
# Wait while the above RENAME is blocked.
|
||||
# Issue query to I_S which will open 't0' and get
|
||||
# blocked on 't1' because of RENAME.
|
||||
select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest';
|
||||
#
|
||||
# Switching to connection 'con12828477_3'.
|
||||
#
|
||||
# Wait while the above SELECT is blocked.
|
||||
#
|
||||
# Check that it holds no lock on 't0' so it can be renamed.
|
||||
rename table t0 to t4;
|
||||
#
|
||||
# Switching to connection 'default'.
|
||||
#
|
||||
#
|
||||
# Unblock the first RENAME.
|
||||
unlock tables;
|
||||
#
|
||||
# Switching to connection 'con12828477_1'.
|
||||
#
|
||||
# Reap the first RENAME
|
||||
#
|
||||
# Switching to connection 'con12828477_2'.
|
||||
#
|
||||
# Reap SELECT to I_S.
|
||||
event_object_table trigger_name
|
||||
#
|
||||
# Switching to connection 'default'.
|
||||
#
|
||||
#
|
||||
# Clean-up.
|
||||
drop database mysqltest;
|
||||
#
|
||||
# End of 5.5 tests
|
||||
#
|
||||
|
@ -1543,8 +1543,6 @@ DROP TABLE t1, information_schema.tables;
|
||||
LOCK TABLES t1 READ, information_schema.tables READ;
|
||||
DROP TABLE t1;
|
||||
|
||||
# Wait till all disconnects are completed
|
||||
--source include/wait_until_count_sessions.inc
|
||||
|
||||
#
|
||||
# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query
|
||||
@ -1608,6 +1606,186 @@ drop table t1;
|
||||
drop view v1;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Test for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR
|
||||
--echo # CERTAIN QUERIES TO INFORMATION_SCHEMA".
|
||||
--echo #
|
||||
--echo # Check that metadata locks which are acquired during the process
|
||||
--echo # of opening tables/.FRMs/.TRG files while filling I_S table are
|
||||
--echo # not kept to the end of statement. Keeping the locks has caused
|
||||
--echo # performance problems in cases when big number of tables (.FRMs
|
||||
--echo # or .TRG files) were scanned as cost of new lock acquisition has
|
||||
--echo # increased linearly.
|
||||
--disable_warnings
|
||||
drop database if exists mysqltest;
|
||||
--enable_warnings
|
||||
create database mysqltest;
|
||||
use mysqltest;
|
||||
create table t0 (i int);
|
||||
create table t1 (j int);
|
||||
create table t2 (k int);
|
||||
|
||||
--echo #
|
||||
--echo # Test that we don't keep locks in case when we to fill
|
||||
--echo # I_S table we perform full-blown table open.
|
||||
--echo #
|
||||
|
||||
--echo # Acquire lock on 't2' so upcoming RENAME is
|
||||
--echo # blocked.
|
||||
lock tables t2 read;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_1'.
|
||||
--echo #
|
||||
connect (con12828477_1, localhost, root,,mysqltest);
|
||||
--echo # The below RENAME should wait on 't2' while
|
||||
--echo # keeping X lock on 't1'.
|
||||
--send rename table t1 to t3, t2 to t1, t3 to t2
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_2'.
|
||||
--echo #
|
||||
connect (con12828477_2, localhost, root,,mysqltest);
|
||||
--echo # Wait while the above RENAME is blocked.
|
||||
let $wait_condition=
|
||||
select count(*) = 1 from information_schema.processlist
|
||||
where state = "Waiting for table metadata lock" and
|
||||
info = "rename table t1 to t3, t2 to t1, t3 to t2";
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo # Issue query to I_S which will open 't0' and get
|
||||
--echo # blocked on 't1' because of RENAME.
|
||||
--send select table_name, auto_increment from information_schema.tables where table_schema='mysqltest'
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_3'.
|
||||
--echo #
|
||||
connect (con12828477_3, localhost, root,,mysqltest);
|
||||
--echo # Wait while the above SELECT is blocked.
|
||||
let $wait_condition=
|
||||
select count(*) = 1 from information_schema.processlist
|
||||
where state = "Waiting for table metadata lock" and
|
||||
info = "select table_name, auto_increment from information_schema.tables where table_schema='mysqltest'";
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo #
|
||||
--echo # Check that it holds no lock on 't0' so it can be renamed.
|
||||
rename table t0 to t4;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'default'.
|
||||
--echo #
|
||||
connection default;
|
||||
--echo #
|
||||
--echo # Unblock the first RENAME.
|
||||
unlock tables;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_1'.
|
||||
--echo #
|
||||
connection con12828477_1;
|
||||
--echo # Reap the first RENAME
|
||||
--reap
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_2'.
|
||||
--echo #
|
||||
connection con12828477_2;
|
||||
--echo # Reap SELECT to I_S.
|
||||
--reap
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'default'.
|
||||
--echo #
|
||||
connection default;
|
||||
|
||||
--echo #
|
||||
--echo # Now test that we don't keep locks in case when we to fill
|
||||
--echo # I_S table we read .FRM or .TRG file only (this was the case
|
||||
--echo # for which problem existed).
|
||||
--echo #
|
||||
|
||||
rename table t4 to t0;
|
||||
--echo # Acquire lock on 't2' so upcoming RENAME is
|
||||
--echo # blocked.
|
||||
lock tables t2 read;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_1'.
|
||||
--echo #
|
||||
connection con12828477_1;
|
||||
--echo # The below RENAME should wait on 't2' while
|
||||
--echo # keeping X lock on 't1'.
|
||||
--send rename table t1 to t3, t2 to t1, t3 to t2
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_2'.
|
||||
--echo #
|
||||
connection con12828477_2;
|
||||
--echo # Wait while the above RENAME is blocked.
|
||||
let $wait_condition=
|
||||
select count(*) = 1 from information_schema.processlist
|
||||
where state = "Waiting for table metadata lock" and
|
||||
info = "rename table t1 to t3, t2 to t1, t3 to t2";
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo # Issue query to I_S which will open 't0' and get
|
||||
--echo # blocked on 't1' because of RENAME.
|
||||
--send select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest'
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_3'.
|
||||
--echo #
|
||||
connection con12828477_3;
|
||||
--echo # Wait while the above SELECT is blocked.
|
||||
let $wait_condition=
|
||||
select count(*) = 1 from information_schema.processlist
|
||||
where state = "Waiting for table metadata lock" and
|
||||
info = "select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest'";
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo #
|
||||
--echo # Check that it holds no lock on 't0' so it can be renamed.
|
||||
rename table t0 to t4;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'default'.
|
||||
--echo #
|
||||
connection default;
|
||||
--echo #
|
||||
--echo # Unblock the first RENAME.
|
||||
unlock tables;
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_1'.
|
||||
--echo #
|
||||
connection con12828477_1;
|
||||
--echo # Reap the first RENAME
|
||||
--reap
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'con12828477_2'.
|
||||
--echo #
|
||||
connection con12828477_2;
|
||||
--echo # Reap SELECT to I_S.
|
||||
--reap
|
||||
|
||||
--echo #
|
||||
--echo # Switching to connection 'default'.
|
||||
--echo #
|
||||
connection default;
|
||||
disconnect con12828477_1;
|
||||
disconnect con12828477_2;
|
||||
disconnect con12828477_3;
|
||||
|
||||
--echo #
|
||||
--echo # Clean-up.
|
||||
drop database mysqltest;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # End of 5.5 tests
|
||||
--echo #
|
||||
|
||||
# Wait till all disconnects are completed
|
||||
--source include/wait_until_count_sessions.inc
|
||||
|
@ -3157,6 +3157,10 @@ end:
|
||||
*/
|
||||
thd->temporary_tables= NULL;
|
||||
close_thread_tables(thd);
|
||||
/*
|
||||
Release metadata lock we might have acquired.
|
||||
See comment in fill_schema_table_from_frm() for details.
|
||||
*/
|
||||
thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
|
||||
|
||||
thd->lex= old_lex;
|
||||
@ -3339,6 +3343,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
|
||||
@param[in] db_name database name
|
||||
@param[in] table_name table name
|
||||
@param[in] schema_table_idx I_S table index
|
||||
@param[in] open_tables_state_backup Open_tables_state object which is used
|
||||
to save/restore original state of metadata
|
||||
locks.
|
||||
@param[in] can_deadlock Indicates that deadlocks are possible
|
||||
due to metadata locks, so to avoid
|
||||
them we should not wait in case if
|
||||
@ -3356,6 +3363,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
|
||||
LEX_STRING *db_name,
|
||||
LEX_STRING *table_name,
|
||||
enum enum_schema_tables schema_table_idx,
|
||||
Open_tables_backup *open_tables_state_backup,
|
||||
bool can_deadlock)
|
||||
{
|
||||
TABLE *table= tables->table;
|
||||
@ -3501,13 +3509,27 @@ end_share:
|
||||
|
||||
end_unlock:
|
||||
mysql_mutex_unlock(&LOCK_open);
|
||||
/*
|
||||
Don't release the MDL lock, it can be part of a transaction.
|
||||
If it is not, it will be released by the call to
|
||||
MDL_context::rollback_to_savepoint() in the caller.
|
||||
*/
|
||||
|
||||
end:
|
||||
/*
|
||||
Release metadata lock we might have acquired.
|
||||
|
||||
Without this step metadata locks acquired for each table processed
|
||||
will be accumulated. In situation when a lot of tables are processed
|
||||
by I_S query this will result in transaction with too many metadata
|
||||
locks. As result performance of acquisition of new lock will suffer.
|
||||
|
||||
Of course, the fact that we don't hold metadata lock on tables which
|
||||
were processed till the end of I_S query makes execution less isolated
|
||||
from concurrent DDL. Consequently one might get 'dirty' results from
|
||||
such a query. But we have never promised serializability of I_S queries
|
||||
anyway.
|
||||
|
||||
We don't have any tables open since we took backup, so rolling back to
|
||||
savepoint is safe.
|
||||
*/
|
||||
DBUG_ASSERT(thd->open_tables == NULL);
|
||||
thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
|
||||
thd->clear_error();
|
||||
return res;
|
||||
}
|
||||
@ -3758,6 +3780,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
int res= fill_schema_table_from_frm(thd, tables, schema_table,
|
||||
db_name, table_name,
|
||||
schema_table_idx,
|
||||
&open_tables_state_backup,
|
||||
can_deadlock);
|
||||
|
||||
thd->pop_internal_handler();
|
||||
|
Loading…
x
Reference in New Issue
Block a user