mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-5597 - Reduce usage of LOCK_open: LOCK_flush
Replaced LOCK_flush with per-share conditional variable.
This commit is contained in:
@@ -20,7 +20,6 @@ where name like 'Wait/Synch/Rwlock/sql/%'
|
|||||||
order by name limit 10;
|
order by name limit 10;
|
||||||
NAME ENABLED TIMED
|
NAME ENABLED TIMED
|
||||||
wait/synch/rwlock/sql/LOCK_dboptions YES YES
|
wait/synch/rwlock/sql/LOCK_dboptions YES YES
|
||||||
wait/synch/rwlock/sql/LOCK_flush YES YES
|
|
||||||
wait/synch/rwlock/sql/LOCK_grant YES YES
|
wait/synch/rwlock/sql/LOCK_grant YES YES
|
||||||
wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
|
wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
|
||||||
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
|
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
|
||||||
@@ -29,6 +28,7 @@ wait/synch/rwlock/sql/LOCK_tdc YES YES
|
|||||||
wait/synch/rwlock/sql/LOGGER::LOCK_logger YES YES
|
wait/synch/rwlock/sql/LOGGER::LOCK_logger YES YES
|
||||||
wait/synch/rwlock/sql/MDL_context::LOCK_waiting_for YES YES
|
wait/synch/rwlock/sql/MDL_context::LOCK_waiting_for YES YES
|
||||||
wait/synch/rwlock/sql/MDL_lock::rwlock YES YES
|
wait/synch/rwlock/sql/MDL_lock::rwlock YES YES
|
||||||
|
wait/synch/rwlock/sql/Query_cache_query::lock YES YES
|
||||||
select * from performance_schema.setup_instruments
|
select * from performance_schema.setup_instruments
|
||||||
where name like 'Wait/Synch/Cond/sql/%'
|
where name like 'Wait/Synch/Cond/sql/%'
|
||||||
and name not in (
|
and name not in (
|
||||||
|
@@ -609,6 +609,7 @@ struct TABLE_SHARE
|
|||||||
Protects ref_count and m_flush_tickets.
|
Protects ref_count and m_flush_tickets.
|
||||||
*/
|
*/
|
||||||
mysql_mutex_t LOCK_table_share;
|
mysql_mutex_t LOCK_table_share;
|
||||||
|
mysql_cond_t COND_release;
|
||||||
TABLE_SHARE *next, **prev; /* Link to unused shares */
|
TABLE_SHARE *next, **prev; /* Link to unused shares */
|
||||||
uint ref_count; /* How many TABLE objects uses this */
|
uint ref_count; /* How many TABLE objects uses this */
|
||||||
/**
|
/**
|
||||||
|
@@ -87,7 +87,6 @@ mysql_mutex_t LOCK_open;
|
|||||||
|
|
||||||
static mysql_mutex_t LOCK_unused_shares;
|
static mysql_mutex_t LOCK_unused_shares;
|
||||||
static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */
|
static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */
|
||||||
static mysql_rwlock_t LOCK_flush; /**< Sync tc_purge() and tdc_remove_table(). */
|
|
||||||
my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */
|
my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */
|
||||||
|
|
||||||
#ifdef HAVE_PSI_INTERFACE
|
#ifdef HAVE_PSI_INTERFACE
|
||||||
@@ -100,11 +99,17 @@ static PSI_mutex_info all_tc_mutexes[]=
|
|||||||
{ &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
|
{ &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
static PSI_rwlock_key key_rwlock_LOCK_tdc, key_rwlock_LOCK_flush;
|
static PSI_rwlock_key key_rwlock_LOCK_tdc;
|
||||||
static PSI_rwlock_info all_tc_rwlocks[]=
|
static PSI_rwlock_info all_tc_rwlocks[]=
|
||||||
{
|
{
|
||||||
{ &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL },
|
{ &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL }
|
||||||
{ &key_rwlock_LOCK_flush, "LOCK_flush", PSI_FLAG_GLOBAL }
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static PSI_cond_key key_TABLE_SHARE_COND_release;
|
||||||
|
static PSI_cond_info all_tc_conds[]=
|
||||||
|
{
|
||||||
|
{ &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -118,6 +123,9 @@ static void init_tc_psi_keys(void)
|
|||||||
|
|
||||||
count= array_elements(all_tc_rwlocks);
|
count= array_elements(all_tc_rwlocks);
|
||||||
mysql_rwlock_register(category, all_tc_rwlocks, count);
|
mysql_rwlock_register(category, all_tc_rwlocks, count);
|
||||||
|
|
||||||
|
count= array_elements(all_tc_conds);
|
||||||
|
mysql_cond_register(category, all_tc_conds, count);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -197,12 +205,10 @@ void tc_purge(bool mark_flushed)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tdc_it.deinit();
|
tdc_it.deinit();
|
||||||
mysql_rwlock_rdlock(&LOCK_flush);
|
|
||||||
mysql_mutex_unlock(&LOCK_open);
|
mysql_mutex_unlock(&LOCK_open);
|
||||||
|
|
||||||
while ((table= purge_tables.pop_front()))
|
while ((table= purge_tables.pop_front()))
|
||||||
intern_close_table(table);
|
intern_close_table(table);
|
||||||
mysql_rwlock_unlock(&LOCK_flush);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -257,10 +263,8 @@ void tc_add_table(THD *thd, TABLE *table)
|
|||||||
{
|
{
|
||||||
purge_table->s->tdc.free_tables.remove(purge_table);
|
purge_table->s->tdc.free_tables.remove(purge_table);
|
||||||
tc_remove_table(purge_table);
|
tc_remove_table(purge_table);
|
||||||
mysql_rwlock_rdlock(&LOCK_flush);
|
|
||||||
mysql_mutex_unlock(&LOCK_open);
|
mysql_mutex_unlock(&LOCK_open);
|
||||||
intern_close_table(purge_table);
|
intern_close_table(purge_table);
|
||||||
mysql_rwlock_unlock(&LOCK_flush);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mysql_mutex_unlock(&LOCK_open);
|
mysql_mutex_unlock(&LOCK_open);
|
||||||
@@ -361,11 +365,9 @@ bool tc_release_table(TABLE *table)
|
|||||||
|
|
||||||
purge:
|
purge:
|
||||||
tc_remove_table(table);
|
tc_remove_table(table);
|
||||||
mysql_rwlock_rdlock(&LOCK_flush);
|
|
||||||
mysql_mutex_unlock(&LOCK_open);
|
mysql_mutex_unlock(&LOCK_open);
|
||||||
table->in_use= 0;
|
table->in_use= 0;
|
||||||
intern_close_table(table);
|
intern_close_table(table);
|
||||||
mysql_rwlock_unlock(&LOCK_flush);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +396,7 @@ static int tdc_delete_share_from_hash(TABLE_SHARE *share)
|
|||||||
mysql_mutex_lock(&share->tdc.LOCK_table_share);
|
mysql_mutex_lock(&share->tdc.LOCK_table_share);
|
||||||
if (--share->tdc.ref_count)
|
if (--share->tdc.ref_count)
|
||||||
{
|
{
|
||||||
|
mysql_cond_broadcast(&share->tdc.COND_release);
|
||||||
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
|
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
|
||||||
mysql_rwlock_unlock(&LOCK_tdc);
|
mysql_rwlock_unlock(&LOCK_tdc);
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
@@ -453,7 +456,6 @@ int tdc_init(void)
|
|||||||
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
|
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
|
||||||
MY_MUTEX_INIT_FAST);
|
MY_MUTEX_INIT_FAST);
|
||||||
mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc);
|
mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc);
|
||||||
mysql_rwlock_init(key_rwlock_LOCK_flush, &LOCK_flush);
|
|
||||||
my_atomic_rwlock_init(&LOCK_tdc_atomics);
|
my_atomic_rwlock_init(&LOCK_tdc_atomics);
|
||||||
oldest_unused_share= &end_of_unused_share;
|
oldest_unused_share= &end_of_unused_share;
|
||||||
end_of_unused_share.tdc.prev= &oldest_unused_share;
|
end_of_unused_share.tdc.prev= &oldest_unused_share;
|
||||||
@@ -501,7 +503,6 @@ void tdc_deinit(void)
|
|||||||
tdc_inited= false;
|
tdc_inited= false;
|
||||||
my_hash_free(&tdc_hash);
|
my_hash_free(&tdc_hash);
|
||||||
my_atomic_rwlock_destroy(&LOCK_tdc_atomics);
|
my_atomic_rwlock_destroy(&LOCK_tdc_atomics);
|
||||||
mysql_rwlock_destroy(&LOCK_flush);
|
|
||||||
mysql_rwlock_destroy(&LOCK_tdc);
|
mysql_rwlock_destroy(&LOCK_tdc);
|
||||||
mysql_mutex_destroy(&LOCK_unused_shares);
|
mysql_mutex_destroy(&LOCK_unused_shares);
|
||||||
mysql_mutex_destroy(&LOCK_open);
|
mysql_mutex_destroy(&LOCK_open);
|
||||||
@@ -567,6 +568,7 @@ void tdc_init_share(TABLE_SHARE *share)
|
|||||||
DBUG_ENTER("tdc_init_share");
|
DBUG_ENTER("tdc_init_share");
|
||||||
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
|
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
|
||||||
&share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST);
|
&share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST);
|
||||||
|
mysql_cond_init(key_TABLE_SHARE_COND_release, &share->tdc.COND_release, 0);
|
||||||
share->tdc.m_flush_tickets.empty();
|
share->tdc.m_flush_tickets.empty();
|
||||||
share->tdc.all_tables.empty();
|
share->tdc.all_tables.empty();
|
||||||
share->tdc.free_tables.empty();
|
share->tdc.free_tables.empty();
|
||||||
@@ -588,6 +590,7 @@ void tdc_deinit_share(TABLE_SHARE *share)
|
|||||||
DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
|
DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
|
||||||
DBUG_ASSERT(share->tdc.all_tables.is_empty());
|
DBUG_ASSERT(share->tdc.all_tables.is_empty());
|
||||||
DBUG_ASSERT(share->tdc.free_tables.is_empty());
|
DBUG_ASSERT(share->tdc.free_tables.is_empty());
|
||||||
|
mysql_cond_destroy(&share->tdc.COND_release);
|
||||||
mysql_mutex_destroy(&share->tdc.LOCK_table_share);
|
mysql_mutex_destroy(&share->tdc.LOCK_table_share);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@@ -825,6 +828,7 @@ void tdc_release_share(TABLE_SHARE *share)
|
|||||||
if (share->tdc.ref_count > 1)
|
if (share->tdc.ref_count > 1)
|
||||||
{
|
{
|
||||||
share->tdc.ref_count--;
|
share->tdc.ref_count--;
|
||||||
|
mysql_cond_broadcast(&share->tdc.COND_release);
|
||||||
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
|
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@@ -950,6 +954,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
|
|||||||
if ((share= tdc_delete_share(db, table_name)))
|
if ((share= tdc_delete_share(db, table_name)))
|
||||||
{
|
{
|
||||||
I_P_List <TABLE, TABLE_share> purge_tables;
|
I_P_List <TABLE, TABLE_share> purge_tables;
|
||||||
|
uint my_refs= 1;
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_open);
|
mysql_mutex_lock(&LOCK_open);
|
||||||
/*
|
/*
|
||||||
@@ -971,29 +976,53 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
|
|||||||
if (kill_delayed_threads)
|
if (kill_delayed_threads)
|
||||||
kill_delayed_threads_for_table(share);
|
kill_delayed_threads_for_table(share);
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
|
||||||
if (remove_type == TDC_RT_REMOVE_NOT_OWN)
|
remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
|
||||||
{
|
{
|
||||||
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
|
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
|
||||||
while ((table= it++))
|
while ((table= it++))
|
||||||
|
{
|
||||||
|
my_refs++;
|
||||||
DBUG_ASSERT(table->in_use == thd);
|
DBUG_ASSERT(table->in_use == thd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
|
||||||
|
|
||||||
mysql_rwlock_rdlock(&LOCK_flush);
|
|
||||||
mysql_mutex_unlock(&LOCK_open);
|
mysql_mutex_unlock(&LOCK_open);
|
||||||
|
|
||||||
while ((table= purge_tables.pop_front()))
|
while ((table= purge_tables.pop_front()))
|
||||||
intern_close_table(table);
|
intern_close_table(table);
|
||||||
mysql_rwlock_unlock(&LOCK_flush);
|
|
||||||
|
|
||||||
DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
|
if (remove_type != TDC_RT_REMOVE_UNUSED)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Even though current thread holds exclusive metadata lock on this share
|
||||||
|
(asserted above), concurrent FLUSH TABLES threads may be in process of
|
||||||
|
closing unused table instances belonging to this share. E.g.:
|
||||||
|
thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
|
||||||
|
thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
|
||||||
|
thr2 (ALTER TABLE): tdc_remove_table();
|
||||||
|
thr1 (FLUSH TABLES): intern_close_table(table);
|
||||||
|
|
||||||
|
Current remove type assumes that all table instances (except for those
|
||||||
|
that are owned by current thread) must be closed before
|
||||||
|
thd_remove_table() returns. Wait for such tables now.
|
||||||
|
|
||||||
|
intern_close_table() decrements ref_count and signals COND_release. When
|
||||||
|
ref_count drops down to number of references owned by current thread
|
||||||
|
waiting is completed.
|
||||||
|
|
||||||
|
Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
|
||||||
|
because it waits for all table instances, whereas we have to wait only
|
||||||
|
for those that are not owned by current thread.
|
||||||
|
*/
|
||||||
|
mysql_mutex_lock(&share->tdc.LOCK_table_share);
|
||||||
|
while (share->tdc.ref_count > my_refs)
|
||||||
|
mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
|
||||||
|
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
|
||||||
|
}
|
||||||
|
|
||||||
tdc_release_share(share);
|
tdc_release_share(share);
|
||||||
|
|
||||||
/* Wait for concurrent threads to free unused objects. */
|
|
||||||
mysql_rwlock_wrlock(&LOCK_flush);
|
|
||||||
mysql_rwlock_unlock(&LOCK_flush);
|
|
||||||
|
|
||||||
found= true;
|
found= true;
|
||||||
}
|
}
|
||||||
DBUG_ASSERT(found || remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
|
DBUG_ASSERT(found || remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
|
||||||
|
Reference in New Issue
Block a user