summaryrefslogtreecommitdiff
path: root/storage/innobase/dict/dict0stats.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/dict/dict0stats.cc')
-rw-r--r--storage/innobase/dict/dict0stats.cc311
1 files changed, 173 insertions, 138 deletions
diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc
index 8bf02f9785c..928bdb3f2ef 100644
--- a/storage/innobase/dict/dict0stats.cc
+++ b/storage/innobase/dict/dict0stats.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 2009, 2013, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2009, 2014, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -46,6 +46,7 @@ Created Jan 06, 2010 Vasil Dimov
#include "ut0rnd.h" /* ut_rnd_interval() */
#include "ut0ut.h" /* ut_format_name(), ut_time() */
+#include <map>
#include <vector>
/* Sampling algorithm description @{
@@ -143,6 +144,17 @@ data: b,b,b,b,b,b,g,g,j,j,j, x, y
then we would store 5,7,10,11,12 in the array. */
typedef std::vector<ib_uint64_t> boundaries_t;
+/* This is used to arrange the index based on the index name.
+@return true if index_name1 is smaller than index_name2. */
+struct index_cmp
+{
+ bool operator()(const char* index_name1, const char* index_name2) const {
+ return(strcmp(index_name1, index_name2) < 0);
+ }
+};
+
+typedef std::map<const char*, dict_index_t*, index_cmp> index_map_t;
+
/*********************************************************************//**
Checks whether an index should be ignored in stats manipulations:
* stats fetch
@@ -266,22 +278,24 @@ dict_stats_persistent_storage_check(
return(true);
}
-/*********************************************************************//**
-Executes a given SQL statement using the InnoDB internal SQL parser
-in its own transaction and commits it.
+/** Executes a given SQL statement using the InnoDB internal SQL parser.
This function will free the pinfo object.
+@param[in,out] pinfo pinfo to pass to que_eval_sql() must already
+have any literals bound to it
+@param[in] sql SQL string to execute
+@param[in,out] trx in case of NULL the function will allocate and
+free the trx object. If it is not NULL then it will be rolled back
+only in the case of error, but not freed.
@return DB_SUCCESS or error code */
static
dberr_t
dict_stats_exec_sql(
-/*================*/
- pars_info_t* pinfo, /*!< in/out: pinfo to pass to que_eval_sql()
- must already have any literals bound to it */
- const char* sql) /*!< in: SQL string to execute */
+ pars_info_t* pinfo,
+ const char* sql,
+ trx_t* trx)
{
- trx_t* trx;
dberr_t err;
-
+ bool trx_started = false;
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
@@ -292,11 +306,24 @@ dict_stats_exec_sql(
return(DB_STATS_DO_NOT_EXIST);
}
- trx = trx_allocate_for_background();
- trx_start_if_not_started(trx);
+ if (trx == NULL) {
+ trx = trx_allocate_for_background();
+ trx_start_if_not_started(trx);
+ trx_started = true;
+ }
err = que_eval_sql(pinfo, sql, FALSE, trx); /* pinfo is freed here */
+ DBUG_EXECUTE_IF("stats_index_error",
+ if (!trx_started) {
+ err = DB_STATS_DO_NOT_EXIST;
+ trx->error_state = DB_STATS_DO_NOT_EXIST;
+ });
+
+ if (!trx_started && err == DB_SUCCESS) {
+ return(DB_SUCCESS);
+ }
+
if (err == DB_SUCCESS) {
trx_commit_for_mysql(trx);
} else {
@@ -308,7 +335,9 @@ dict_stats_exec_sql(
ut_a(trx->error_state == DB_SUCCESS);
}
- trx_free_for_background(trx);
+ if (trx_started) {
+ trx_free_for_background(trx);
+ }
return(err);
}
@@ -400,6 +429,11 @@ dict_stats_table_clone_create(
t->corrupted = table->corrupted;
+ /* This private object "t" is not shared with other threads, so
+ we do not need the stats_latch. The lock/unlock routines will do
+ nothing if stats_latch is NULL. */
+ t->stats_latch = NULL;
+
UT_LIST_INIT(t->indexes);
for (index = dict_table_get_first_index(table);
@@ -731,7 +765,7 @@ static
dict_table_t*
dict_stats_snapshot_create(
/*=======================*/
- const dict_table_t* table) /*!< in: table whose stats to copy */
+ dict_table_t* table) /*!< in: table whose stats to copy */
{
mutex_enter(&dict_sys->mutex);
@@ -1583,7 +1617,8 @@ dict_stats_analyze_index_for_n_prefix(
== !(REC_INFO_MIN_REC_FLAG & rec_get_info_bits(
btr_pcur_get_rec(&pcur), page_is_comp(page))));
- last_idx_on_level = boundaries->at(n_diff_for_this_prefix - 1);
+ last_idx_on_level = boundaries->at(
+ static_cast<unsigned int>(n_diff_for_this_prefix - 1));
rec_idx = 0;
@@ -1595,7 +1630,7 @@ dict_stats_analyze_index_for_n_prefix(
for (i = 0; i < n_recs_to_dive_below; i++) {
ib_uint64_t left;
ib_uint64_t right;
- ulint rnd;
+ ib_uint64_t rnd;
ib_uint64_t dive_below_idx;
/* there are n_diff_for_this_prefix elements
@@ -1636,9 +1671,11 @@ dict_stats_analyze_index_for_n_prefix(
/* we do not pass (left, right) because we do not want to ask
ut_rnd_interval() to work with too big numbers since
ib_uint64_t could be bigger than ulint */
- rnd = ut_rnd_interval(0, (ulint) (right - left));
+ rnd = static_cast<ib_uint64_t>(
+ ut_rnd_interval(0, static_cast<ulint>(right - left)));
- dive_below_idx = boundaries->at(left + rnd);
+ dive_below_idx = boundaries->at(
+ static_cast<unsigned int>(left + rnd));
#if 0
DEBUG_PRINTF(" %s(): dive below record with index="
@@ -2079,20 +2116,28 @@ dict_stats_update_persistent(
}
#include "mysql_com.h"
-/*********************************************************************//**
-Save an individual index's statistic into the persistent statistics
+/** Save an individual index's statistic into the persistent statistics
storage.
+@param[in] index index to be updated
+@param[in] last_update timestamp of the stat
+@param[in] stat_name name of the stat
+@param[in] stat_value value of the stat
+@param[in] sample_size n pages sampled or NULL
+@param[in] stat_description description of the stat
+@param[in,out] trx in case of NULL the function will
+allocate and free the trx object. If it is not NULL then it will be
+rolled back only in the case of error, but not freed.
@return DB_SUCCESS or error code */
static
dberr_t
dict_stats_save_index_stat(
-/*=======================*/
- dict_index_t* index, /*!< in: index */
- lint last_update, /*!< in: timestamp of the stat */
- const char* stat_name, /*!< in: name of the stat */
- ib_uint64_t stat_value, /*!< in: value of the stat */
- ib_uint64_t* sample_size, /*!< in: n pages sampled or NULL */
- const char* stat_description)/*!< in: description of the stat */
+ dict_index_t* index,
+ lint last_update,
+ const char* stat_name,
+ ib_uint64_t stat_value,
+ ib_uint64_t* sample_size,
+ const char* stat_description,
+ trx_t* trx)
{
pars_info_t* pinfo;
dberr_t ret;
@@ -2131,8 +2176,16 @@ dict_stats_save_index_stat(
ret = dict_stats_exec_sql(
pinfo,
- "PROCEDURE INDEX_STATS_SAVE_INSERT () IS\n"
+ "PROCEDURE INDEX_STATS_SAVE () IS\n"
"BEGIN\n"
+
+ "DELETE FROM \"" INDEX_STATS_NAME "\"\n"
+ "WHERE\n"
+ "database_name = :database_name AND\n"
+ "table_name = :table_name AND\n"
+ "index_name = :index_name AND\n"
+ "stat_name = :stat_name;\n"
+
"INSERT INTO \"" INDEX_STATS_NAME "\"\n"
"VALUES\n"
"(\n"
@@ -2145,48 +2198,7 @@ dict_stats_save_index_stat(
":sample_size,\n"
":stat_description\n"
");\n"
- "END;");
-
- if (ret == DB_DUPLICATE_KEY) {
-
- pinfo = pars_info_create();
- pars_info_add_str_literal(pinfo, "database_name", db_utf8);
- pars_info_add_str_literal(pinfo, "table_name", table_utf8);
- UNIV_MEM_ASSERT_RW_ABORT(index->name, strlen(index->name));
- pars_info_add_str_literal(pinfo, "index_name", index->name);
- UNIV_MEM_ASSERT_RW_ABORT(&last_update, 4);
- pars_info_add_int4_literal(pinfo, "last_update", last_update);
- UNIV_MEM_ASSERT_RW_ABORT(stat_name, strlen(stat_name));
- pars_info_add_str_literal(pinfo, "stat_name", stat_name);
- UNIV_MEM_ASSERT_RW_ABORT(&stat_value, 8);
- pars_info_add_ull_literal(pinfo, "stat_value", stat_value);
- if (sample_size != NULL) {
- UNIV_MEM_ASSERT_RW_ABORT(sample_size, 8);
- pars_info_add_ull_literal(pinfo, "sample_size", *sample_size);
- } else {
- pars_info_add_literal(pinfo, "sample_size", NULL,
- UNIV_SQL_NULL, DATA_FIXBINARY, 0);
- }
- UNIV_MEM_ASSERT_RW_ABORT(stat_description, strlen(stat_description));
- pars_info_add_str_literal(pinfo, "stat_description",
- stat_description);
-
- ret = dict_stats_exec_sql(
- pinfo,
- "PROCEDURE INDEX_STATS_SAVE_UPDATE () IS\n"
- "BEGIN\n"
- "UPDATE \"" INDEX_STATS_NAME "\" SET\n"
- "last_update = :last_update,\n"
- "stat_value = :stat_value,\n"
- "sample_size = :sample_size,\n"
- "stat_description = :stat_description\n"
- "WHERE\n"
- "database_name = :database_name AND\n"
- "table_name = :table_name AND\n"
- "index_name = :index_name AND\n"
- "stat_name = :stat_name;\n"
- "END;");
- }
+ "END;", trx);
if (ret != DB_SUCCESS) {
char buf_table[MAX_FULL_NAME_LEN];
@@ -2205,14 +2217,18 @@ dict_stats_save_index_stat(
return(ret);
}
-/*********************************************************************//**
-Save the table's statistics into the persistent statistics storage.
+/** Save the table's statistics into the persistent statistics storage.
+@param[in] table_orig table whose stats to save
+@param[in] only_for_index if this is non-NULL, then stats for indexes
+that are not equal to it will not be saved, if NULL, then all
+indexes' stats are saved
@return DB_SUCCESS or error code */
static
dberr_t
dict_stats_save(
/*============*/
- dict_table_t* table_orig) /*!< in: table */
+ dict_table_t* table_orig,
+ const index_id_t* only_for_index)
{
pars_info_t* pinfo;
lint now;
@@ -2234,26 +2250,27 @@ dict_stats_save(
lint */
now = (lint) ut_time();
-#define PREPARE_PINFO_FOR_TABLE_SAVE(p, t, n) \
- do { \
- pars_info_add_str_literal((p), "database_name", db_utf8); \
- pars_info_add_str_literal((p), "table_name", table_utf8); \
- pars_info_add_int4_literal((p), "last_update", (n)); \
- pars_info_add_ull_literal((p), "n_rows", (t)->stat_n_rows); \
- pars_info_add_ull_literal((p), "clustered_index_size", \
- (t)->stat_clustered_index_size); \
- pars_info_add_ull_literal((p), "sum_of_other_index_sizes", \
- (t)->stat_sum_of_other_index_sizes); \
- } while(false);
-
pinfo = pars_info_create();
- PREPARE_PINFO_FOR_TABLE_SAVE(pinfo, table, now);
+ pars_info_add_str_literal(pinfo, "database_name", db_utf8);
+ pars_info_add_str_literal(pinfo, "table_name", table_utf8);
+ pars_info_add_int4_literal(pinfo, "last_update", now);
+ pars_info_add_ull_literal(pinfo, "n_rows", table->stat_n_rows);
+ pars_info_add_ull_literal(pinfo, "clustered_index_size",
+ table->stat_clustered_index_size);
+ pars_info_add_ull_literal(pinfo, "sum_of_other_index_sizes",
+ table->stat_sum_of_other_index_sizes);
ret = dict_stats_exec_sql(
pinfo,
- "PROCEDURE TABLE_STATS_SAVE_INSERT () IS\n"
+ "PROCEDURE TABLE_STATS_SAVE () IS\n"
"BEGIN\n"
+
+ "DELETE FROM \"" TABLE_STATS_NAME "\"\n"
+ "WHERE\n"
+ "database_name = :database_name AND\n"
+ "table_name = :table_name;\n"
+
"INSERT INTO \"" TABLE_STATS_NAME "\"\n"
"VALUES\n"
"(\n"
@@ -2264,28 +2281,7 @@ dict_stats_save(
":clustered_index_size,\n"
":sum_of_other_index_sizes\n"
");\n"
- "END;");
-
- if (ret == DB_DUPLICATE_KEY) {
- pinfo = pars_info_create();
-
- PREPARE_PINFO_FOR_TABLE_SAVE(pinfo, table, now);
-
- ret = dict_stats_exec_sql(
- pinfo,
- "PROCEDURE TABLE_STATS_SAVE_UPDATE () IS\n"
- "BEGIN\n"
- "UPDATE \"" TABLE_STATS_NAME "\" SET\n"
- "last_update = :last_update,\n"
- "n_rows = :n_rows,\n"
- "clustered_index_size = :clustered_index_size,\n"
- "sum_of_other_index_sizes = "
- " :sum_of_other_index_sizes\n"
- "WHERE\n"
- "database_name = :database_name AND\n"
- "table_name = :table_name;\n"
- "END;");
- }
+ "END;", NULL);
if (ret != DB_SUCCESS) {
char buf[MAX_FULL_NAME_LEN];
@@ -2295,39 +2291,56 @@ dict_stats_save(
"%s: %s\n",
ut_format_name(table->name, TRUE, buf, sizeof(buf)),
ut_strerr(ret));
- goto end;
+
+ mutex_exit(&dict_sys->mutex);
+ rw_lock_x_unlock(&dict_operation_lock);
+
+ dict_stats_snapshot_free(table);
+
+ return(ret);
}
+ trx_t* trx = trx_allocate_for_background();
+ trx_start_if_not_started(trx);
+
dict_index_t* index;
+ index_map_t indexes;
+
+ /* Below we do all the modifications in innodb_index_stats in a single
+ transaction for performance reasons. Modifying more than one row in a
+ single transaction may deadlock with other transactions if they
+ lock the rows in different order. Other transaction could be for
+ example when we DROP a table and do
+ DELETE FROM innodb_index_stats WHERE database_name = '...'
+ AND table_name = '...'; which will affect more than one row. To
+ prevent deadlocks we always lock the rows in the same order - the
+ order of the PK, which is (database_name, table_name, index_name,
+ stat_name). This is why below we sort the indexes by name and then
+ for each index, do the mods ordered by stat_name. */
for (index = dict_table_get_first_index(table);
index != NULL;
index = dict_table_get_next_index(index)) {
- if (dict_stats_should_ignore_index(index)) {
- continue;
- }
+ indexes[index->name] = index;
+ }
- ut_ad(!dict_index_is_univ(index));
+ index_map_t::const_iterator it;
- ret = dict_stats_save_index_stat(index, now, "size",
- index->stat_index_size,
- NULL,
- "Number of pages "
- "in the index");
- if (ret != DB_SUCCESS) {
- goto end;
+ for (it = indexes.begin(); it != indexes.end(); ++it) {
+
+ index = it->second;
+
+ if (only_for_index != NULL && index->id != *only_for_index) {
+ continue;
}
- ret = dict_stats_save_index_stat(index, now, "n_leaf_pages",
- index->stat_n_leaf_pages,
- NULL,
- "Number of leaf pages "
- "in the index");
- if (ret != DB_SUCCESS) {
- goto end;
+ if (dict_stats_should_ignore_index(index)) {
+ continue;
}
+ ut_ad(!dict_index_is_univ(index));
+
for (ulint i = 0; i < index->n_uniq; i++) {
char stat_name[16];
@@ -2355,15 +2368,37 @@ dict_stats_save(
index, now, stat_name,
index->stat_n_diff_key_vals[i],
&index->stat_n_sample_sizes[i],
- stat_description);
+ stat_description, trx);
if (ret != DB_SUCCESS) {
goto end;
}
}
+
+ ret = dict_stats_save_index_stat(index, now, "n_leaf_pages",
+ index->stat_n_leaf_pages,
+ NULL,
+ "Number of leaf pages "
+ "in the index", trx);
+ if (ret != DB_SUCCESS) {
+ goto end;
+ }
+
+ ret = dict_stats_save_index_stat(index, now, "size",
+ index->stat_index_size,
+ NULL,
+ "Number of pages "
+ "in the index", trx);
+ if (ret != DB_SUCCESS) {
+ goto end;
+ }
}
+ trx_commit_for_mysql(trx);
+
end:
+ trx_free_for_background(trx);
+
mutex_exit(&dict_sys->mutex);
rw_lock_x_unlock(&dict_operation_lock);
@@ -2860,7 +2895,7 @@ dict_stats_update_for_index(
dict_table_stats_lock(index->table, RW_X_LATCH);
dict_stats_analyze_index(index);
dict_table_stats_unlock(index->table, RW_X_LATCH);
- dict_stats_save(index->table);
+ dict_stats_save(index->table, &index->id);
DBUG_VOID_RETURN;
}
/* else */
@@ -2955,7 +2990,7 @@ dict_stats_update(
return(err);
}
- err = dict_stats_save(table);
+ err = dict_stats_save(table, NULL);
return(err);
}
@@ -2988,7 +3023,7 @@ dict_stats_update(
if (dict_stats_persistent_storage_check(false)) {
- return(dict_stats_save(table));
+ return(dict_stats_save(table, NULL));
}
return(DB_STATS_DO_NOT_EXIST);
@@ -3178,7 +3213,7 @@ dict_stats_drop_index(
"database_name = :database_name AND\n"
"table_name = :table_name AND\n"
"index_name = :index_name;\n"
- "END;\n");
+ "END;\n", NULL);
mutex_exit(&dict_sys->mutex);
rw_lock_x_unlock(&dict_operation_lock);
@@ -3246,7 +3281,7 @@ dict_stats_delete_from_table_stats(
"DELETE FROM \"" TABLE_STATS_NAME "\" WHERE\n"
"database_name = :database_name AND\n"
"table_name = :table_name;\n"
- "END;\n");
+ "END;\n", NULL);
return(ret);
}
@@ -3284,7 +3319,7 @@ dict_stats_delete_from_index_stats(
"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
"database_name = :database_name AND\n"
"table_name = :table_name;\n"
- "END;\n");
+ "END;\n", NULL);
return(ret);
}
@@ -3407,7 +3442,7 @@ dict_stats_rename_in_table_stats(
"WHERE\n"
"database_name = :old_dbname_utf8 AND\n"
"table_name = :old_tablename_utf8;\n"
- "END;\n");
+ "END;\n", NULL);
return(ret);
}
@@ -3453,7 +3488,7 @@ dict_stats_rename_in_index_stats(
"WHERE\n"
"database_name = :old_dbname_utf8 AND\n"
"table_name = :old_tablename_utf8;\n"
- "END;\n");
+ "END;\n", NULL);
return(ret);
}
@@ -3834,7 +3869,7 @@ test_dict_stats_save()
index2_stat_n_sample_sizes[2] = TEST_IDX2_N_DIFF3_SAMPLE_SIZE;
index2_stat_n_sample_sizes[3] = TEST_IDX2_N_DIFF4_SAMPLE_SIZE;
- ret = dict_stats_save(&table);
+ ret = dict_stats_save(&table, NULL);
ut_a(ret == DB_SUCCESS);