commit 3c34f1879b8f2d45727d2824c36fe27f6285da3b Author: Laurynas Biveinis Date: Fri May 15 15:41:03 2015 +0300 Fix bug https://bugs.mysql.com/bug.php?id=62018 (innodb adaptive hash index mutex contention) by partitioning btr_search_latch into a latch array indexed by index id. Add system variable innodb_adaptive_hash_index_partitions to control the number of partitions. Patch author Alexey Kopytov. diff --git a/mysql-test/suite/innodb/r/ahi_partitions.result b/mysql-test/suite/innodb/r/ahi_partitions.result new file mode 100644 index 0000000..4c862ba --- /dev/null +++ b/mysql-test/suite/innodb/r/ahi_partitions.result @@ -0,0 +1,33 @@ +SELECT @@GLOBAL.innodb_adaptive_hash_index; +@@GLOBAL.innodb_adaptive_hash_index +1 +SELECT @@GLOBAL.innodb_adaptive_hash_index_partitions; +@@GLOBAL.innodb_adaptive_hash_index_partitions +4 +SET GLOBAL innodb_monitor_enable=module_adaptive_hash; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; +CREATE TABLE t2 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; +Filling tables +Querying +SELECT COUNT >= 6 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_pages_added'; +should_be_1 +1 +SELECT COUNT >= 6 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_rows_added'; +should_be_1 +1 +SELECT COUNT > 0 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_searches'; +should_be_1 +1 +SELECT COUNT(*) > 0 AS should_be_1 FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE +WHERE PAGE_STATE LIKE "MEMORY"; +should_be_1 +1 +SELECT COUNT(*) >= 6 AS should_be_1 FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE +WHERE IS_HASHED LIKE "YES"; +should_be_1 +1 +SET GLOBAL innodb_monitor_disable=module_adaptive_hash; +DROP TABLE t1, t2, t3; +SET GLOBAL innodb_monitor_enable=default; +SET GLOBAL innodb_monitor_disable=default; diff --git a/mysql-test/suite/innodb/t/ahi_partitions-master.opt b/mysql-test/suite/innodb/t/ahi_partitions-master.opt new file mode 100644 index 0000000..b04a0df --- /dev/null +++ b/mysql-test/suite/innodb/t/ahi_partitions-master.opt @@ -0,0 +1 @@ +--innodb-adaptive-hash-index-partitions=4 diff --git a/mysql-test/suite/innodb/t/ahi_partitions.test b/mysql-test/suite/innodb/t/ahi_partitions.test new file mode 100644 index 0000000..7a84223 --- /dev/null +++ b/mysql-test/suite/innodb/t/ahi_partitions.test @@ -0,0 +1,67 @@ +# +# Basic test for InnoDB adaptive hash index partitions. +# TODO: add another testcase that uses DEBUG_SYNC to check the partition locking +# +--source include/have_innodb.inc + +# Check setup +SELECT @@GLOBAL.innodb_adaptive_hash_index; +SELECT @@GLOBAL.innodb_adaptive_hash_index_partitions; + +SET GLOBAL innodb_monitor_enable=module_adaptive_hash; + +# 6 index trees across 4 AHI partitions +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; +CREATE TABLE t2 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT, c CHAR(200), UNIQUE INDEX b(b)) ENGINE=InnoDB; + +--echo Filling tables +--disable_query_log +let $i=3; +while ($i) +{ + eval INSERT INTO t1 VALUES ($i, $i, REPEAT("a", 200)); + eval INSERT INTO t2 VALUES ($i, $i, REPEAT("a", 200)); + eval INSERT INTO t3 VALUES ($i, $i, REPEAT("a", 200)); + dec $i; +} + +--echo Querying +--disable_result_log +let $i=200; +while ($i) +{ + SELECT b FROM t1 WHERE a=1; + SELECT a FROM t1 WHERE b=1; + SELECT b FROM t2 WHERE a=2; + SELECT a FROM t2 WHERE b=2; + SELECT b FROM t3 WHERE a=3; + SELECT a FROM t3 WHERE b=3; + dec $i; +} +--enable_result_log +--enable_query_log + +# Some buffer pool pages should be hashed +SELECT COUNT >= 6 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_pages_added'; +# Some rows should be hashed +SELECT COUNT >= 6 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_rows_added'; +# AHI should have been used for queries, but the exact lower bound is hard to determine +SELECT COUNT > 0 as should_be_1 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'adaptive_hash_searches'; + +# Buffer pool must contain AHI pages now +SELECT COUNT(*) > 0 AS should_be_1 FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE + WHERE PAGE_STATE LIKE "MEMORY"; + +# Buffer pool must contain no less than the number of index trees hashed pages now +SELECT COUNT(*) >= 6 AS should_be_1 FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE + WHERE IS_HASHED LIKE "YES"; + +SET GLOBAL innodb_monitor_disable=module_adaptive_hash; + +DROP TABLE t1, t2, t3; + +--disable_warnings +SET GLOBAL innodb_monitor_enable=default; +SET GLOBAL innodb_monitor_disable=default; +--enable_warnings diff --git a/mysql-test/suite/perfschema/r/show_sanity.result b/mysql-test/suite/perfschema/r/show_sanity.result index 5063eaf..7b907b4 100644 --- a/mysql-test/suite/perfschema/r/show_sanity.result +++ b/mysql-test/suite/perfschema/r/show_sanity.result @@ -318,6 +318,7 @@ where show_mode = "5.7" order by show_mode, source, variable_name; SHOW_MODE SOURCE VARIABLE_NAME 5.6 I_S.SESSION_VARIABLES CHECK_PROXY_USERS +5.6 I_S.SESSION_VARIABLES INNODB_ADAPTIVE_HASH_INDEX_PARTITIONS 5.6 I_S.SESSION_VARIABLES MYSQL_NATIVE_PASSWORD_PROXY_USERS 5.6 I_S.SESSION_VARIABLES SHA256_PASSWORD_PROXY_USERS diff --git a/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_partitions_basic.result b/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_partitions_basic.result new file mode 100644 index 0000000..3da3abb --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_partitions_basic.result @@ -0,0 +1,9 @@ +SELECT @@GLOBAL.innodb_adaptive_hash_index_partitions; +@@GLOBAL.innodb_adaptive_hash_index_partitions +1 +SET @@GLOBAL.innodb_adaptive_hash_index_partitions=1; +ERROR HY000: Variable 'innodb_adaptive_hash_index_partitions' is a read only variable +SELECT @@LOCAL.innodb_adaptive_hash_index_partitions; +ERROR HY000: Variable 'innodb_adaptive_hash_index_partitions' is a GLOBAL variable +SELECT @@SESSION.innodb_adaptive_hash_index_partitions; +ERROR HY000: Variable 'innodb_adaptive_hash_index_partitions' is a GLOBAL variable diff --git a/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_partitions_basic.test b/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_partitions_basic.test new file mode 100644 index 0000000..6bd3a08 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_partitions_basic.test @@ -0,0 +1,14 @@ +# A sys_vars suite test for innodb_adaptive_hash_index_partitions. + +--source include/have_innodb.inc + +SELECT @@GLOBAL.innodb_adaptive_hash_index_partitions; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@GLOBAL.innodb_adaptive_hash_index_partitions=1; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@LOCAL.innodb_adaptive_hash_index_partitions; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@SESSION.innodb_adaptive_hash_index_partitions; diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 09930af..1cc40bc 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -739,8 +739,8 @@ btr_cur_search_to_nth_level( btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ ulint has_search_latch,/*!< in: info on the latch mode the - caller currently has on btr_search_latch: - RW_S_LATCH, or 0 */ + caller currently has on the AHI latch for this + index: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ @@ -896,7 +896,8 @@ btr_cur_search_to_nth_level( # endif /* Use of AHI is disabled for intrinsic table as these tables re-use the index-id and AHI validation is based on index-id. */ - if (rw_lock_get_writer(&btr_search_latch) == RW_LOCK_NOT_LOCKED + if (rw_lock_get_writer(btr_search_get_latch(cursor->index)) == + RW_LOCK_NOT_LOCKED && latch_mode <= BTR_MODIFY_LEAF && info->last_hash_succ && !index->disable_ahi @@ -935,7 +936,7 @@ btr_cur_search_to_nth_level( if (has_search_latch) { /* Release possible search latch to obey latching order */ - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(cursor->index)); } /* Store the position of the tree latch we push to mtr so that we @@ -1872,7 +1873,8 @@ need_opposite_intention: /* We do a dirty read of btr_search_enabled here. We will properly check btr_search_enabled again in btr_search_build_page_hash_index() before building a - page hash index, while holding btr_search_latch. */ + page hash index, while holding the required AHI search + latch. */ if (btr_search_enabled && !index->disable_ahi) { btr_search_info_update(index, cursor); } @@ -1913,7 +1915,7 @@ func_exit: if (has_search_latch) { - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(cursor->index)); } if (mbr_adj) { @@ -3649,7 +3651,7 @@ btr_cur_parse_update_in_place( ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); rec = page + rec_offset; - /* We do not need to reserve btr_search_latch, as the page is only + /* We do not need to reserve the AHI search latch, as the page is only being recovered, and there cannot be a hash index to it. */ offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); @@ -3863,13 +3865,13 @@ btr_cur_update_in_place( btr_search_update_hash_on_delete(cursor); } - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(cursor->index)); } row_upd_rec_in_place(rec, index, offsets, update, page_zip); if (is_hashed) { - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(cursor->index)); } btr_cur_update_in_place_log(flags, rec, index, update, @@ -4719,7 +4721,7 @@ btr_cur_parse_del_mark_set_clust_rec( if (page) { rec = page + offset; - /* We do not need to reserve btr_search_latch, as the page + /* We do not need to the AHI search latch, as the page is only being recovered, and there cannot be a hash index to it. Besides, these fields are being updated in place and the adaptive hash index does not depend on them. */ @@ -4797,7 +4799,7 @@ btr_cur_del_mark_set_clust_rec( return(err); } - /* The btr_search_latch is not needed here, because + /* The AHI search latch is not needed here, because the adaptive hash index does not depend on the delete-mark and the delete-mark is being updated in place. */ @@ -4901,7 +4903,7 @@ btr_cur_parse_del_mark_set_sec_rec( if (page) { rec = page + offset; - /* We do not need to reserve btr_search_latch, as the page + /* We do not need to reserve the AHI search latch, as the page is only being recovered, and there cannot be a hash index to it. Besides, the delete-mark flag is being updated in place and the adaptive hash index does not depend on it. */ @@ -4951,7 +4953,7 @@ btr_cur_del_mark_set_sec_rec( cursor->index->name(), cursor->index->id, trx_get_id_for_print(thr_get_trx(thr)))); - /* We do not need to reserve btr_search_latch, as the + /* We do not need to reserve the AHI search latch, as the delete-mark flag is being updated in place and the adaptive hash index does not depend on it. */ btr_rec_set_deleted_flag(rec, buf_block_get_page_zip(block), val); @@ -4975,7 +4977,7 @@ btr_cur_set_deleted_flag_for_ibuf( ibool val, /*!< in: value to set */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - /* We do not need to reserve btr_search_latch, as the page + /* We do not need to reserve the AHI search latch, as the page has just been read to the buffer pool and there cannot be a hash index to it. Besides, the delete-mark flag is being updated in place and the adaptive hash index does not depend diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 185793c..a2fab53 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -46,9 +46,13 @@ Created 2/17/1996 Heikki Tuuri #include "sync0sync.h" /** Flag: has the search system been enabled? -Protected by btr_search_latch. */ +Writes are protected by latching all the AHI search latches, reads are +protected by latching any single one of them. */ char btr_search_enabled = TRUE; +/** Number of adaptive hash index partitions */ +ulint btr_search_index_num; + #ifdef UNIV_SEARCH_PERF_STAT /** Number of successful adaptive hash index lookups */ ulint btr_search_n_succ = 0; @@ -58,18 +62,19 @@ ulint btr_search_n_hash_fail = 0; /** padding to prevent other memory update hotspots from residing on the same memory -cache line as btr_search_latch */ +cache line as btr_search_latch_arr */ byte btr_sea_pad1[64]; -/** The latch protecting the adaptive search system: this latch protects the -(1) positions of records on those pages where a hash index has been built. -NOTE: It does not protect values of non-ordering fields within a record from +/** Array of latches protecting individual AHI partitions. The latches +protect: (1) positions of records on those pages where a hash index from the +corresponding AHI partition has been built. +NOTE: They do not protect values of non-ordering fields within a record from being updated in-place! We can use fact (1) to perform unique searches to indexes. */ /* We will allocate the latch from dynamic memory to get it to the same DRAM page as other hotspot semaphores */ -rw_lock_t* btr_search_latch_temp; +rw_lock_t* btr_search_latch_arr; /** padding to prevent other memory update hotspots from residing on the same memory cache line */ @@ -102,6 +107,22 @@ btr_search_build_page_hash_index( ulint n_fields,/*!< in: hash this many full fields */ ibool left_side);/*!< in: hash for searches from left side? */ +/********************************************************************//** +Returns the adaptive hash index table for a given index key. +@return the adaptive hash index table for a given index key */ +__attribute__((pure,warn_unused_result)) +static +hash_table_t* +btr_search_get_hash_table( +/*======================*/ + const dict_index_t* index) /*!< in: index */ +{ + ut_ad(index); + ut_ad(index->search_table); + + return(index->search_table); +} + /*****************************************************************//** This function should be called before reserving any btr search mutex, if the intended operation might add nodes to the search system hash table. @@ -114,18 +135,19 @@ allocate a new node to the hash table, it will succeed. However, the check will not guarantee success. */ static void -btr_search_check_free_space_in_heap(void) -/*=====================================*/ +btr_search_check_free_space_in_heap( +/*================================*/ + dict_index_t* index) { hash_table_t* table; mem_heap_t* heap; #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(index); heap = table->heap; @@ -136,7 +158,7 @@ btr_search_check_free_space_in_heap(void) if (heap->free_block == NULL) { buf_block_t* block = buf_block_alloc(NULL); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); if (btr_search_enabled && heap->free_block == NULL) { @@ -145,7 +167,7 @@ btr_search_check_free_space_in_heap(void) buf_block_free(block); } - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); } } @@ -156,24 +178,42 @@ btr_search_sys_create( /*==================*/ ulint hash_size) /*!< in: hash index hash table size */ { + /* PS bug lp:1018264 - Multiple hash index partitions causes overly + large hash index: When multiple adaptive hash index partitions are + specified, _each_ partition was being created with hash_size which + should be 1/64 of the total size of all buffer pools which is + incorrect and can cause overly high memory usage. hash_size + should be representing the _total_ size of all partitions, not the + individual size of each partition. */ + hash_size /= btr_search_index_num; + /* We allocate the search latch from dynamic memory: see above at the global variable definition */ - btr_search_latch_temp = reinterpret_cast( - ut_malloc_nokey(sizeof(rw_lock_t))); - - rw_lock_create( - btr_search_latch_key, &btr_search_latch, SYNC_SEARCH_SYS); + /* btr_search_index_num is constrained to machine word size for + historical reasons. This limitation can be easily removed later. */ + btr_search_latch_arr = reinterpret_cast( + ut_malloc_nokey(sizeof(rw_lock_t) * btr_search_index_num)); btr_search_sys = reinterpret_cast( ut_malloc_nokey(sizeof(btr_search_sys_t))); - btr_search_sys->hash_index = ib_create( - hash_size, "hash_table_mutex", 0, MEM_HEAP_FOR_BTR_SEARCH); + btr_search_sys->hash_tables = (hash_table_t **) + ut_malloc_nokey(sizeof(hash_table_t *) * btr_search_index_num); + + for (ulint i = 0; i < btr_search_index_num; i++) { + + rw_lock_create(btr_search_latch_key, + &btr_search_latch_arr[i], SYNC_SEARCH_SYS); + + btr_search_sys->hash_tables[i] + = ib_create(hash_size, NULL, 0, + MEM_HEAP_FOR_BTR_SEARCH); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG - btr_search_sys->hash_index->adaptive = TRUE; + btr_search_sys->hash_tables[i]->adaptive = TRUE; #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + } } /** Resize hash index hash table. @@ -182,23 +222,28 @@ void btr_search_sys_resize( ulint hash_size) { - rw_lock_x_lock(&btr_search_latch); + btr_search_x_lock_all(); if (btr_search_enabled) { - rw_lock_x_unlock(&btr_search_latch); + btr_search_x_unlock_all(); ib::error() << "btr_search_sys_resize failed because" " hash index hash table is not empty."; ut_ad(0); return; } - mem_heap_free(btr_search_sys->hash_index->heap); - hash_table_free(btr_search_sys->hash_index); + hash_size /= btr_search_index_num; - btr_search_sys->hash_index = ib_create( - hash_size, "hash_table_mutex", 0, MEM_HEAP_FOR_BTR_SEARCH); + for (ulint i = 0; i < btr_search_index_num; i++) { - rw_lock_x_unlock(&btr_search_latch); + mem_heap_free(btr_search_sys->hash_tables[i]->heap); + hash_table_free(btr_search_sys->hash_tables[i]); + + btr_search_sys->hash_tables[i] + = ib_create(hash_size, 0, 0, MEM_HEAP_FOR_BTR_SEARCH); + } + + btr_search_x_unlock_all(); } /*****************************************************************//** @@ -207,11 +252,21 @@ void btr_search_sys_free(void) /*=====================*/ { - rw_lock_free(&btr_search_latch); - ut_free(btr_search_latch_temp); - btr_search_latch_temp = NULL; - mem_heap_free(btr_search_sys->hash_index->heap); - hash_table_free(btr_search_sys->hash_index); + for (ulint i = 0; i < btr_search_index_num; i++) { + + rw_lock_free(&btr_search_latch_arr[i]); + + mem_heap_free(btr_search_sys->hash_tables[i]->heap); + + hash_table_free(btr_search_sys->hash_tables[i]); + + } + + ut_free(btr_search_latch_arr); + btr_search_latch_arr = NULL; + + ut_free(btr_search_sys->hash_tables); + ut_free(btr_search_sys); btr_search_sys = NULL; } @@ -227,13 +282,14 @@ btr_search_disable_ref_count( dict_index_t* index; ut_ad(mutex_own(&dict_sys->mutex)); -#ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); -#endif /* UNIV_SYNC_DEBUG */ for (index = dict_table_get_first_index(table); index; index = dict_table_get_next_index(index)) { +#ifdef UNIV_SYNC_DEBUG + ut_ad(rw_lock_own(btr_search_get_latch(index), + RW_LOCK_X)); +#endif /* UNIV_SYNC_DEBUG */ index->search_info->ref_count = 0; } } @@ -247,11 +303,11 @@ btr_search_disable(void) dict_table_t* table; mutex_enter(&dict_sys->mutex); - rw_lock_x_lock(&btr_search_latch); + btr_search_x_lock_all(); if (!btr_search_enabled) { mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(&btr_search_latch); + btr_search_x_unlock_all(); return; } @@ -277,10 +333,12 @@ btr_search_disable(void) buf_pool_clear_hash_index(); /* Clear the adaptive hash index. */ - hash_table_clear(btr_search_sys->hash_index); - mem_heap_empty(btr_search_sys->hash_index->heap); + for (ulint i = 0; i < btr_search_index_num; i++) { + hash_table_clear(btr_search_sys->hash_tables[i]); + mem_heap_empty(btr_search_sys->hash_tables[i]->heap); + } - rw_lock_x_unlock(&btr_search_latch); + btr_search_x_unlock_all(); } /********************************************************************//** @@ -296,11 +354,11 @@ btr_search_enable(void) } buf_pool_mutex_exit_all(); - rw_lock_x_lock(&btr_search_latch); + btr_search_x_lock_all(); btr_search_enabled = TRUE; - rw_lock_x_unlock(&btr_search_latch); + btr_search_x_unlock_all(); } /*****************************************************************//** @@ -345,25 +403,26 @@ btr_search_info_create( /*****************************************************************//** Returns the value of ref_count. The value is protected by -btr_search_latch. +the latch of the AHI partition corresponding to this index. @return ref_count value. */ ulint btr_search_info_get_ref_count( /*==========================*/ - btr_search_t* info) /*!< in: search info. */ + btr_search_t* info, /*!< in: search info. */ + dict_index_t* index) /*!< in: index */ { ulint ret; ut_ad(info); #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); ret = info->ref_count; - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); return(ret); } @@ -379,16 +438,14 @@ btr_search_info_update_hash( btr_search_t* info, /*!< in/out: search info */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { - dict_index_t* index; + dict_index_t* index = cursor->index; ulint n_unique; #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - index = cursor->index; - if (dict_index_is_ibuf(index)) { /* So many deletes are performed on an insert buffer tree that we do not consider a hash index useful on it: */ @@ -482,8 +539,8 @@ btr_search_update_block_hash_info( /*!< in: cursor */ { #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(cursor->index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(cursor->index), RW_LOCK_X)); ut_ad(rw_lock_own(&block->lock, RW_LOCK_S) || rw_lock_own(&block->lock, RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ @@ -563,7 +620,7 @@ btr_search_update_hash_ref( ut_ad(cursor->flag == BTR_CUR_HASH_FAIL); #ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(rw_lock_own(btr_search_get_latch(cursor->index), RW_LOCK_X)); ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S) || rw_lock_own(&(block->lock), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ @@ -603,11 +660,12 @@ btr_search_update_hash_ref( mem_heap_free(heap); } #ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(rw_lock_own(btr_search_get_latch(cursor->index), + RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - ha_insert_for_fold(btr_search_sys->hash_index, fold, - block, rec); + ha_insert_for_fold(btr_search_get_hash_table(cursor->index), + fold, block, rec); MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_ADDED); } @@ -625,8 +683,8 @@ btr_search_info_update_slow( ibool build_index; #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(cursor->index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(cursor->index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ block = btr_cur_get_block(cursor); @@ -642,7 +700,7 @@ btr_search_info_update_slow( if (build_index || (cursor->flag == BTR_CUR_HASH_FAIL)) { - btr_search_check_free_space_in_heap(); + btr_search_check_free_space_in_heap(cursor->index); } if (cursor->flag == BTR_CUR_HASH_FAIL) { @@ -652,11 +710,11 @@ btr_search_info_update_slow( btr_search_n_hash_fail++; #endif /* UNIV_SEARCH_PERF_STAT */ - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(cursor->index)); btr_search_update_hash_ref(info, block, cursor); - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(cursor->index)); } if (build_index) { @@ -681,12 +739,12 @@ btr_search_check_guess( btr_cur_t* cursor, /*!< in: guessed cursor position */ ibool can_only_compare_to_cursor_rec, /*!< in: if we do not have a latch on the page - of cursor, but only a latch on - btr_search_latch, then ONLY the columns - of the record UNDER the cursor are - protected, not the next or previous record - in the chain: we cannot look at the next or - previous record to check our guess! */ + of cursor, but only a latch on the AHI search + latch, then ONLY the columns of the record + UNDER the cursor are protected, not the next or + previous record in the chain: we cannot look at + the next or previous record to check our + guess! */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ @@ -849,8 +907,9 @@ btr_search_guess_on_hash( to protect the record! */ btr_cur_t* cursor, /*!< out: tree cursor */ ulint has_search_latch,/*!< in: latch mode the caller - currently has on btr_search_latch: - RW_S_LATCH, RW_X_LATCH, or 0 */ + currently has on the AHI search latch + for this index: RW_S_LATCH, RW_X_LATCH, + or 0 */ mtr_t* mtr) /*!< in: mtr */ { const rec_t* rec; @@ -894,10 +953,10 @@ btr_search_guess_on_hash( cursor->flag = BTR_CUR_HASH; if (!has_search_latch) { - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); if (!btr_search_enabled) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); btr_search_failure(info, cursor); @@ -905,15 +964,16 @@ btr_search_guess_on_hash( } } - ut_ad(rw_lock_get_writer(&btr_search_latch) != RW_LOCK_X); - ut_ad(rw_lock_get_reader_count(&btr_search_latch) > 0); + ut_ad(rw_lock_get_writer(btr_search_get_latch(index)) != RW_LOCK_X); + ut_ad(rw_lock_get_reader_count(btr_search_get_latch(index)) > 0); - rec = (rec_t*) ha_search_and_get_data(btr_search_sys->hash_index, fold); + rec = (rec_t*) ha_search_and_get_data( + btr_search_get_hash_table(index), fold); if (rec == NULL) { if (!has_search_latch) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); } btr_search_failure(info, cursor); @@ -930,7 +990,7 @@ btr_search_guess_on_hash( __FILE__, __LINE__, mtr)) { if (!has_search_latch) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); } btr_search_failure(info, cursor); @@ -938,7 +998,7 @@ btr_search_guess_on_hash( return(FALSE); } - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); } @@ -963,7 +1023,7 @@ btr_search_guess_on_hash( /* Check the validity of the guess within the page */ - /* If we only have the latch on btr_search_latch, not on the + /* If we only have the latch on the AHI search latch, not on the page, it only protects the columns of the record the cursor is positioned on. We cannot look at the next of the previous record to determine if our guess for the cursor position is @@ -1074,27 +1134,26 @@ btr_search_drop_page_hash_index( ulint* offsets; btr_search_t* info; +retry: /* Do a dirty check on block->index, return if the block is - not in the adaptive hash index. This is to avoid acquiring - shared btr_search_latch for performance consideration. */ - if (!block->index || block->index->disable_ahi) { + not in the adaptive hash index. This is to avoid acquiring an AHI latch + for performance considerations. */ + index = block->index; + if (!index || index->disable_ahi) { return; } #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ + rw_lock_s_lock(btr_search_get_latch(index)); -retry: - rw_lock_s_lock(&btr_search_latch); - index = block->index; - - if (UNIV_LIKELY(!index)) { + if (UNIV_UNLIKELY(index != block->index)) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); - return; + goto retry; } ut_ad(block->page.id.space() == index->space); @@ -1120,7 +1179,7 @@ retry: } #endif /* UNIV_DEBUG */ - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(index); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S) @@ -1132,10 +1191,10 @@ retry: n_fields = block->curr_n_fields; /* NOTE: The fields of block must not be accessed after - releasing btr_search_latch, as the index page might only + releasing the AHI search latch, as the index page might only be s-latched! */ - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); ut_a(n_fields > 0); @@ -1186,7 +1245,7 @@ next_rec: mem_heap_free(heap); } - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); if (UNIV_UNLIKELY(!block->index)) { /* Someone else has meanwhile dropped the hash index */ @@ -1201,7 +1260,7 @@ next_rec: /* Someone else has meanwhile built a new hash index on the page, with different parameters */ - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); ut_free(folds); goto retry; @@ -1230,14 +1289,14 @@ cleanup: << index->name << ", still " << block->n_pointers << " hash nodes remain."; - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); ut_ad(btr_search_validate()); } else { - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); } #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ ut_free(folds); @@ -1318,29 +1377,29 @@ btr_search_build_page_hash_index( ut_a(!dict_index_is_ibuf(index)); #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S) || rw_lock_own(&(block->lock), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); if (!btr_search_enabled) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); return; } - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(index); page = buf_block_get_frame(block); if (block->index && ((block->curr_n_fields != n_fields) || (block->curr_left_side != left_side))) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); btr_search_drop_page_hash_index(block); } else { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); } n_recs = page_get_n_recs(page); @@ -1427,9 +1486,9 @@ btr_search_build_page_hash_index( fold = next_fold; } - btr_search_check_free_space_in_heap(); + btr_search_check_free_space_in_heap(index); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); if (UNIV_UNLIKELY(!btr_search_enabled)) { goto exit_func; @@ -1463,7 +1522,7 @@ btr_search_build_page_hash_index( MONITOR_INC(MONITOR_ADAPTIVE_HASH_PAGE_ADDED); MONITOR_INC_VALUE(MONITOR_ADAPTIVE_HASH_ROW_ADDED, n_cached); exit_func: - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); ut_free(folds); ut_free(recs); @@ -1503,7 +1562,7 @@ btr_search_move_or_delete_hash_entries( ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); ut_a(!new_block->index || new_block->index == index); ut_a(!block->index || block->index == index); @@ -1512,7 +1571,7 @@ btr_search_move_or_delete_hash_entries( if (new_block->index) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); btr_search_drop_page_hash_index(block); @@ -1526,7 +1585,7 @@ btr_search_move_or_delete_hash_entries( new_block->n_fields = block->curr_n_fields; new_block->left_side = left_side; - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); ut_a(n_fields > 0); @@ -1537,7 +1596,7 @@ btr_search_move_or_delete_hash_entries( return; } - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); } /********************************************************************//** @@ -1580,7 +1639,7 @@ btr_search_update_hash_on_delete( ut_a(block->curr_n_fields > 0); ut_a(!dict_index_is_ibuf(index)); - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(cursor->index); rec = btr_cur_get_rec(cursor); @@ -1591,7 +1650,7 @@ btr_search_update_hash_on_delete( mem_heap_free(heap); } - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(cursor->index)); if (block->index) { ut_a(block->index == index); @@ -1604,7 +1663,7 @@ btr_search_update_hash_on_delete( } } - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(cursor->index)); } /********************************************************************//** @@ -1644,7 +1703,7 @@ btr_search_update_hash_node_on_insert( ut_a(cursor->index == index); ut_a(!dict_index_is_ibuf(index)); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(cursor->index)); if (!block->index) { @@ -1657,7 +1716,7 @@ btr_search_update_hash_node_on_insert( && (cursor->n_fields == block->curr_n_fields) && !block->curr_left_side) { - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(cursor->index); if (ha_search_and_update_if_found( table, cursor->fold, rec, block, @@ -1666,9 +1725,9 @@ btr_search_update_hash_node_on_insert( } func_exit: - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(cursor->index)); } else { - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(cursor->index)); btr_search_update_hash_on_insert(cursor); } @@ -1719,9 +1778,9 @@ btr_search_update_hash_on_insert( } ut_ad(block->page.id.space() == index->space); - btr_search_check_free_space_in_heap(); + btr_search_check_free_space_in_heap(cursor->index); - table = btr_search_sys->hash_index; + table = btr_search_get_hash_table(cursor->index); rec = btr_cur_get_rec(cursor); @@ -1752,7 +1811,7 @@ btr_search_update_hash_on_insert( } else { if (left_side) { - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); locked = TRUE; @@ -1770,7 +1829,7 @@ btr_search_update_hash_on_insert( if (!locked) { - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); locked = TRUE; @@ -1792,7 +1851,7 @@ check_next_rec: if (!left_side) { if (!locked) { - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); locked = TRUE; @@ -1811,7 +1870,7 @@ check_next_rec: if (!locked) { - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(btr_search_get_latch(index)); locked = TRUE; @@ -1832,17 +1891,19 @@ function_exit: mem_heap_free(heap); } if (locked) { - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(btr_search_get_latch(index)); } } #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG /********************************************************************//** -Validates the search system. +Validates one hash table in the search system. @return TRUE if ok */ +static ibool -btr_search_validate(void) -/*=====================*/ +btr_search_validate_one_table( +/*==========================*/ + ulint t) { ha_node_t* node; ulint n_page_dumps = 0; @@ -1853,32 +1914,32 @@ btr_search_validate(void) ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; - /* How many cells to check before temporarily releasing - btr_search_latch. */ + /* How many cells to check before temporarily releasing the AHI search + latches */ ulint chunk_size = 10000; rec_offs_init(offsets_); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(&btr_search_latch_arr[t]); buf_pool_mutex_enter_all(); - cell_count = hash_get_n_cells(btr_search_sys->hash_index); + cell_count = hash_get_n_cells(btr_search_sys->hash_tables[t]); for (i = 0; i < cell_count; i++) { - /* We release btr_search_latch every once in a while to + /* We release the AHI search latches every once in a while to give other queries a chance to run. */ if ((i != 0) && ((i % chunk_size) == 0)) { buf_pool_mutex_exit_all(); - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(&btr_search_latch_arr[t]); os_thread_yield(); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(&btr_search_latch_arr[t]); buf_pool_mutex_enter_all(); if (cell_count != hash_get_n_cells( - btr_search_sys->hash_index)) { + btr_search_sys->hash_tables[t])) { cell_count = hash_get_n_cells( - btr_search_sys->hash_index); + btr_search_sys->hash_tables[t]); if (i >= cell_count) { break; @@ -1887,7 +1948,8 @@ btr_search_validate(void) } node = (ha_node_t*) - hash_get_nth_cell(btr_search_sys->hash_index, i)->node; + hash_get_nth_cell(btr_search_sys->hash_tables[t], + i)->node; for (; node != NULL; node = node->next) { const buf_block_t* block @@ -1982,20 +2044,20 @@ btr_search_validate(void) } for (i = 0; i < cell_count; i += chunk_size) { - /* We release btr_search_latch every once in a while to + /* We release the AHI search latches every once in a while to give other queries a chance to run. */ if (i != 0) { buf_pool_mutex_exit_all(); - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(&btr_search_latch_arr[t]); os_thread_yield(); - rw_lock_x_lock(&btr_search_latch); + rw_lock_x_lock(&btr_search_latch_arr[t]); buf_pool_mutex_enter_all(); if (cell_count != hash_get_n_cells( - btr_search_sys->hash_index)) { + btr_search_sys->hash_tables[t])) { cell_count = hash_get_n_cells( - btr_search_sys->hash_index); + btr_search_sys->hash_tables[t]); if (i >= cell_count) { break; @@ -2005,17 +2067,39 @@ btr_search_validate(void) ulint end_index = ut_min(i + chunk_size - 1, cell_count - 1); - if (!ha_validate(btr_search_sys->hash_index, i, end_index)) { + if (!ha_validate(btr_search_sys->hash_tables[t], i, + end_index)) { ok = FALSE; } } buf_pool_mutex_exit_all(); - rw_lock_x_unlock(&btr_search_latch); + rw_lock_x_unlock(&btr_search_latch_arr[t]); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ok); } + +/********************************************************************//** +Validates the search system. +@return TRUE if ok */ + +ibool +btr_search_validate(void) +/*=====================*/ +{ + ibool ok = TRUE; + + for (ulint i = 0; i < btr_search_index_num; i++) { + + if (!btr_search_validate_one_table(i)) + ok = FALSE; + } + + return(ok); +} + + #endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */ diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index f0fba5f..366776b 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -2307,12 +2307,12 @@ buf_pool_resize() buf_resize_status("Disabling adaptive hash index."); - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(&btr_search_latch_arr[0]); if (btr_search_enabled) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(&btr_search_latch_arr[0]); btr_search_disabled = true; } else { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(&btr_search_latch_arr[0]); } btr_search_disable(); @@ -2766,7 +2766,7 @@ buf_pool_clear_hash_index(void) ulint p; #ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(btr_search_own_all(RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(!buf_pool_resizing); ut_ad(!btr_search_enabled); @@ -2784,7 +2784,7 @@ buf_pool_clear_hash_index(void) dict_index_t* index = block->index; /* We can set block->index = NULL - when we have an x-latch on btr_search_latch; + when we have the AHI latches x-latched, see the comment in buf0buf.h */ if (!index) { diff --git a/storage/innobase/dict/dict0boot.cc b/storage/innobase/dict/dict0boot.cc index 77250ed..a63330b 100644 --- a/storage/innobase/dict/dict0boot.cc +++ b/storage/innobase/dict/dict0boot.cc @@ -33,6 +33,7 @@ Created 4/18/1996 Heikki Tuuri #include "dict0crea.h" #include "btr0btr.h" +#include "btr0sea.h" #include "dict0load.h" #include "trx0trx.h" #include "srv0srv.h" @@ -365,6 +366,7 @@ dict_boot(void) dict_mem_index_add_field(index, "NAME", 0); index->id = DICT_TABLES_ID; + btr_search_index_init(index); error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr @@ -379,6 +381,7 @@ dict_boot(void) dict_mem_index_add_field(index, "ID", 0); index->id = DICT_TABLE_IDS_ID; + btr_search_index_init(index); error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_TABLE_IDS, @@ -411,6 +414,7 @@ dict_boot(void) dict_mem_index_add_field(index, "POS", 0); index->id = DICT_COLUMNS_ID; + btr_search_index_init(index); error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_COLUMNS, @@ -445,6 +449,7 @@ dict_boot(void) dict_mem_index_add_field(index, "ID", 0); index->id = DICT_INDEXES_ID; + btr_search_index_init(index); error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_INDEXES, @@ -473,6 +478,7 @@ dict_boot(void) dict_mem_index_add_field(index, "POS", 0); index->id = DICT_FIELDS_ID; + btr_search_index_init(index); error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_FIELDS, diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 481b4c1..e4f3c86 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -33,6 +33,7 @@ Created 1/8/1996 Heikki Tuuri #include "btr0pcur.h" #include "btr0btr.h" +#include "btr0sea.h" #include "page0page.h" #include "mach0data.h" #include "dict0boot.h" @@ -819,6 +820,7 @@ dict_build_index_def( index->id = 1; } } + btr_search_index_init(index); /* Inherit the space id from the table; we store all indexes of a table in the same tablespace */ diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 71636ee..e7d209a 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1349,7 +1349,7 @@ dict_table_can_be_evicted( See also: dict_index_remove_from_cache_low() */ - if (btr_search_info_get_ref_count(info) > 0) { + if (btr_search_info_get_ref_count(info, index) > 0) { return(FALSE); } } @@ -2785,7 +2785,8 @@ dict_index_remove_from_cache_low( zero. See also: dict_table_can_be_evicted() */ do { - ulint ref_count = btr_search_info_get_ref_count(info); + ulint ref_count = btr_search_info_get_ref_count(info, + index); if (ref_count == 0) { break; @@ -3107,6 +3108,7 @@ dict_index_build_internal_clust( new_index->n_user_defined_cols = index->n_fields; new_index->id = index->id; + btr_search_index_init(new_index); /* Copy the fields of index */ dict_index_copy(new_index, index, table, 0, index->n_fields); @@ -3279,6 +3281,7 @@ dict_index_build_internal_non_clust( new_index->n_user_defined_cols = index->n_fields; new_index->id = index->id; + btr_search_index_init(new_index); /* Copy fields from index to new_index */ dict_index_copy(new_index, index, table, 0, index->n_fields); @@ -3367,6 +3370,7 @@ dict_index_build_internal_fts( new_index->n_user_defined_cols = index->n_fields; new_index->id = index->id; + btr_search_index_init(new_index); /* Copy fields from index to new_index */ dict_index_copy(new_index, index, table, 0, index->n_fields); diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 1677d7a..d1ab5e6 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -34,6 +34,7 @@ Created 4/24/1996 Heikki Tuuri #include "mysql_version.h" #include "btr0pcur.h" #include "btr0btr.h" +#include "btr0sea.h" #include "dict0boot.h" #include "dict0crea.h" #include "dict0dict.h" @@ -2151,6 +2152,7 @@ err_len: (*index)->id = id; (*index)->page = mach_read_from_4(field); + btr_search_index_init(*index); ut_ad((*index)->page); (*index)->merge_threshold = merge_threshold; diff --git a/storage/innobase/ha/ha0ha.cc b/storage/innobase/ha/ha0ha.cc index 32f4dd2..61587b5 100644 --- a/storage/innobase/ha/ha0ha.cc +++ b/storage/innobase/ha/ha0ha.cc @@ -139,6 +139,33 @@ ib_recreate( return(new_table); } +#ifdef UNIV_SYNC_DEBUG +/*************************************************************//** +Verifies that the specified hash table is a part of adaptive hash index and +that its corresponding latch is X-latched by the current thread. */ +static +bool +ha_assert_btr_x_locked( +/*===================*/ + const hash_table_t* table) /*!adaptive); + + for (i = 0; i < btr_search_index_num; i++) { + if (btr_search_sys->hash_tables[i] == table) { + break; + } + } + + ut_ad(i < btr_search_index_num); + ut_ad(rw_lock_own(&btr_search_latch_arr[i], RW_LOCK_X)); + + return(true); +} +#endif /* UNIV_SYNC_DEBUG */ + /*************************************************************//** Empties a hash table and frees the memory heaps. */ void @@ -148,7 +175,7 @@ ha_clear( { ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #ifdef UNIV_SYNC_DEBUG - ut_ad(!table->adaptive || rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!table->adaptive || ha_assert_btr_x_locked(table)); #endif /* UNIV_SYNC_DEBUG */ for (ulint i = 0; i < table->n_sync_obj; i++) { @@ -309,7 +336,7 @@ ha_delete_hash_node( ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(ha_assert_btr_x_locked(table)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(btr_search_enabled); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG @@ -347,7 +374,7 @@ ha_search_and_update_if_found_func( ut_a(new_block->frame == page_align(new_data)); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ #ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(ha_assert_btr_x_locked(table)); #endif /* UNIV_SYNC_DEBUG */ if (!btr_search_enabled) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 309b93c..d939873 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4982,9 +4982,6 @@ ha_innobase::open( thd = ha_thd(); - /* Under some cases MySQL seems to call this function while - holding btr_search_latch. This breaks the latching order as - we acquire dict_sys->mutex below and leads to a deadlock. */ if (thd != NULL) { innobase_release_temporary_latches(ht, thd); } @@ -17404,6 +17401,14 @@ static MYSQL_SYSVAR_BOOL(adaptive_hash_index, btr_search_enabled, " Disable with --skip-innodb-adaptive-hash-index.", NULL, innodb_adaptive_hash_index_update, TRUE); +/* btr_search_index_num is constrained to machine word size for historical +reasons. This limitation can be easily removed later. */ +static MYSQL_SYSVAR_ULONG(adaptive_hash_index_partitions, btr_search_index_num, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Number of InnoDB adaptive hash index partitions (default 1: disable " + "partitioning)", + NULL, NULL, 1, 1, sizeof(ulint) * 8, 0); + static MYSQL_SYSVAR_ULONG(replication_delay, srv_replication_delay, PLUGIN_VAR_RQCMDARG, "Replication thread delay (ms) on the slave server if" @@ -18048,6 +18053,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(stats_persistent_sample_pages), MYSQL_SYSVAR(stats_auto_recalc), MYSQL_SYSVAR(adaptive_hash_index), + MYSQL_SYSVAR(adaptive_hash_index_partitions), MYSQL_SYSVAR(stats_method), MYSQL_SYSVAR(replication_delay), MYSQL_SYSVAR(status_file), diff --git a/storage/innobase/handler/ha_innopart.cc b/storage/innobase/handler/ha_innopart.cc index 492c7bf..85e1db1 100644 --- a/storage/innobase/handler/ha_innopart.cc +++ b/storage/innobase/handler/ha_innopart.cc @@ -913,10 +913,6 @@ ha_innopart::open( } thd = ha_thd(); - /* Under some cases MySQL seems to call this function while - holding btr_search_latch. This breaks the latching order as - we acquire dict_sys->mutex below and leads to a deadlock. */ - if (thd != NULL) { innobase_release_temporary_latches(ht, thd); } diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 5e9b72f..3988cc7 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -555,6 +555,7 @@ ibuf_init_at_db_start(void) "innodb_change_buffer", "CLUST_IND", IBUF_SPACE_ID, DICT_CLUSTERED | DICT_IBUF, 1); ibuf->index->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID; + btr_search_index_init(ibuf->index); ibuf->index->table = dict_mem_table_create( "innodb_change_buffer", IBUF_SPACE_ID, 1, 0, 0); ibuf->index->n_uniq = REC_MAX_N_FIELDS; diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 3cd064b..b9313a2 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -185,8 +185,8 @@ btr_cur_search_to_nth_level( btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ ulint has_search_latch,/*!< in: latch mode the caller - currently has on btr_search_latch: - RW_S_LATCH, or 0 */ + currently has on the AHI search latch for this + index: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index beef0c5..7bc0dac 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -139,8 +139,8 @@ btr_pcur_open_with_no_init_func( btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ ulint has_search_latch,/*!< in: latch mode the caller - currently has on btr_search_latch: - RW_S_LATCH, or 0 */ + currently has on the AHI search latch for this + index: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index ccefa80..210fbbf 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -492,7 +492,7 @@ btr_pcur_open_with_no_init_func( btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ ulint has_search_latch,/*!< in: latch mode the caller - currently has on btr_search_latch: + currently has on the AHI search latch: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index fece9f4..24b75c9 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -81,12 +81,13 @@ btr_search_info_create( mem_heap_t* heap); /*!< in: heap where created */ /*****************************************************************//** Returns the value of ref_count. The value is protected by -btr_search_latch. +the latch of the AHI partition corresponding to this index. @return ref_count value. */ ulint btr_search_info_get_ref_count( /*==========================*/ - btr_search_t* info); /*!< in: search info. */ + btr_search_t* info, /*!< in: search info. */ + dict_index_t* index); /*!< in: index */ /*********************************************************************//** Updates the search info. */ UNIV_INLINE @@ -111,8 +112,9 @@ btr_search_guess_on_hash( ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< out: tree cursor */ ulint has_search_latch,/*!< in: latch mode the caller - currently has on btr_search_latch: - RW_S_LATCH, RW_X_LATCH, or 0 */ + currently has on the AHI search latch + for this index: RW_S_LATCH, RW_X_LATCH, + or 0 */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** Moves or deletes hash entries for moved records. If new_page is already hashed, @@ -181,12 +183,67 @@ ibool btr_search_validate(void); /*======================*/ +/********************************************************************//** +Returns the adaptive hash index latch for a given index key. +@return the adaptive hash index latch for a given index key */ +UNIV_INLINE +rw_lock_t* +btr_search_get_latch( +/*=================*/ + const dict_index_t* index) /*!< in: index */ + __attribute__((pure,warn_unused_result)); + +/*********************************************************************//** +Initializes AHI-related fields in a newly created index. */ +UNIV_INLINE +void +btr_search_index_init( +/*===============*/ + dict_index_t* index); /*!< in: index */ + +/********************************************************************//** +Latches all adaptive hash index latches in exclusive mode. */ +UNIV_INLINE +void +btr_search_x_lock_all(void); +/*========================*/ + +/********************************************************************//** +Unlatches all adaptive hash index latches in exclusive mode. */ +UNIV_INLINE +void +btr_search_x_unlock_all(void); +/*==========================*/ + +#ifdef UNIV_SYNC_DEBUG +/******************************************************************//** +Checks if the thread has locked all the adaptive hash index latches in the +specified mode. + +@return true if all latches are locked by the current thread, false +otherwise. */ +UNIV_INLINE +bool +btr_search_own_all( +/*===============*/ + ulint lock_type) + __attribute__((warn_unused_result)); +/********************************************************************//** +Checks if the thread owns any adaptive hash latches in either S or X mode. +@return true if the thread owns at least one latch in any mode. */ +UNIV_INLINE +bool +btr_search_own_any(void) +/*=====================*/ + __attribute__((warn_unused_result)); +#endif + /** The search info struct in an index */ struct btr_search_t{ ulint ref_count; /*!< Number of blocks in this index tree that have search index built i.e. block->index points to this index. - Protected by btr_search_latch except + Protected by the AHI search latch except when during initialization in btr_search_info_create(). */ @@ -237,9 +294,9 @@ struct btr_search_t{ /** The hash index system */ struct btr_search_sys_t{ - hash_table_t* hash_index; /*!< the adaptive hash index, - mapping dtuple_fold values - to rec_t pointers on index pages */ + hash_table_t** hash_tables; /*!< the array of adaptive hash index + tables, mapping dtuple_fold values to + rec_t pointers on index pages */ }; /** The adaptive hash index */ diff --git a/storage/innobase/include/btr0sea.ic b/storage/innobase/include/btr0sea.ic index 591df1d..3d40472 100644 --- a/storage/innobase/include/btr0sea.ic +++ b/storage/innobase/include/btr0sea.ic @@ -59,8 +59,8 @@ btr_search_info_update( btr_search_t* info; #ifdef UNIV_SYNC_DEBUG - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_S)); - ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_X)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); + ut_ad(!rw_lock_own(btr_search_get_latch(index), RW_LOCK_X)); #endif /* UNIV_SYNC_DEBUG */ if (dict_index_is_spatial(index)) { @@ -83,3 +83,121 @@ btr_search_info_update( btr_search_info_update_slow(info, cursor); } + +/********************************************************************//** +Returns the adaptive hash index latch for a given index key. +@return the adaptive hash index latch for a given index key */ +UNIV_INLINE +rw_lock_t* +btr_search_get_latch( +/*=================*/ + const dict_index_t* index) /*!< in: index */ +{ + ut_ad(index); + ut_ad(index->search_latch >= btr_search_latch_arr && + index->search_latch < btr_search_latch_arr + + btr_search_index_num); + + return(index->search_latch); +} + +/*********************************************************************//** +Returns the AHI partition number corresponding to a given index ID. */ +__attribute__((pure,warn_unused_result)) +UNIV_INLINE +ulint +btr_search_get_key( +/*===============*/ + index_id_t index_id) /*!< in: index ID */ +{ + return(index_id % btr_search_index_num); +} + +/*********************************************************************//** +Initializes AHI-related fields in a newly created index. */ +UNIV_INLINE +void +btr_search_index_init( +/*===============*/ + dict_index_t* index) /*!< in: index */ +{ + ut_ad(index); + + index->search_latch = + &btr_search_latch_arr[btr_search_get_key(index->id)]; + index->search_table = + btr_search_sys->hash_tables[btr_search_get_key(index->id)]; +} + +/********************************************************************//** +Latches all adaptive hash index latches in exclusive mode. */ +UNIV_INLINE +void +btr_search_x_lock_all(void) +/*=======================*/ +{ + ulint i; + + for (i = 0; i < btr_search_index_num; i++) { + rw_lock_x_lock(&btr_search_latch_arr[i]); + } +} + +/********************************************************************//** +Unlatches all adaptive hash index latches in exclusive mode. */ +UNIV_INLINE +void +btr_search_x_unlock_all(void) +/*==========================*/ +{ + ulint i; + + for (i = 0; i < btr_search_index_num; i++) { + rw_lock_x_unlock(&btr_search_latch_arr[i]); + } +} + +#ifdef UNIV_SYNC_DEBUG +/******************************************************************//** +Checks if the thread has locked all the adaptive hash index latches in the +specified mode. + +@return true if all latches are locked by the current thread, false +otherwise. */ +UNIV_INLINE +bool +btr_search_own_all( +/*===============*/ + ulint lock_type) +{ + ulint i; + + for (i = 0; i < btr_search_index_num; i++) { + if (!rw_lock_own(&btr_search_latch_arr[i], lock_type)) { + return(false); + } + } + + return(true); +} + +/********************************************************************//** +Checks if the thread owns any adaptive hash latches in either S or X mode. +@return true if the thread owns at least one latch in any mode. */ +UNIV_INLINE +bool +btr_search_own_any(void) +/*====================*/ +{ + ulint i; + + for (i = 0; i < btr_search_index_num; i++) { + if (rw_lock_own(&btr_search_latch_arr[i], RW_LOCK_S) || + rw_lock_own(&btr_search_latch_arr[i], RW_LOCK_X)) { + return(true); + } + } + + return(false); +} +#endif /* UNIV_SYNC_DEBUG */ diff --git a/storage/innobase/include/btr0types.h b/storage/innobase/include/btr0types.h index 4532c51..eba9558 100644 --- a/storage/innobase/include/btr0types.h +++ b/storage/innobase/include/btr0types.h @@ -42,30 +42,31 @@ struct btr_search_t; #ifndef UNIV_HOTBACKUP -/** @brief The latch protecting the adaptive search system +/** @brief The array of latches protecting the adaptive search partitions -This latch protects the -(1) hash index; +These latches protect the +(1) hash index from the corresponding AHI partition; (2) columns of a record to which we have a pointer in the hash index; -but does NOT protect: +but do NOT protect: (3) next record offset field in a record; (4) next or previous records on the same page. -Bear in mind (3) and (4) when using the hash index. +Bear in mind (3) and (4) when using the hash indexes. */ -extern rw_lock_t* btr_search_latch_temp; +extern rw_lock_t* btr_search_latch_arr; #endif /* UNIV_HOTBACKUP */ -/** The latch protecting the adaptive search system */ -#define btr_search_latch (*btr_search_latch_temp) - /** Flag: has the search system been enabled? -Protected by btr_search_latch. */ +Writes are protected by latching all the AHI search latches, reads are +protected by latching any single one of them. */ extern char btr_search_enabled; +/** Number of adaptive hash index partitions */ +extern ulint btr_search_index_num; + /** The size of a reference to data stored on a different page. The reference is stored at the end of the prefix of the field in the index record. */ diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index ad9be58..1f0881a 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1772,7 +1772,7 @@ struct buf_block_t{ /** @name Hash search fields These 5 fields may only be modified when we have - an x-latch on btr_search_latch AND + an x-latch on the AHI latch corresponding to index AND - we are holding an s-latch or x-latch on buf_block_t::lock or - we know that buf_block_t::buf_fix_count == 0. @@ -1780,7 +1780,8 @@ struct buf_block_t{ in the buffer pool in buf0buf.cc. Another exception is that assigning block->index = NULL - is allowed whenever holding an x-latch on btr_search_latch. */ + is allowed whenever holding an x-latch on the corresponding AHI search + latch. */ /* @{ */ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 6ed35f5..a498f7f 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -735,6 +735,11 @@ to start with. */ initialized to 0, NULL or FALSE in dict_mem_index_create(). */ struct dict_index_t{ index_id_t id; /*!< id of the index */ + rw_lock_t* search_latch; + /*!< latch protecting the AHI partition + corresponding to this index */ + hash_table_t* search_table; + /*!< hash table protected by search_latch */ mem_heap_t* heap; /*!< memory heap */ id_name_t name; /*!< index name */ const char* table_name;/*!< table name */ diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index ba34a5c..9791ccf 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -63,13 +63,6 @@ void trx_search_latch_release_if_reserved(trx_t* trx); /** -Reserve the AHI latch if not already reserved by this transaction. -@param[in,out] trx Transaction that may own the AHI latch */ -UNIV_INLINE -void -trx_reserve_search_latch_if_not_reserved(trx_t* trx); - -/** Releases the search latch if the transaction has been hogging it for too long. @param[in,out] trx Transaction that may own the AHI latch */ UNIV_INLINE @@ -1373,7 +1366,7 @@ but does NOT protect: Bear in mind (3) and (4) when using the hash index. */ -extern rw_lock_t* btr_search_latch_temp; +extern rw_lock_t* btr_search_latch_arr; /** Track if a transaction is executing inside InnoDB code. It acts like a gate between the Server and InnoDB. */ @@ -1584,9 +1577,6 @@ private: trx_t* m_trx; }; -/** The latch protecting the adaptive search system */ -#define btr_search_latch (*btr_search_latch_temp) - #ifndef UNIV_NONINL #include "trx0trx.ic" #endif diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index 3342126..ff5b4d7 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -208,47 +208,9 @@ Releases the search latch if trx has reserved it. @param[in,out] trx Transaction that may own the AHI latch */ UNIV_INLINE void -trx_search_latch_release_if_reserved(trx_t* trx) +trx_search_latch_release_if_reserved(trx_t* trx __attribute__((unused))) { - if (trx->has_search_latch - && trx->killed_by != os_thread_get_curr_id()) { - - rw_lock_s_unlock(&btr_search_latch); - - trx->has_search_latch = false; - } -} - -/** -Reserve the AHI latch if not already reserved by this transaction. -@param[in,out] trx Transaction that may own the AHI latch */ -UNIV_INLINE -void -trx_reserve_search_latch_if_not_reserved(trx_t* trx) -{ - if (!trx->has_search_latch) { - - rw_lock_s_lock(&btr_search_latch); - - trx->has_search_latch = true; - } -} - -/** -Releases the search latch if the transaction has been hogging it for too long. -@param[in,out] trx Transaction that may own the AHI latch */ -UNIV_INLINE -void -trx_search_latch_timeout(trx_t* trx) -{ - if (trx->search_latch_timeout > 0 && trx->has_search_latch) { -#ifndef INNODB_RW_LOCKS_USE_ATOMICS - trx->search_latch_timeout--; -#endif /* !INNODB_RW_LOCKS_USE_ATOMICS */ - rw_lock_s_unlock(&btr_search_latch); - - trx->has_search_latch = false; - } + ut_ad(!trx->has_search_latch); } /********************************************************************//** diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index 9471bf5..51773f3 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -33,6 +33,7 @@ Created 10/13/2010 Jimmy Yang #include "row0row.h" #include "btr0cur.h" #include "btr0bulk.h" +#include "btr0sea.h" #include "fts0plugin.h" /** Read the next record to buffer N. @@ -91,6 +92,7 @@ row_merge_create_fts_sort_index( new_index->cached = TRUE; new_index->parser = index->parser; new_index->is_ngram = index->is_ngram; + btr_search_index_init(new_index); idx_field = dict_index_get_nth_field(index, 0); charset = fts_index_get_charset(index); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 4f940fd..257dae7 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -1433,7 +1433,7 @@ row_sel_try_search_shortcut( index */ ibool search_latch_locked, /*!< in: whether the search holds - btr_search_latch */ + AHI search latch for plan->index */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; @@ -1451,7 +1451,7 @@ row_sel_try_search_shortcut( ut_ad(!plan->must_get_clust); #ifdef UNIV_SYNC_DEBUG if (search_latch_locked) { - ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_S)); + ut_ad(rw_lock_own(btr_search_get_latch(index), RW_LOCK_S)); } #endif /* UNIV_SYNC_DEBUG */ @@ -1625,10 +1625,11 @@ table_loop: && !plan->must_get_clust && !plan->table->big_rows) { if (!search_latch_locked) { - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); search_latch_locked = TRUE; - } else if (rw_lock_get_writer(&btr_search_latch) == RW_LOCK_X_WAIT) { + } else if (rw_lock_get_writer(btr_search_get_latch(index)) + == RW_LOCK_X_WAIT) { /* There is an x-latch request waiting: release the s-latch for a moment; as an s-latch here is often @@ -1637,8 +1638,8 @@ table_loop: from acquiring an s-latch for a long time, lowering performance significantly in multiprocessors. */ - rw_lock_s_unlock(&btr_search_latch); - rw_lock_s_lock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); + rw_lock_s_lock(btr_search_get_latch(index)); } found_flag = row_sel_try_search_shortcut(node, plan, @@ -1663,7 +1664,7 @@ table_loop: } if (search_latch_locked) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); search_latch_locked = FALSE; } @@ -2257,7 +2258,7 @@ lock_wait_or_error: func_exit: if (search_latch_locked) { - rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_unlock(btr_search_get_latch(index)); } if (heap != NULL) { @@ -3051,6 +3052,9 @@ row_sel_store_mysql_field_func( /* Copy an externally stored field to a temporary heap */ ut_a(!prebuilt->trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!btr_search_own_any()); +#endif ut_ad(field_no == templ->clust_rec_field_no); ut_ad(templ->type != DATA_POINT); @@ -3838,12 +3842,10 @@ row_sel_try_search_shortcut_for_mysql( ut_ad(dict_index_is_clust(index)); ut_ad(!prebuilt->templ_contains_blob); + ut_ad(trx->has_search_latch); + btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, pcur, - (trx->has_search_latch) - ? RW_S_LATCH - : 0, - mtr); + BTR_SEARCH_LEAF, pcur, RW_S_LATCH, mtr); rec = btr_pcur_get_rec(pcur); if (!page_rec_is_user_rec(rec)) { @@ -4372,26 +4374,6 @@ row_search_mvcc( DBUG_RETURN(DB_CORRUPTION); } - /*-------------------------------------------------------------*/ - /* PHASE 0: Release a possible s-latch we are holding on the - adaptive hash index latch if there is someone waiting behind */ - - if (trx->has_search_latch -#ifndef INNODB_RW_LOCKS_USE_ATOMICS - && rw_lock_get_writer(&btr_search_latch) != RW_LOCK_NOT_LOCKED -#endif /* !INNODB_RW_LOCKS_USE_ATOMICS */ - ) { - - /* There is an x-latch request on the adaptive hash index: - release the s-latch to reduce starvation and wait for - BTR_SEA_TIMEOUT rounds before trying to keep it again over - calls from MySQL */ - - trx_search_latch_release_if_reserved(trx); - - trx->search_latch_timeout = BTR_SEA_TIMEOUT; - } - /* Reset the new record lock info if srv_locks_unsafe_for_binlog is set or session is using a READ COMMITED isolation level. Then we are able to remove the record locks set here on an individual @@ -4546,7 +4528,9 @@ row_search_mvcc( and if we try that, we can deadlock on the adaptive hash index semaphore! */ - trx_reserve_search_latch_if_not_reserved(trx); + ut_ad(!trx->has_search_latch); + rw_lock_s_lock(btr_search_get_latch(index)); + trx->has_search_latch = true; switch (row_sel_try_search_shortcut_for_mysql( &rec, prebuilt, &offsets, &heap, @@ -4597,7 +4581,8 @@ row_search_mvcc( err = DB_SUCCESS; - trx_search_latch_timeout(trx); + rw_lock_s_unlock(btr_search_get_latch(index)); + trx->has_search_latch = false; goto func_exit; @@ -4607,7 +4592,8 @@ row_search_mvcc( err = DB_RECORD_NOT_FOUND; - trx_search_latch_timeout(trx); + rw_lock_s_unlock(btr_search_get_latch(index)); + trx->has_search_latch = false; /* NOTE that we do NOT store the cursor position */ @@ -4623,13 +4609,19 @@ row_search_mvcc( mtr_commit(&mtr); mtr_start(&mtr); + + rw_lock_s_unlock(btr_search_get_latch(index)); + trx->has_search_latch = false; } } /*-------------------------------------------------------------*/ /* PHASE 3: Open or restore index cursor position */ - trx_search_latch_release_if_reserved(trx); + ut_ad(!trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!btr_search_own_any()); +#endif spatial_search = dict_index_is_spatial(index) && mode >= PAGE_CUR_CONTAIN; @@ -5830,6 +5822,10 @@ func_exit: } } + ut_ad(!trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!btr_search_own_any()); +#endif { btrsea_sync_check check(trx->has_search_latch); diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 08dcbee..4fc9256 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1209,9 +1209,13 @@ srv_printf_innodb_monitor( "-------------------------------------\n", file); ibuf_print(file); - rw_lock_s_lock(&btr_search_latch); - ha_print_info(file, btr_search_sys->hash_index); - rw_lock_s_unlock(&btr_search_latch); + for (ulint i = 0; i < btr_search_index_num; i++) { + + fprintf(file, "Adaptive hash index partition %lu:\n", i); + rw_lock_s_lock(&btr_search_latch_arr[i]); + ha_print_info(file, btr_search_sys->hash_tables[i]); + rw_lock_s_unlock(&btr_search_latch_arr[i]); + } fprintf(file, "%.2f hash searches/s, %.2f non-hash searches/s\n", diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index d61c717..fc00b44 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -256,6 +256,9 @@ struct TrxFactory { ut_a(trx->lock.wait_thr == NULL); ut_a(!trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!btr_search_own_any()); +#endif ut_a(trx->dict_operation_lock_mode == 0); @@ -326,6 +329,9 @@ struct TrxFactory { ut_a(trx->lock.wait_lock == NULL); ut_a(!trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!btr_search_own_any()); +#endif ut_a(trx->dict_operation_lock_mode == 0);