1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Patch that changes metadata locking subsystem to use mutex per lock and

condition variable per context instead of one mutex and one conditional
variable for the whole subsystem.

This should increase concurrency in this subsystem.

It also opens the way for further changes which are necessary to solve
such bugs as bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock"
and bug #37346 "innodb does not detect deadlock between update and alter
table".

Two other notable changes done by this patch:

- MDL subsystem no longer implicitly acquires global intention exclusive
  metadata lock when per-object metadata lock is acquired. Now this has
  to be done by explicit calls outside of MDL subsystem.
- Instead of using separate MDL_context for opening system tables/tables
  for purposes of I_S we now create MDL savepoint in the main context
  before opening tables and rollback to this savepoint after closing
  them. This means that it is now possible to get ER_LOCK_DEADLOCK error
  even not inside a transaction. This might happen in unlikely case when
  one runs DDL on one of system tables while also running DDL on some
  other tables. Cases when this ER_LOCK_DEADLOCK error is not justified
  will be addressed by advanced deadlock detector for MDL subsystem which
  we plan to implement.
This commit is contained in:
Dmitry Lenev
2010-01-21 23:43:03 +03:00
parent c005126107
commit a63f8480db
24 changed files with 1595 additions and 848 deletions

View File

@ -2207,22 +2207,26 @@ err:
locked. Additional check for 'non_temp_tables_count' is to avoid
leaving LOCK TABLES mode if we have dropped only temporary tables.
*/
if (thd->locked_tables_mode &&
thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
if (! thd->locked_tables_mode)
unlock_table_names(thd);
else
{
thd->locked_tables_list.unlock_locked_tables(thd);
goto end;
}
for (table= tables; table; table= table->next_local)
{
if (table->mdl_request.ticket)
if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
{
/*
Under LOCK TABLES we may have several instances of table open
and locked and therefore have to remove several metadata lock
requests associated with them.
*/
thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket);
thd->locked_tables_list.unlock_locked_tables(thd);
goto end;
}
for (table= tables; table; table= table->next_local)
{
if (table->mdl_request.ticket)
{
/*
Under LOCK TABLES we may have several instances of table open
and locked and therefore have to remove several metadata lock
requests associated with them.
*/
thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket);
}
}
}
}
@ -4349,6 +4353,14 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
if (!(table= table_list->table))
{
/*
If the table didn't exist, we have a shared metadata lock
on it that is left from mysql_admin_table()'s attempt to
open it. Release the shared metadata lock before trying to
acquire the exclusive lock to satisfy MDL asserts and avoid
deadlocks.
*/
thd->mdl_context.release_transactional_locks();
/*
Attempt to do full-blown table open in mysql_admin_table() has failed.
Let us try to open at least a .FRM for this table.
@ -4360,6 +4372,14 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
table_list->mdl_request.init(MDL_key::TABLE,
table_list->db, table_list->table_name,
MDL_EXCLUSIVE);
MDL_request mdl_global_request;
mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
if (thd->mdl_context.acquire_global_intention_exclusive_lock(
&mdl_global_request))
DBUG_RETURN(0);
if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request))
DBUG_RETURN(0);
has_mdl_lock= TRUE;
@ -4491,7 +4511,7 @@ end:
}
/* In case of a temporary table there will be no metadata lock. */
if (error && has_mdl_lock)
thd->mdl_context.release_lock(table_list->mdl_request.ticket);
thd->mdl_context.release_transactional_locks();
DBUG_RETURN(error);
}
@ -6544,6 +6564,13 @@ view_err:
{
target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
MDL_EXCLUSIVE);
/*
Global intention exclusive lock must have been already acquired when
table to be altered was open, so there is no need to do it here.
*/
DBUG_ASSERT(thd->
mdl_context.is_global_lock_owner(MDL_INTENTION_EXCLUSIVE));
if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request))
DBUG_RETURN(TRUE);
if (target_mdl_request.ticket == NULL)