diff options
author | Sergey Vojtovich <svoj@mariadb.org> | 2015-04-03 11:44:46 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@mariadb.org> | 2015-04-03 11:44:46 +0400 |
commit | 9f924c47e88a8f0d44410c252c27e3a0d9e9191c (patch) | |
tree | dad270306d9d62495c0b94dfaf65bc9deb1ddfe4 | |
parent | d4076bad363e314d246ca41550c62d0e98b5458c (diff) | |
download | mariadb-git-10.0-power.tar.gz |
Multi-rwlock lock for table definition cache hash.10.0-power
-rw-r--r-- | sql/table_cache.cc | 113 |
1 files changed, 92 insertions, 21 deletions
diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 9e2246a5846..e0e5f38547f 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -53,6 +53,77 @@ #include "table.h" #include "sql_base.h" + +/** + Table definition hash lock. + + Lock consists of multiple rw-locks (8 by default). Lock is write locked + when all instances are write locked. Lock is read locked when any instance + is read locked. + + This class was implemented to workaround scalability bottleneck of table + definition hash rw-lock. + + To be replaced with lock-free hash when we understand how to solve the + following problems: + - lock-free hash iterator (can be workarounded by introducing global + all_tables list); + - lf_hash_search() may fail because of OOM. It is rather complex to handle + it properly. +*/ + +class TDC_lock +{ + uint m_instances; + bool m_write_locked; + char pad[128]; + struct st_locks + { + mysql_rwlock_t lock; + char pad[128]; + } *locks; +public: + int init(PSI_rwlock_key key, uint instances) + { + m_instances= instances; + m_write_locked= false; + locks= (st_locks *) my_malloc(sizeof(struct st_locks) * m_instances, MYF(MY_WME)); + if (!locks) + return 1; + for (uint i= 0; i < m_instances; i++) + mysql_rwlock_init(key, &locks[i].lock); + return 0; + } + void destroy(void) + { + for (uint i= 0; i < m_instances; i++) + mysql_rwlock_destroy(&locks[i].lock); + my_free(locks); + } + void wrlock(void) + { + for (uint i= 0; i < m_instances; i++) + mysql_rwlock_wrlock(&locks[i].lock); + m_write_locked= true; + } + void rdlock(THD *thd= 0) + { + mysql_rwlock_rdlock(&locks[thd ? thd->thread_id % m_instances : 0].lock); + } + void unlock(THD *thd= 0) + { + if (m_write_locked) + { + m_write_locked= false; + for (uint i= 0; i < m_instances; i++) + mysql_rwlock_unlock(&locks[i].lock); + } + else + mysql_rwlock_unlock(&locks[thd ? thd->thread_id % m_instances : 0].lock); + } +}; + + /** Configuration. */ ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */ ulong tc_size; /**< Table cache threshold for LRU eviction. */ @@ -79,7 +150,7 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */ */ static mysql_mutex_t LOCK_unused_shares; -static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */ +static TDC_lock LOCK_tdc; /**< Protects tdc_hash. */ my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */ #ifdef HAVE_PSI_INTERFACE @@ -93,7 +164,7 @@ static PSI_mutex_info all_tc_mutexes[]= static PSI_rwlock_key key_rwlock_LOCK_tdc; static PSI_rwlock_info all_tc_rwlocks[]= { - { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL } + { &key_rwlock_LOCK_tdc, "LOCK_tdc", 0 } }; @@ -431,20 +502,20 @@ extern "C" uchar *tdc_key(const uchar *record, size_t *length, static int tdc_delete_share_from_hash(TABLE_SHARE *share) { DBUG_ENTER("tdc_delete_share_from_hash"); - mysql_rwlock_wrlock(&LOCK_tdc); + LOCK_tdc.wrlock(); mysql_mutex_lock(&share->tdc.LOCK_table_share); if (--share->tdc.ref_count) { mysql_cond_broadcast(&share->tdc.COND_release); mysql_mutex_unlock(&share->tdc.LOCK_table_share); - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); DBUG_RETURN(1); } my_hash_delete(&tdc_hash, (uchar*) share); /* Notify PFS early, while still locked. */ PSI_CALL_release_table_share(share->m_psi); share->m_psi= 0; - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); if (share->tdc.m_flush_tickets.is_empty()) { @@ -487,12 +558,12 @@ int tdc_init(void) tdc_inited= true; mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares, MY_MUTEX_INIT_FAST); - mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc); my_atomic_rwlock_init(&LOCK_tdc_atomics); oldest_unused_share= &end_of_unused_share; end_of_unused_share.tdc.prev= &oldest_unused_share; tdc_version= 1L; /* Increments on each reload */ - DBUG_RETURN(my_hash_init(&tdc_hash, &my_charset_bin, tdc_size, 0, 0, tdc_key, + DBUG_RETURN(LOCK_tdc.init(key_rwlock_LOCK_tdc, 8) || + my_hash_init(&tdc_hash, &my_charset_bin, tdc_size, 0, 0, tdc_key, 0, 0)); } @@ -535,7 +606,7 @@ void tdc_deinit(void) tdc_inited= false; my_hash_free(&tdc_hash); my_atomic_rwlock_destroy(&LOCK_tdc_atomics); - mysql_rwlock_destroy(&LOCK_tdc); + LOCK_tdc.destroy(); mysql_mutex_destroy(&LOCK_unused_shares); } DBUG_VOID_RETURN; @@ -552,9 +623,9 @@ ulong tdc_records(void) { ulong records; DBUG_ENTER("tdc_records"); - mysql_rwlock_rdlock(&LOCK_tdc); + LOCK_tdc.rdlock(); records= tdc_hash.records; - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); DBUG_RETURN(records); } @@ -653,14 +724,14 @@ TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name) DBUG_ENTER("tdc_lock_share"); key_length= tdc_create_key(key, db, table_name); - mysql_rwlock_rdlock(&LOCK_tdc); + LOCK_tdc.rdlock(); TABLE_SHARE* share= (TABLE_SHARE*) my_hash_search(&tdc_hash, (uchar*) key, key_length); if (share && !share->error) mysql_mutex_lock(&share->tdc.LOCK_table_share); else share= 0; - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); DBUG_RETURN(share); } @@ -705,20 +776,20 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, bool was_unused; DBUG_ENTER("tdc_acquire_share"); - mysql_rwlock_rdlock(&LOCK_tdc); + LOCK_tdc.rdlock(thd); share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value, (uchar*) key, key_length); if (!share) { TABLE_SHARE *new_share; - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(thd); if (!(new_share= alloc_table_share(db, table_name, key, key_length))) DBUG_RETURN(0); new_share->error= OPEN_FRM_OPEN_ERROR; - mysql_rwlock_wrlock(&LOCK_tdc); + LOCK_tdc.wrlock(); share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value, (uchar*) key, key_length); @@ -731,12 +802,12 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, if (my_hash_insert(&tdc_hash, (uchar*) share)) { mysql_mutex_unlock(&share->tdc.LOCK_table_share); - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); free_table_share(share); DBUG_RETURN(0); } need_purge= tdc_hash.records > tdc_size; - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); /* note that tdc_acquire_share() *always* uses discovery */ open_table_def(thd, share, flags | GTS_USE_DISCOVERY); @@ -765,7 +836,7 @@ 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); + LOCK_tdc.unlock(thd); DBUG_ASSERT(!(flags & GTS_NOLOCK)); DBUG_ASSERT(!share->error); DBUG_ASSERT(!share->is_view); @@ -774,7 +845,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, } mysql_mutex_lock(&share->tdc.LOCK_table_share); - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(thd); /* We found an existing table definition. Return it if we didn't get @@ -1126,7 +1197,7 @@ void TDC_iterator::init(void) { DBUG_ENTER("TDC_iterator::init"); idx= 0; - mysql_rwlock_rdlock(&LOCK_tdc); + LOCK_tdc.rdlock(); DBUG_VOID_RETURN; } @@ -1138,7 +1209,7 @@ void TDC_iterator::init(void) void TDC_iterator::deinit(void) { DBUG_ENTER("TDC_iterator::deinit"); - mysql_rwlock_unlock(&LOCK_tdc); + LOCK_tdc.unlock(); DBUG_VOID_RETURN; } |