diff options
author | Sergey Vojtovich <svoj@mariadb.org> | 2013-12-10 19:00:36 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@mariadb.org> | 2013-12-10 19:00:36 +0400 |
commit | 98c4f167c505627644369acfc4e278fab50d29d3 (patch) | |
tree | ddb6f3c7198382d26bee8d2ecac95f4d97e1b93e /sql | |
parent | b2c56742b5543f77a12b86731c51151d0d3e421d (diff) | |
download | mariadb-git-98c4f167c505627644369acfc4e278fab50d29d3.tar.gz |
MDEV-4956 - Reduce usage of LOCK_open: TABLE_SHARE::tdc.used_tables
- tc_acquire_table and tc_release_table do not access
TABLE_SHARE::tdc.used_tables anymore
- in tc_acquire_table(): release LOCK_tdc after we relase LOCK_open
(saves a few CPU cycles in critical section)
- in tc_release_table(): if we reached table cache threshold, evict
to-be-released table without moving it to unused_tables. unused_tables
must be empty at this point.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sql_base.cc | 15 | ||||
-rw-r--r-- | sql/sql_test.cc | 28 | ||||
-rw-r--r-- | sql/sys_vars.cc | 13 | ||||
-rw-r--r-- | sql/table.cc | 6 | ||||
-rw-r--r-- | sql/table.h | 19 | ||||
-rw-r--r-- | sql/table_cache.cc | 100 |
6 files changed, 91 insertions, 90 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 62def81b6ed..d5ef6fb4633 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -309,9 +309,11 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) share->table_name.str); (*start_list)->in_use= 0; mysql_mutex_lock(&LOCK_open); - TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables); - while (it++) - ++(*start_list)->in_use; + TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables); + TABLE *table; + while ((table= it++)) + if (table->in_use) + ++(*start_list)->in_use; mysql_mutex_unlock(&LOCK_open); (*start_list)->locked= 0; /* Obsolete. */ start_list= &(*start_list)->next; @@ -371,16 +373,19 @@ void free_io_cache(TABLE *table) void kill_delayed_threads_for_table(TABLE_SHARE *share) { - TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables); + TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables); TABLE *tab; mysql_mutex_assert_owner(&LOCK_open); + if (!delayed_insert_threads) + return; + while ((tab= it++)) { THD *in_use= tab->in_use; - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + if (in_use && (in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { in_use->killed= KILL_SYSTEM_THREAD; diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 1a241521fd7..151ceed4006 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -92,21 +92,23 @@ static void print_cached_tables(void) mysql_mutex_lock(&LOCK_open); while ((share= tdc_it.next())) { - TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables); + TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables); while ((entry= it++)) { - printf("%-14.14s %-32s%6ld%8ld%6d %s\n", - entry->s->db.str, entry->s->table_name.str, entry->s->version, - entry->in_use->thread_id, entry->db_stat ? 1 : 0, - lock_descriptions[(int)entry->reginfo.lock_type]); - } - it.init(share->tdc.free_tables); - while ((entry= it++)) - { - unused++; - printf("%-14.14s %-32s%6ld%8ld%6d %s\n", - entry->s->db.str, entry->s->table_name.str, entry->s->version, - 0L, entry->db_stat ? 1 : 0, "Not in use"); + if (entry->in_use) + { + printf("%-14.14s %-32s%6ld%8ld%6d %s\n", + entry->s->db.str, entry->s->table_name.str, entry->s->version, + entry->in_use->thread_id, entry->db_stat ? 1 : 0, + lock_descriptions[(int)entry->reginfo.lock_type]); + } + else + { + unused++; + printf("%-14.14s %-32s%6ld%8ld%6d %s\n", + entry->s->db.str, entry->s->table_name.str, entry->s->version, + 0L, entry->db_stat ? 1 : 0, "Not in use"); + } } } tdc_it.deinit(); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 803d61e1252..97ddedf2353 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2859,11 +2859,22 @@ static Sys_var_ulong Sys_table_def_size( VALID_RANGE(TABLE_DEF_CACHE_MIN, 512*1024), DEFAULT(TABLE_DEF_CACHE_DEFAULT), BLOCK_SIZE(1)); + +static bool fix_table_open_cache(sys_var *, THD *, enum_var_type) +{ + mysql_mutex_unlock(&LOCK_global_system_variables); + tc_purge(); + mysql_mutex_lock(&LOCK_global_system_variables); + return false; +} + + static Sys_var_ulong Sys_table_cache_size( "table_open_cache", "The number of cached open tables", GLOBAL_VAR(tc_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, 512*1024), DEFAULT(TABLE_OPEN_CACHE_DEFAULT), - BLOCK_SIZE(1)); + BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_table_open_cache)); static Sys_var_ulong Sys_thread_cache_size( "thread_cache_size", diff --git a/sql/table.cc b/sql/table.cc index 49396052cdd..84f929fa7a7 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3817,7 +3817,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, if (gvisitor->m_lock_open_count++ == 0) mysql_mutex_lock(&LOCK_open); - TABLE_list::Iterator tables_it(tdc.used_tables); + All_share_tables_list::Iterator tables_it(tdc.all_tables); /* In case of multiple searches running in parallel, avoid going @@ -3835,7 +3835,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, while ((table= tables_it++)) { - if (gvisitor->inspect_edge(&table->in_use->mdl_context)) + if (table->in_use && gvisitor->inspect_edge(&table->in_use->mdl_context)) { goto end_leave_node; } @@ -3844,7 +3844,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, tables_it.rewind(); while ((table= tables_it++)) { - if (table->in_use->mdl_context.visit_subgraph(gvisitor)) + if (table->in_use && table->in_use->mdl_context.visit_subgraph(gvisitor)) { goto end_leave_node; } diff --git a/sql/table.h b/sql/table.h index 301b98a5ec2..17a8387ffa8 100644 --- a/sql/table.h +++ b/sql/table.h @@ -479,6 +479,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, struct TABLE_share; +struct All_share_tables; extern ulong tdc_refresh_version(void); @@ -603,6 +604,7 @@ struct TABLE_SHARE mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */ typedef I_P_List <TABLE, TABLE_share> TABLE_list; + typedef I_P_List <TABLE, All_share_tables> All_share_tables_list; struct { /** @@ -619,7 +621,7 @@ struct TABLE_SHARE Doubly-linked (back-linked) lists of used and unused TABLE objects for this share. */ - TABLE_list used_tables; + All_share_tables_list all_tables; TABLE_list free_tables; } tdc; @@ -1014,8 +1016,10 @@ private: One should use methods of I_P_List template instead. */ TABLE *share_next, **share_prev; + TABLE *share_all_next, **share_all_prev; friend struct TABLE_share; + friend struct All_share_tables; public: @@ -1379,6 +1383,19 @@ struct TABLE_share }; +struct All_share_tables +{ + static inline TABLE **next_ptr(TABLE *l) + { + return &l->share_all_next; + } + static inline TABLE ***prev_ptr(TABLE *l) + { + return &l->share_all_prev; + } +}; + + enum enum_schema_table_state { NOT_PROCESSED= 0, diff --git a/sql/table_cache.cc b/sql/table_cache.cc index b3c08400d56..0ff7ddd7f75 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -43,11 +43,8 @@ - free_table_share() Table cache invariants: - - TABLE_SHARE::used_tables shall not contain objects with TABLE::in_use == 0 - TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0 - unused_tables shall not contain objects with TABLE::in_use != 0 - - cached TABLE object must be either in TABLE_SHARE::used_tables or in - TABLE_SHARE::free_tables */ #include "my_global.h" @@ -65,7 +62,7 @@ static HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */ static TABLE_SHARE *oldest_unused_share, end_of_unused_share; TABLE *unused_tables; /**< Collection of unused TABLE objects. */ -static int64 tdc_version; /* Increments on each reload */ +static int64 tdc_version; /* Increments on each reload */ static int64 last_table_id; static bool tdc_inited; @@ -81,7 +78,7 @@ static uint tc_count; /**< Number of TABLE objects in table cache. */ TABLE::next TABLE::prev TABLE_SHARE::tdc.free_tables - TABLE_SHARE::tdc.used_tables + TABLE_SHARE::tdc.all_tables */ mysql_mutex_t LOCK_open; @@ -159,7 +156,8 @@ uint tc_records(void) Free all unused TABLE objects. While locked: - - remove unused objects from TABLE_SHARE::tdc.free_tables lists + - remove unused objects from TABLE_SHARE::tdc.free_tables and + TABLE_SHARE::tdc.all_tables - reset unused_tables - decrement tc_count @@ -180,6 +178,7 @@ void tc_purge(void) do { unused_tables->s->tdc.free_tables.remove(unused_tables); + unused_tables->s->tdc.all_tables.remove(unused_tables); tc_count--; } while ((unused_tables= unused_tables->next)); mysql_rwlock_rdlock(&LOCK_flush); @@ -249,14 +248,11 @@ static void check_unused(THD *thd) count--; open_files++; } - it.init(share->tdc.used_tables); - while ((entry= it++)) + TABLE_SHARE::All_share_tables_list::Iterator it2(share->tdc.all_tables); + while ((entry= it2++)) { - if (!entry->in_use) - { - DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */ - } - open_files++; + if (entry->in_use) + open_files++; } } mysql_mutex_unlock(&LOCK_open); @@ -278,7 +274,8 @@ static void check_unused(THD *thd) @pre LOCK_open is locked, table is not used. While locked: - - remove object from TABLE_SHARE::tdc.free_tables + - remove object from TABLE_SHARE::tdc.free_tables and + TABLE_SHARE::tdc.all_tables - remove object from unused_tables @note This is helper routine, supposed to be used by table cache @@ -291,6 +288,7 @@ static void tc_remove_table(TABLE *table) DBUG_ASSERT(!table->in_use); /* Remove from per-share chain of unused TABLE objects. */ table->s->tdc.free_tables.remove(table); + table->s->tdc.all_tables.remove(table); /* And global unused chain. */ table->next->prev= table->prev; @@ -313,11 +311,11 @@ static void tc_remove_table(TABLE *table) Added object cannot be evicted or acquired. While locked: - - add object to TABLE_SHARE::tdc.used_tables + - add object to TABLE_SHARE::tdc.all_tables - increment tc_count - evict LRU object from table cache if we reached threshold - While unlocked: + While unlocked: - free evicted object */ @@ -325,7 +323,7 @@ void tc_add_table(THD *thd, TABLE *table) { DBUG_ASSERT(table->in_use == thd); mysql_mutex_lock(&LOCK_open); - table->s->tdc.used_tables.push_front(table); + table->s->tdc.all_tables.push_front(table); tc_count++; /* If we have too many TABLE instances around, try to get rid of them */ if (tc_count > tc_size && unused_tables) @@ -352,13 +350,9 @@ void tc_add_table(THD *thd, TABLE *table) While locked: - pop object from TABLE_SHARE::tdc.free_tables() - - remove share protection - remove object from unused_tables - - add object to TABLE_SHARE::tdc.used_tables() - mark object used by thd - @note share protection is kept if there are no unused objects. - @return TABLE object, or NULL if no unused objects. */ @@ -372,7 +366,6 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) mysql_mutex_unlock(&LOCK_open); return 0; } - mysql_rwlock_unlock(&LOCK_tdc); DBUG_ASSERT(!table->in_use); /* Unlink table from global unused tables list. */ @@ -385,15 +378,12 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) table->prev->next=table->next; /* Remove from unused list */ table->next->prev=table->prev; table->in_use= thd; - /* Add table to list of used tables for this share. */ - table->s->tdc.used_tables.push_front(table); mysql_mutex_unlock(&LOCK_open); /* The ex-unused table must be fully functional. */ DBUG_ASSERT(table->db_stat && table->file); /* The children must be detached from the table. */ DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); - check_unused(thd); return table; } @@ -407,13 +397,12 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) While locked: - mark object not in use by any thread - - remove object from TABLE_SHARE::tdc.used_tables - if object is marked for purge, decrement tc_count - add object to TABLE_SHARE::tdc.free_tables - add object to unused_tables - evict LRU object from table cache if we reached threshold - While unlocked: + While unlocked: - free evicted/purged object @note Another thread may mark share for purge any moment (even @@ -433,12 +422,12 @@ bool tc_release_table(TABLE *table) DBUG_ASSERT(table->file); mysql_mutex_lock(&LOCK_open); - /* Remove table from the list of tables used in this share. */ - table->s->tdc.used_tables.remove(table); table->in_use= 0; - if (table->s->has_old_version() || table->needs_reopen() || !tdc_size) + if (table->s->has_old_version() || table->needs_reopen() || !tdc_size || + tc_count > tc_size) { tc_count--; + table->s->tdc.all_tables.remove(table); mysql_rwlock_rdlock(&LOCK_flush); mysql_mutex_unlock(&LOCK_open); intern_close_table(table); @@ -457,21 +446,7 @@ bool tc_release_table(TABLE *table) } else unused_tables=table->next=table->prev=table; - /* - We free the least used table, not the subject table, - to keep the LRU order. - */ - if (tc_count > tc_size) - { - TABLE *purge_table= unused_tables; - tc_remove_table(purge_table); - mysql_rwlock_rdlock(&LOCK_flush); - mysql_mutex_unlock(&LOCK_open); - intern_close_table(purge_table); - mysql_rwlock_unlock(&LOCK_flush); - } - else - mysql_mutex_unlock(&LOCK_open); + mysql_mutex_unlock(&LOCK_open); check_unused(thd); return false; } @@ -636,19 +611,9 @@ ulong tdc_records(void) void tdc_purge(bool all) { DBUG_ENTER("tdc_purge"); - for (;;) + while (all || tdc_records() > tdc_size) { TABLE_SHARE *share; - if (!all) - { - mysql_rwlock_rdlock(&LOCK_tdc); - if (tdc_hash.records <= tdc_size) - { - mysql_rwlock_unlock(&LOCK_tdc); - break; - } - mysql_rwlock_unlock(&LOCK_tdc); - } mysql_mutex_lock(&LOCK_unused_shares); if (!oldest_unused_share->tdc.next) @@ -684,7 +649,7 @@ void tdc_init_share(TABLE_SHARE *share) mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share, &share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST); share->tdc.m_flush_tickets.empty(); - share->tdc.used_tables.empty(); + share->tdc.all_tables.empty(); share->tdc.free_tables.empty(); tdc_assign_new_table_id(share); share->version= tdc_refresh_version(); @@ -701,7 +666,7 @@ void tdc_deinit_share(TABLE_SHARE *share) DBUG_ENTER("tdc_deinit_share"); DBUG_ASSERT(share->tdc.ref_count == 0); DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty()); - DBUG_ASSERT(share->tdc.used_tables.is_empty()); + DBUG_ASSERT(share->tdc.all_tables.is_empty()); DBUG_ASSERT(share->tdc.free_tables.is_empty()); mysql_mutex_destroy(&share->tdc.LOCK_table_share); DBUG_VOID_RETURN; @@ -721,7 +686,7 @@ void tdc_deinit_share(TABLE_SHARE *share) Caller is expected to unlock table share with tdc_unlock_share(). @retval 0 Share not found - @retval !0 Pointer to locked table share + @retval !0 Pointer to locked table share */ TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name) @@ -846,6 +811,8 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, { if ((*out_table= tc_acquire_table(thd, share))) { + mysql_rwlock_unlock(&LOCK_tdc); + check_unused(thd); DBUG_ASSERT(!(flags & GTS_NOLOCK)); DBUG_ASSERT(!share->error); DBUG_ASSERT(!share->is_view); @@ -894,7 +861,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, share->tdc.next->tdc.prev= share->tdc.prev; share->tdc.next= 0; share->tdc.prev= 0; - } + } mysql_mutex_unlock(&LOCK_unused_shares); } @@ -928,7 +895,7 @@ err: void tdc_release_share(TABLE_SHARE *share) { DBUG_ENTER("tdc_release_share"); - + mysql_mutex_lock(&share->tdc.LOCK_table_share); DBUG_PRINT("enter", ("share: 0x%lx table: %s.%s ref_count: %u version: %lu", @@ -998,7 +965,7 @@ static TABLE_SHARE *tdc_delete_share(const char *db, const char *table_name) /* Concurrent thread may start using share again, reset prev and next. */ share->tdc.prev= 0; share->tdc.next= 0; - } + } mysql_mutex_unlock(&LOCK_unused_shares); if (!tdc_delete_share_from_hash(share)) @@ -1072,13 +1039,11 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables); #ifndef DBUG_OFF - if (remove_type == TDC_RT_REMOVE_ALL) - DBUG_ASSERT(share->tdc.used_tables.is_empty()); - else if (remove_type == TDC_RT_REMOVE_NOT_OWN) + if (remove_type == TDC_RT_REMOVE_NOT_OWN) { - TABLE_SHARE::TABLE_list::Iterator it2(share->tdc.used_tables); + TABLE_SHARE::All_share_tables_list::Iterator it2(share->tdc.all_tables); while ((table= it2++)) - DBUG_ASSERT(table->in_use == thd); + DBUG_ASSERT(!table->in_use || table->in_use == thd); } #endif /* @@ -1105,6 +1070,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, mysql_rwlock_unlock(&LOCK_flush); check_unused(thd); + DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL); tdc_release_share(share); /* Wait for concurrent threads to free unused objects. */ |