mirror of
https://github.com/MariaDB/server.git
synced 2026-04-22 08:15:31 +02:00
Fix Bug#30423 "InnoDBs treatment of NULL in index stats causes bad
"rows examined" estimates". This change implements "innodb_stats_method"
with options of "nulls_equal", "nulls_unequal" and "null_ignored".
rb://553 approved by Marko
This commit is contained in:
parent
634fe86056
commit
9cd4d49840
25 changed files with 1157 additions and 77 deletions
|
|
@ -174,6 +174,25 @@ static char* internal_innobase_data_file_path = NULL;
|
|||
|
||||
static char* innodb_version_str = (char*) INNODB_VERSION_STR;
|
||||
|
||||
/** Possible values for system variable "innodb_stats_method". The values
|
||||
are defined the same as its corresponding MyISAM system variable
|
||||
"myisam_stats_method"(see "myisam_stats_method_names"), for better usability */
|
||||
static const char* innodb_stats_method_names[] = {
|
||||
"nulls_equal",
|
||||
"nulls_unequal",
|
||||
"nulls_ignored",
|
||||
NullS
|
||||
};
|
||||
|
||||
/** Used to define an enumerate type of the system variable innodb_stats_method.
|
||||
This is the same as "myisam_stats_method_typelib" */
|
||||
static TYPELIB innodb_stats_method_typelib = {
|
||||
array_elements(innodb_stats_method_names) - 1,
|
||||
"innodb_stats_method_typelib",
|
||||
innodb_stats_method_names,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* The following counter is used to convey information to InnoDB
|
||||
about server activity: in selects it is not sensible to call
|
||||
srv_active_wake_master_thread after each fetch or search, we only do
|
||||
|
|
@ -7507,6 +7526,65 @@ innobase_get_mysql_key_number_for_index(
|
|||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Calculate Record Per Key value. Need to exclude the NULL value if
|
||||
innodb_stats_method is set to "nulls_ignored"
|
||||
@return estimated record per key value */
|
||||
static
|
||||
ha_rows
|
||||
innodb_rec_per_key(
|
||||
/*===============*/
|
||||
dict_index_t* index, /*!< in: dict_index_t structure */
|
||||
ulint i, /*!< in: the column we are
|
||||
calculating rec per key */
|
||||
ha_rows records) /*!< in: estimated total records */
|
||||
{
|
||||
ha_rows rec_per_key;
|
||||
|
||||
ut_ad(i < dict_index_get_n_unique(index));
|
||||
|
||||
/* Note the stat_n_diff_key_vals[] stores the diff value with
|
||||
n-prefix indexing, so it is always stat_n_diff_key_vals[i + 1] */
|
||||
if (index->stat_n_diff_key_vals[i + 1] == 0) {
|
||||
|
||||
rec_per_key = records;
|
||||
} else if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED) {
|
||||
ib_int64_t num_null;
|
||||
|
||||
/* Number of rows with NULL value in this
|
||||
field */
|
||||
num_null = records - index->stat_n_non_null_key_vals[i];
|
||||
|
||||
/* In theory, index->stat_n_non_null_key_vals[i]
|
||||
should always be less than the number of records.
|
||||
Since this is statistics value, the value could
|
||||
have slight discrepancy. But we will make sure
|
||||
the number of null values is not a negative number. */
|
||||
num_null = (num_null < 0) ? 0 : num_null;
|
||||
|
||||
/* If the number of NULL values is the same as or
|
||||
large than that of the distinct values, we could
|
||||
consider that the table consists mostly of NULL value.
|
||||
Set rec_per_key to 1. */
|
||||
if (index->stat_n_diff_key_vals[i + 1] <= num_null) {
|
||||
rec_per_key = 1;
|
||||
} else {
|
||||
/* Need to exclude rows with NULL values from
|
||||
rec_per_key calculation */
|
||||
rec_per_key = (ha_rows)(
|
||||
(records - num_null)
|
||||
/ (index->stat_n_diff_key_vals[i + 1]
|
||||
- num_null));
|
||||
}
|
||||
} else {
|
||||
rec_per_key = (ha_rows)
|
||||
(records / index->stat_n_diff_key_vals[i + 1]);
|
||||
}
|
||||
|
||||
return(rec_per_key);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Returns statistics information of the table to the MySQL interpreter,
|
||||
in various fields of the handle object. */
|
||||
|
|
@ -7737,13 +7815,8 @@ ha_innobase::info_low(
|
|||
break;
|
||||
}
|
||||
|
||||
if (index->stat_n_diff_key_vals[j + 1] == 0) {
|
||||
|
||||
rec_per_key = stats.records;
|
||||
} else {
|
||||
rec_per_key = (ha_rows)(stats.records /
|
||||
index->stat_n_diff_key_vals[j + 1]);
|
||||
}
|
||||
rec_per_key = innodb_rec_per_key(
|
||||
index, j, stats.records);
|
||||
|
||||
/* Since MySQL seems to favor table scans
|
||||
too much over index searches, we pretend
|
||||
|
|
@ -10934,6 +11007,13 @@ static MYSQL_SYSVAR_STR(change_buffering, innobase_change_buffering,
|
|||
innodb_change_buffering_validate,
|
||||
innodb_change_buffering_update, "inserts");
|
||||
|
||||
static MYSQL_SYSVAR_ENUM(stats_method, srv_innodb_stats_method,
|
||||
PLUGIN_VAR_RQCMDARG,
|
||||
"Specifies how InnoDB index statistics collection code should "
|
||||
"treat NULLs. Possible values are NULLS_EQUAL (default), "
|
||||
"NULLS_UNEQUAL and NULLS_IGNORED",
|
||||
NULL, NULL, SRV_STATS_NULLS_EQUAL, &innodb_stats_method_typelib);
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
|
||||
static MYSQL_SYSVAR_UINT(change_buffering_debug, ibuf_debug,
|
||||
PLUGIN_VAR_RQCMDARG,
|
||||
|
|
@ -10988,6 +11068,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
|
|||
MYSQL_SYSVAR(stats_on_metadata),
|
||||
MYSQL_SYSVAR(stats_sample_pages),
|
||||
MYSQL_SYSVAR(adaptive_hash_index),
|
||||
MYSQL_SYSVAR(stats_method),
|
||||
MYSQL_SYSVAR(replication_delay),
|
||||
MYSQL_SYSVAR(status_file),
|
||||
MYSQL_SYSVAR(strict_mode),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue