mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-28327 InnoDB persistent statistics fail to update after bulk insert
- Background statistics thread should keep the table in the statistics queue itself when the table under bulk insert operation dict_stats_analyze_index(): Set the maximum value for index_stats_t if the table is in bulk operation dict_stats_update(), dict_stats_update_transient_for_index(), dict_stats_update_transient(): Returns DB_SUCCESS_LOCKED_REC if the table under bulk insert operation dict_stats_process_entry_from_recalc_pool(): Add the table back to recalc pool if the table under bulk insert operation
This commit is contained in:
@ -182,3 +182,14 @@ CREATE TABLE t (i INT) ENGINE=InnoDB PARTITION BY HASH (i) PARTITIONS 2;
|
||||
INSERT INTO t VALUES (0);
|
||||
INSERT INTO t VALUES (1),(0),(1);
|
||||
DROP TABLE t;
|
||||
#
|
||||
# MDEV-28327 InnoDB persistent statistics fail to update
|
||||
# after bulk insert
|
||||
#
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY)ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT * FROM seq_1_to_4096;
|
||||
# Wait till statistics update after bulk insert operation
|
||||
SELECT n_rows FROM mysql.innodb_table_stats WHERE TABLE_NAME="t1";
|
||||
n_rows
|
||||
4096
|
||||
DROP TABLE t1;
|
||||
|
@ -193,3 +193,16 @@ CREATE TABLE t (i INT) ENGINE=InnoDB PARTITION BY HASH (i) PARTITIONS 2;
|
||||
INSERT INTO t VALUES (0);
|
||||
INSERT INTO t VALUES (1),(0),(1);
|
||||
DROP TABLE t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-28327 InnoDB persistent statistics fail to update
|
||||
--echo # after bulk insert
|
||||
--echo #
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY)ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT * FROM seq_1_to_4096;
|
||||
--echo # Wait till statistics update after bulk insert operation
|
||||
let $wait_condition= select n_rows > 100 from mysql.innodb_table_stats
|
||||
where table_name="t1";
|
||||
source include/wait_condition.inc;
|
||||
SELECT n_rows FROM mysql.innodb_table_stats WHERE TABLE_NAME="t1";
|
||||
DROP TABLE t1;
|
||||
|
@ -1036,6 +1036,12 @@ struct index_field_stats_t
|
||||
n_non_null_key_vals(n_non_null_key_vals)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_bulk_operation() const
|
||||
{
|
||||
return n_diff_key_vals == UINT64_MAX &&
|
||||
n_sample_sizes == UINT64_MAX && n_non_null_key_vals == UINT64_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************//**
|
||||
@ -1377,13 +1383,16 @@ relatively quick and is used to calculate transient statistics that
|
||||
are not saved on disk. This was the only way to calculate statistics
|
||||
before the Persistent Statistics feature was introduced.
|
||||
This function doesn't update the defragmentation related stats.
|
||||
Only persistent statistics supports defragmentation stats. */
|
||||
Only persistent statistics supports defragmentation stats.
|
||||
@return error code
|
||||
@retval DB_SUCCESS_LOCKED_REC if the table under bulk insert operation */
|
||||
static
|
||||
void
|
||||
dberr_t
|
||||
dict_stats_update_transient_for_index(
|
||||
/*==================================*/
|
||||
dict_index_t* index) /*!< in/out: index */
|
||||
{
|
||||
dberr_t err = DB_SUCCESS;
|
||||
if (srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|
||||
&& (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO
|
||||
|| !dict_index_is_clust(index))) {
|
||||
@ -1397,6 +1406,7 @@ dummy_empty:
|
||||
index->table->stats_mutex_lock();
|
||||
dict_stats_empty_index(index, false);
|
||||
index->table->stats_mutex_unlock();
|
||||
return err;
|
||||
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
|
||||
} else if (ibuf_debug && !dict_index_is_clust(index)) {
|
||||
goto dummy_empty;
|
||||
@ -1420,6 +1430,7 @@ invalid:
|
||||
|
||||
const auto bulk_trx_id = index->table->bulk_trx_id;
|
||||
if (bulk_trx_id && trx_sys.find(nullptr, bulk_trx_id, false)) {
|
||||
err= DB_SUCCESS_LOCKED_REC;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
@ -1461,6 +1472,8 @@ invalid:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
@ -1468,9 +1481,11 @@ Calculates new estimates for table and index statistics. This function
|
||||
is relatively quick and is used to calculate transient statistics that
|
||||
are not saved on disk.
|
||||
This was the only way to calculate statistics before the
|
||||
Persistent Statistics feature was introduced. */
|
||||
Persistent Statistics feature was introduced.
|
||||
@return error code
|
||||
@retval DB_SUCCESS_LOCKED REC if the table under bulk insert operation */
|
||||
static
|
||||
void
|
||||
dberr_t
|
||||
dict_stats_update_transient(
|
||||
/*========================*/
|
||||
dict_table_t* table) /*!< in/out: table */
|
||||
@ -1479,6 +1494,7 @@ dict_stats_update_transient(
|
||||
|
||||
dict_index_t* index;
|
||||
ulint sum_of_index_sizes = 0;
|
||||
dberr_t err = DB_SUCCESS;
|
||||
|
||||
/* Find out the sizes of the indexes and how many different values
|
||||
for the key they approximately have */
|
||||
@ -1487,15 +1503,15 @@ dict_stats_update_transient(
|
||||
|
||||
if (!table->space) {
|
||||
/* Nothing to do. */
|
||||
empty_table:
|
||||
dict_stats_empty_table(table, true);
|
||||
return;
|
||||
return err;
|
||||
} else if (index == NULL) {
|
||||
/* Table definition is corrupt */
|
||||
|
||||
ib::warn() << "Table " << table->name
|
||||
<< " has no indexes. Cannot calculate statistics.";
|
||||
dict_stats_empty_table(table, true);
|
||||
return;
|
||||
goto empty_table;
|
||||
}
|
||||
|
||||
for (; index != NULL; index = dict_table_get_next_index(index)) {
|
||||
@ -1507,14 +1523,15 @@ dict_stats_update_transient(
|
||||
}
|
||||
|
||||
if (dict_stats_should_ignore_index(index)
|
||||
|| !index->is_readable()) {
|
||||
|| !index->is_readable()
|
||||
|| err == DB_SUCCESS_LOCKED_REC) {
|
||||
index->table->stats_mutex_lock();
|
||||
dict_stats_empty_index(index, false);
|
||||
index->table->stats_mutex_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
dict_stats_update_transient_for_index(index);
|
||||
err = dict_stats_update_transient_for_index(index);
|
||||
|
||||
sum_of_index_sizes += index->stat_index_size;
|
||||
}
|
||||
@ -1538,6 +1555,8 @@ dict_stats_update_transient(
|
||||
table->stat_initialized = TRUE;
|
||||
|
||||
table->stats_mutex_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* @{ Pseudo code about the relation between the following functions
|
||||
@ -2420,6 +2439,19 @@ struct index_stats_t
|
||||
for (ulint i= 0; i < n_uniq; ++i)
|
||||
stats.push_back(index_field_stats_t{0, 1, 0});
|
||||
}
|
||||
|
||||
void set_bulk_operation()
|
||||
{
|
||||
memset((void*) &stats[0], 0xff, stats.size() * sizeof stats[0]);
|
||||
}
|
||||
|
||||
bool is_bulk_operation() const
|
||||
{
|
||||
for (auto &s : stats)
|
||||
if (!s.is_bulk_operation())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Set dict_index_t::stat_n_diff_key_vals[] and stat_n_sample_sizes[].
|
||||
@ -2549,8 +2581,7 @@ empty_index:
|
||||
|
||||
const auto bulk_trx_id = index->table->bulk_trx_id;
|
||||
if (bulk_trx_id && trx_sys.find(nullptr, bulk_trx_id, false)) {
|
||||
result.index_size = 1;
|
||||
result.n_leaf_pages = 1;
|
||||
result.set_bulk_operation();
|
||||
goto empty_index;
|
||||
}
|
||||
|
||||
@ -2812,7 +2843,8 @@ found_level:
|
||||
Calculates new estimates for table and index statistics. This function
|
||||
is relatively slow and is used to calculate persistent statistics that
|
||||
will be saved on disk.
|
||||
@return DB_SUCCESS or error code */
|
||||
@return DB_SUCCESS or error code
|
||||
@retval DB_SUCCESS_LOCKED_REC if the table under bulk insert operation */
|
||||
static
|
||||
dberr_t
|
||||
dict_stats_update_persistent(
|
||||
@ -2846,6 +2878,10 @@ dict_stats_update_persistent(
|
||||
|
||||
index_stats_t stats = dict_stats_analyze_index(index);
|
||||
|
||||
if (stats.is_bulk_operation()) {
|
||||
return DB_SUCCESS_LOCKED_REC;
|
||||
}
|
||||
|
||||
table->stats_mutex_lock();
|
||||
index->stat_index_size = stats.index_size;
|
||||
index->stat_n_leaf_pages = stats.n_leaf_pages;
|
||||
@ -3841,7 +3877,8 @@ dict_stats_update_for_index(
|
||||
/*********************************************************************//**
|
||||
Calculates new estimates for table and index statistics. The statistics
|
||||
are used in query optimization.
|
||||
@return DB_SUCCESS or error code */
|
||||
@return DB_SUCCESS or error code
|
||||
@retval DB_SUCCESS_LOCKED_REC if the table under bulk insert operation */
|
||||
dberr_t
|
||||
dict_stats_update(
|
||||
/*==============*/
|
||||
@ -4054,9 +4091,7 @@ dict_stats_update(
|
||||
}
|
||||
|
||||
transient:
|
||||
dict_stats_update_transient(table);
|
||||
|
||||
return(DB_SUCCESS);
|
||||
return dict_stats_update_transient(table);
|
||||
}
|
||||
|
||||
/** Execute DELETE FROM mysql.innodb_table_stats
|
||||
|
@ -339,8 +339,9 @@ invalid_table_id:
|
||||
const bool update_now=
|
||||
difftime(time(nullptr), table->stats_last_recalc) >= MIN_RECALC_INTERVAL;
|
||||
|
||||
if (update_now)
|
||||
dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
|
||||
const dberr_t err= update_now
|
||||
? dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT)
|
||||
: DB_SUCCESS_LOCKED_REC;
|
||||
|
||||
dict_table_close(table, false, thd, mdl);
|
||||
|
||||
@ -361,7 +362,7 @@ done:
|
||||
ut_ad(i->state == recalc::IN_PROGRESS);
|
||||
recalc_pool.erase(i);
|
||||
const bool reschedule= !update_now && recalc_pool.empty();
|
||||
if (!update_now)
|
||||
if (err == DB_SUCCESS_LOCKED_REC)
|
||||
recalc_pool.emplace_back(recalc{table_id, recalc::IDLE});
|
||||
mysql_mutex_unlock(&recalc_pool_mutex);
|
||||
if (reschedule)
|
||||
|
Reference in New Issue
Block a user