Commit 54fd56c5 authored by Sunny Bains's avatar Sunny Bains

Fix Bug #54538 - use of exclusive innodb dictionary lock limits performance.

            
This patch doesn't get rid of the need to acquire the dict_sys->mutex but
reduces the need to keep the mutex locked for the duration of the query
to fsp_get_available_space_in_free_extents() from ha_innobase::info().

rb://390.
parent eff4a5d2
...@@ -966,6 +966,8 @@ try_again: ...@@ -966,6 +966,8 @@ try_again:
HASH_SEARCH(name_hash, system->name_hash, ut_fold_string(name), space, HASH_SEARCH(name_hash, system->name_hash, ut_fold_string(name), space,
0 == strcmp(name, space->name)); 0 == strcmp(name, space->name));
if (space != NULL) { if (space != NULL) {
ibool success;
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: Warning: trying to init to the" " InnoDB: Warning: trying to init to the"
...@@ -1002,9 +1004,10 @@ try_again: ...@@ -1002,9 +1004,10 @@ try_again:
namesake_id = space->id; namesake_id = space->id;
mutex_exit(&(system->mutex)); success = fil_space_free(namesake_id, FALSE);
ut_a(success);
fil_space_free(namesake_id); mutex_exit(&(system->mutex));
goto try_again; goto try_again;
} }
...@@ -1127,6 +1130,33 @@ fil_assign_new_space_id(void) ...@@ -1127,6 +1130,33 @@ fil_assign_new_space_id(void)
return(id); return(id);
} }
/***********************************************************************
Check if the space id exists in the cache, complain to stderr if the
space id cannot be found. */
static
fil_space_t*
fil_space_search(
/*=============*/
/* out: file space instance*/
ulint id) /* in: space id */
{
fil_space_t* space;
ut_ad(mutex_own(&fil_system->mutex));
HASH_SEARCH(hash, fil_system->spaces, id, space, space->id == id);
if (space == NULL) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: trying to remove tablespace %lu"
" from the cache but\n"
"InnoDB: it is not there.\n", (ulong) id);
}
return(space);
}
/*********************************************************************** /***********************************************************************
Frees a space object from the tablespace memory cache. Closes the files in Frees a space object from the tablespace memory cache. Closes the files in
the chain but does not delete them. There must not be any pending i/o's or the chain but does not delete them. There must not be any pending i/o's or
...@@ -1135,27 +1165,21 @@ flushes on the files. */ ...@@ -1135,27 +1165,21 @@ flushes on the files. */
ibool ibool
fil_space_free( fil_space_free(
/*===========*/ /*===========*/
/* out: TRUE if success */ /* out: TRUE if success */
ulint id) /* in: space id */ ulint id, /* in: space id */
ibool x_latched) /* in: TRUE if caller has space->latch
in X mode */
{ {
fil_system_t* system = fil_system; fil_system_t* system = fil_system;
fil_space_t* space; fil_space_t* space;
fil_space_t* namespace; fil_space_t* namespace;
fil_node_t* fil_node; fil_node_t* fil_node;
mutex_enter(&(system->mutex)); ut_ad(mutex_own(&fil_system->mutex));
HASH_SEARCH(hash, system->spaces, id, space, space->id == id); space = fil_space_search(id);
if (!space) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: trying to remove tablespace %lu"
" from the cache but\n"
"InnoDB: it is not there.\n", (ulong) id);
mutex_exit(&(system->mutex));
if (space == NULL) {
return(FALSE); return(FALSE);
} }
...@@ -1191,7 +1215,9 @@ fil_space_free( ...@@ -1191,7 +1215,9 @@ fil_space_free(
ut_a(0 == UT_LIST_GET_LEN(space->chain)); ut_a(0 == UT_LIST_GET_LEN(space->chain));
mutex_exit(&(system->mutex)); if (x_latched) {
rw_lock_x_unlock(&space->latch);
}
rw_lock_free(&(space->latch)); rw_lock_free(&(space->latch));
...@@ -2048,6 +2074,19 @@ try_again: ...@@ -2048,6 +2074,19 @@ try_again:
path = mem_strdup(space->name); path = mem_strdup(space->name);
mutex_exit(&(system->mutex)); mutex_exit(&(system->mutex));
/* Important: We rely on the data dictionary mutex to ensure
that a race is not possible here. It should serialize the tablespace
drop/free. We acquire an X latch only to avoid a race condition
when accessing the tablespace instance via:
fsp_get_available_space_in_free_extents().
There our main motivation is to reduce the contention on the
dictionary mutex and not correctness. */
rw_lock_x_lock(&space->latch);
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
/* Invalidate in the buffer pool all pages belonging to the /* Invalidate in the buffer pool all pages belonging to the
tablespace. Since we have set space->is_being_deleted = TRUE, readahead tablespace. Since we have set space->is_being_deleted = TRUE, readahead
...@@ -2060,7 +2099,11 @@ try_again: ...@@ -2060,7 +2099,11 @@ try_again:
#endif #endif
/* printf("Deleting tablespace %s id %lu\n", space->name, id); */ /* printf("Deleting tablespace %s id %lu\n", space->name, id); */
success = fil_space_free(id); mutex_enter(&system->mutex);
success = fil_space_free(id, TRUE);
mutex_exit(&system->mutex);
if (success) { if (success) {
success = os_file_delete(path); success = os_file_delete(path);
...@@ -2068,6 +2111,8 @@ try_again: ...@@ -2068,6 +2111,8 @@ try_again:
if (!success) { if (!success) {
success = os_file_delete_if_exists(path); success = os_file_delete_if_exists(path);
} }
} else {
rw_lock_x_unlock(&space->latch);
} }
if (success) { if (success) {
...@@ -4569,3 +4614,28 @@ fil_page_get_type( ...@@ -4569,3 +4614,28 @@ fil_page_get_type(
return(mach_read_from_2(page + FIL_PAGE_TYPE)); return(mach_read_from_2(page + FIL_PAGE_TYPE));
} }
/***********************************************************************
Returns TRUE if a single-table tablespace is being deleted. */
ibool
fil_tablespace_is_being_deleted(
/*============================*/
/* out: TRUE if space is being deleted */
ulint id) /* in: space id */
{
fil_space_t* space;
ibool is_being_deleted;
mutex_enter(&fil_system->mutex);
HASH_SEARCH(hash, fil_system->spaces, id, space, space->id == id);
ut_a(space != NULL);
is_being_deleted = space->is_being_deleted;
mutex_exit(&fil_system->mutex);
return(is_being_deleted);
}
...@@ -2842,12 +2842,61 @@ fsp_get_available_space_in_free_extents( ...@@ -2842,12 +2842,61 @@ fsp_get_available_space_in_free_extents(
ut_ad(!mutex_own(&kernel_mutex)); ut_ad(!mutex_own(&kernel_mutex));
/* The convoluted mutex acquire is to overcome latching order
issues: The problem is that the fil_mutex is at a lower level
than the tablespace latch and the buffer pool mutex. We have to
first prevent any operations on the file system by acquiring the
dictionary mutex. Then acquire the tablespace latch to obey the
latching order and then release the dictionary mutex. That way we
ensure that the tablespace instance can't be freed while we are
examining its contents (see fil_space_free()).
However, there is one further complication, we release the fil_mutex
when we need to invalidate the the pages in the buffer pool and we
reacquire the fil_mutex when deleting and freeing the tablespace
instance in fil0fil.c. Here we need to account for that situation
too. */
dict_mutex_enter_for_mysql();
/* At this stage there is no guarantee that the tablespace even
exists in the cache. */
if (fil_tablespace_deleted_or_being_deleted_in_mem(space, -1)) {
dict_mutex_exit_for_mysql();
return(ULLINT_UNDEFINED);
}
mtr_start(&mtr); mtr_start(&mtr);
latch = fil_space_get_latch(space); latch = fil_space_get_latch(space);
/* This should ensure that the tablespace instance can't be freed
by another thread. However, the tablespace pages can still be freed
from the buffer pool. We need to check for that again. */
mtr_x_lock(latch, &mtr); mtr_x_lock(latch, &mtr);
dict_mutex_exit_for_mysql();
/* At this point it is possible for the tablespace to be deleted and
its pages removed from the buffer pool. We need to check for that
situation. However, the tablespace instance can't be deleted because
our latching above should ensure that. */
if (fil_tablespace_is_being_deleted(space)) {
mtr_commit(&mtr);
return(ULLINT_UNDEFINED);
}
/* From here on even if the user has dropped the tablespace, the
pages _must_ still exist in the buffer pool and the tablespace
instance _must be in the file system hash table. */
space_header = fsp_get_space_header(space, &mtr); space_header = fsp_get_space_header(space, &mtr);
size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, &mtr); size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, &mtr);
......
...@@ -6485,20 +6485,12 @@ ha_innobase::info( ...@@ -6485,20 +6485,12 @@ ha_innobase::info(
so the "old" value can remain. delete_length is initialized so the "old" value can remain. delete_length is initialized
to 0 in the ha_statistics' constructor. */ to 0 in the ha_statistics' constructor. */
if (!(flag & HA_STATUS_NO_LOCK)) { if (!(flag & HA_STATUS_NO_LOCK)) {
ullint avail_space;
/* lock the data dictionary to avoid races with avail_space = fsp_get_available_space_in_free_extents(
ibd_file_missing and tablespace_discarded */ ib_table->space);
row_mysql_lock_data_dictionary(prebuilt->trx);
/* ib_table->space must be an existent tablespace */
if (!ib_table->ibd_file_missing
&& !ib_table->tablespace_discarded) {
stats.delete_length =
fsp_get_available_space_in_free_extents(
ib_table->space) * 1024;
} else {
if (avail_space == ULLINT_UNDEFINED) {
THD* thd; THD* thd;
thd = ha_thd(); thd = ha_thd();
...@@ -6515,9 +6507,9 @@ ha_innobase::info( ...@@ -6515,9 +6507,9 @@ ha_innobase::info(
ib_table->name); ib_table->name);
stats.delete_length = 0; stats.delete_length = 0;
} else {
stats.delete_length = avail_space * 1024;
} }
row_mysql_unlock_data_dictionary(prebuilt->trx);
} }
stats.check_time = 0; stats.check_time = 0;
......
...@@ -202,8 +202,10 @@ the chain but does not delete them. */ ...@@ -202,8 +202,10 @@ the chain but does not delete them. */
ibool ibool
fil_space_free( fil_space_free(
/*===========*/ /*===========*/
/* out: TRUE if success */ /* out: TRUE if success */
ulint id); /* in: space id */ ulint id, /* in: space id */
ibool x_latched); /* in: TRUE if caller has space->latch
in X mode */
/*********************************************************************** /***********************************************************************
Returns the size of the space in pages. The tablespace must be cached in the Returns the size of the space in pages. The tablespace must be cached in the
memory cache. */ memory cache. */
...@@ -710,6 +712,14 @@ fil_page_get_type( ...@@ -710,6 +712,14 @@ fil_page_get_type(
written to page, the return value not defined */ written to page, the return value not defined */
byte* page); /* in: file page */ byte* page); /* in: file page */
/***********************************************************************
Returns TRUE if a single-table tablespace is being deleted. */
ibool
fil_tablespace_is_being_deleted(
/*============================*/
/* out: TRUE if space is being deleted */
ulint id); /* in: space id */
typedef struct fil_space_struct fil_space_t; typedef struct fil_space_struct fil_space_t;
......
...@@ -234,6 +234,12 @@ typedef unsigned long long int ullint; ...@@ -234,6 +234,12 @@ typedef unsigned long long int ullint;
/* Maximum value for a ulint */ /* Maximum value for a ulint */
#define ULINT_MAX ((ulint)(-2)) #define ULINT_MAX ((ulint)(-2))
/* THe 'undefined' value for ullint */
#define ULLINT_UNDEFINED ((ullint)(-1))
/* Maximum value for a ullint */
#define ULLINT_MAX ((ullint)(-2))
/* This 'ibool' type is used within Innobase. Remember that different included /* This 'ibool' type is used within Innobase. Remember that different included
headers may define 'bool' differently. Do not assume that 'bool' is a ulint! */ headers may define 'bool' differently. Do not assume that 'bool' is a ulint! */
#define ibool ulint #define ibool ulint
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment