Commit 1ab049e5 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18878 Purge: Optimize away futile table lookups

If a table has been dropped, rebuilt, or its tablespace has been
discarded or the table is corrupted, it does not make sense to
look up that table again while purging old undo log records.

purge_node_t::purge_node_t(): Replaces row_purge_node_create().

que_common_t::que_common_t(): Constructor.

row_import_update_index_root(): Remove the constant parameter
dict_locked=true, and update the table->def_trx_id in the cache.

purge_node_t::unavailable_table_id: The latest unavailable table ID,
to avoid future lookups.

purge_node_t::def_trx_id: The latest modification of the table
identified by unavailable_table_id, or TRX_ID_MAX.

purge_node_t::is_skipped(): Determine if a table should be skipped.

purge_node_t::skip(): Note that a table should be skipped.
parent 3ea49d35
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2018, MariaDB Corporation.
Copyright (c) 2017, 2019, MariaDB Corporation.
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
......@@ -460,39 +460,6 @@ struct que_fork_t{
/* Flag which is ORed to control structure statement node types */
#define QUE_NODE_CONTROL_STAT 1024
/* Query graph node types */
#define QUE_NODE_LOCK 1
#define QUE_NODE_INSERT 2
#define QUE_NODE_UPDATE 4
#define QUE_NODE_CURSOR 5
#define QUE_NODE_SELECT 6
#define QUE_NODE_AGGREGATE 7
#define QUE_NODE_FORK 8
#define QUE_NODE_THR 9
#define QUE_NODE_UNDO 10
#define QUE_NODE_COMMIT 11
#define QUE_NODE_ROLLBACK 12
#define QUE_NODE_PURGE 13
#define QUE_NODE_CREATE_TABLE 14
#define QUE_NODE_CREATE_INDEX 15
#define QUE_NODE_SYMBOL 16
#define QUE_NODE_RES_WORD 17
#define QUE_NODE_FUNC 18
#define QUE_NODE_ORDER 19
#define QUE_NODE_PROC (20 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_IF (21 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_WHILE (22 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_ASSIGNMENT 23
#define QUE_NODE_FETCH 24
#define QUE_NODE_OPEN 25
#define QUE_NODE_COL_ASSIGNMENT 26
#define QUE_NODE_FOR (27 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_RETURN 28
#define QUE_NODE_ROW_PRINTF 29
#define QUE_NODE_ELSIF 30
#define QUE_NODE_CALL 31
#define QUE_NODE_EXIT 32
#include "que0que.ic"
#endif
/*****************************************************************************
Copyright (c) 1996, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
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
......@@ -36,6 +37,39 @@ typedef struct que_fork_t que_t;
struct que_thr_t;
/* Query graph node types */
#define QUE_NODE_LOCK 1
#define QUE_NODE_INSERT 2
#define QUE_NODE_UPDATE 4
#define QUE_NODE_CURSOR 5
#define QUE_NODE_SELECT 6
#define QUE_NODE_AGGREGATE 7
#define QUE_NODE_FORK 8
#define QUE_NODE_THR 9
#define QUE_NODE_UNDO 10
#define QUE_NODE_COMMIT 11
#define QUE_NODE_ROLLBACK 12
#define QUE_NODE_PURGE 13
#define QUE_NODE_CREATE_TABLE 14
#define QUE_NODE_CREATE_INDEX 15
#define QUE_NODE_SYMBOL 16
#define QUE_NODE_RES_WORD 17
#define QUE_NODE_FUNC 18
#define QUE_NODE_ORDER 19
#define QUE_NODE_PROC (20 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_IF (21 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_WHILE (22 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_ASSIGNMENT 23
#define QUE_NODE_FETCH 24
#define QUE_NODE_OPEN 25
#define QUE_NODE_COL_ASSIGNMENT 26
#define QUE_NODE_FOR (27 + QUE_NODE_CONTROL_STAT)
#define QUE_NODE_RETURN 28
#define QUE_NODE_ROW_PRINTF 29
#define QUE_NODE_ELSIF 30
#define QUE_NODE_CALL 31
#define QUE_NODE_EXIT 32
/* Common struct at the beginning of each query graph node; the name of this
substruct must be 'common' */
......@@ -51,6 +85,11 @@ struct que_common_t{
symbol node or a function node, then we
have to free the data field in val
explicitly */
/** Constructor */
que_common_t(ulint type, que_node_t* parent)
: type(type), parent(parent), brother(), val(), val_buf_size()
{}
};
#endif
/*****************************************************************************
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2019, MariaDB Corporation.
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
......@@ -63,22 +63,13 @@ row_import_update_discarded_flag(
dict_sys_t:: mutex. */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*****************************************************************//**
Update the (space, root page) of a table's indexes from the values
in the data dictionary.
/** Update the root page numbers and tablespace ID of a table.
@param[in,out] trx dictionary transaction
@param[in,out] table persistent table
@param[in] reset whether to reset the fields to FIL_NULL
@return DB_SUCCESS or error code */
dberr_t
row_import_update_index_root(
/*=========================*/
trx_t* trx, /*!< in/out: transaction that
covers the update */
const dict_table_t* table, /*!< in: Table for which we want
to set the root page_no */
bool reset, /*!< in: if true then set to
FIL_NUL */
bool dict_locked) /*!< in: Set to true if the
caller already owns the
dict_sys_t:: mutex. */
row_import_update_index_root(trx_t* trx, dict_table_t* table, bool reset)
MY_ATTRIBUTE((nonnull, warn_unused_result));
#endif /* row0import_h */
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2019, MariaDB Corporation.
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
......@@ -35,16 +35,6 @@ Created 3/14/1997 Heikki Tuuri
#include "ut0vec.h"
#include "row0mysql.h"
/** Create a purge node to a query graph.
@param[in] parent parent node, i.e., a thr node
@param[in] heap memory heap where created
@return own: purge node */
purge_node_t*
row_purge_node_create(
que_thr_t* parent,
mem_heap_t* heap)
MY_ATTRIBUTE((warn_unused_result));
/** Determines if it is possible to remove a secondary index entry.
Removal is possible if the secondary index entry does not refer to any
not delete marked version of a clustered index record where DB_TRX_ID
......@@ -102,6 +92,13 @@ struct purge_node_t{
ulint rec_type;/*!< undo log record type: TRX_UNDO_INSERT_REC,
... */
private:
/** latest unavailable table ID (do not bother looking up again) */
table_id_t unavailable_table_id;
/** the latest modification of the table definition identified by
unavailable_table_id, or TRX_ID_MAX */
trx_id_t def_trx_id;
public:
dict_table_t* table; /*!< table where purge is done */
ulint cmpl_info;/* compiler analysis info of an update */
......@@ -131,6 +128,12 @@ struct purge_node_t{
It resets after processing each undo log record. */
purge_vcol_info_t vcol_info;
/** Constructor */
explicit purge_node_t(que_thr_t* parent) :
common(QUE_NODE_PURGE, parent), heap(mem_heap_create(256)),
done(TRUE)
{}
#ifdef UNIV_DEBUG
/***********************************************************//**
Validate the persisent cursor. The purge node has two references
......@@ -146,6 +149,24 @@ struct purge_node_t{
computation.
@return true if the table failed to open. */
bool vcol_op_failed() const { return !vcol_info.validate(); }
/** Determine if a table should be skipped in purge.
@param[in] table_id table identifier
@return whether to skip the table lookup and processing */
bool is_skipped(table_id_t id) const
{
return id == unavailable_table_id && trx_id <= def_trx_id;
}
/** Remember that a table should be skipped in purge.
@param[in] id table identifier
@param[in] limit last transaction for which to skip */
void skip(table_id_t id, trx_id_t limit)
{
DBUG_ASSERT(limit >= trx_id || !srv_safe_truncate);
unavailable_table_id = id;
def_trx_id = limit;
}
};
#endif
/*****************************************************************************
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2018, MariaDB Corporation.
Copyright (c) 2015, 2019, MariaDB Corporation.
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
......@@ -3064,23 +3064,13 @@ row_import_read_cfg(
return(err);
}
/*****************************************************************//**
Update the <space, root page> of a table's indexes from the values
in the data dictionary.
/** Update the root page numbers and tablespace ID of a table.
@param[in,out] trx dictionary transaction
@param[in,out] table persistent table
@param[in] reset whether to reset the fields to FIL_NULL
@return DB_SUCCESS or error code */
dberr_t
row_import_update_index_root(
/*=========================*/
trx_t* trx, /*!< in/out: transaction that
covers the update */
const dict_table_t* table, /*!< in: Table for which we want
to set the root page_no */
bool reset, /*!< in: if true then set to
FIL_NUL */
bool dict_locked) /*!< in: Set to true if the
caller already owns the
dict_sys_t:: mutex. */
row_import_update_index_root(trx_t* trx, dict_table_t* table, bool reset)
{
const dict_index_t* index;
que_t* graph = 0;
......@@ -3096,9 +3086,7 @@ row_import_update_index_root(
"WHERE TABLE_ID = :table_id AND ID = :index_id;\n"
"END;\n"};
if (!dict_locked) {
mutex_enter(&dict_sys->mutex);
}
table->def_trx_id = trx->id;
for (index = dict_table_get_first_index(table);
index != 0;
......@@ -3173,10 +3161,6 @@ row_import_update_index_root(
que_graph_free(graph);
if (!dict_locked) {
mutex_exit(&dict_sys->mutex);
}
return(err);
}
......@@ -4118,7 +4102,7 @@ row_import_for_mysql(
row_mysql_lock_data_dictionary(trx);
/* Update the root pages of the table's indexes. */
err = row_import_update_index_root(trx, table, false, true);
err = row_import_update_index_root(trx, table, false);
if (err != DB_SUCCESS) {
return(row_import_error(prebuilt, trx, err));
......
......@@ -2940,7 +2940,7 @@ row_discard_tablespace(
}
/* Update the index root pages in the system tables, on disk */
err = row_import_update_index_root(trx, table, true, true);
err = row_import_update_index_root(trx, table, true);
if (err != DB_SUCCESS) {
return(err);
......
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2018, MariaDB Corporation.
Copyright (c) 2017, 2019, MariaDB Corporation.
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
......@@ -57,31 +57,6 @@ check.
If you make a change in this module make sure that no codepath is
introduced where a call to log_free_check() is bypassed. */
/** Create a purge node to a query graph.
@param[in] parent parent node, i.e., a thr node
@param[in] heap memory heap where created
@return own: purge node */
purge_node_t*
row_purge_node_create(
que_thr_t* parent,
mem_heap_t* heap)
{
purge_node_t* node;
ut_ad(parent != NULL);
ut_ad(heap != NULL);
node = static_cast<purge_node_t*>(
mem_heap_zalloc(heap, sizeof(*node)));
node->common.type = QUE_NODE_PURGE;
node->common.parent = parent;
node->done = TRUE;
node->heap = mem_heap_create(256);
return(node);
}
/***********************************************************//**
Repositions the pcur in the purge node on the clustered index record,
if found. If the record is not found, close pcur.
......@@ -966,7 +941,6 @@ row_purge_parse_undo_rec(
byte* ptr;
undo_no_t undo_no;
table_id_t table_id;
trx_id_t trx_id;
roll_ptr_t roll_ptr;
ulint info_bits;
ulint type;
......@@ -980,15 +954,14 @@ row_purge_parse_undo_rec(
node->rec_type = type;
if (type == TRX_UNDO_UPD_DEL_REC && !*updated_extern) {
return(false);
if ((type == TRX_UNDO_UPD_DEL_REC && !*updated_extern)
|| node->is_skipped(table_id)) {
node->table = NULL;
return false;
}
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
ptr = trx_undo_update_rec_get_sys_cols(ptr, &node->trx_id, &roll_ptr,
&info_bits);
node->table = NULL;
node->trx_id = trx_id;
/* Prevent DROP TABLE etc. from running when we are doing the purge
for this row */
......@@ -999,15 +972,18 @@ row_purge_parse_undo_rec(
node->table = dict_table_open_on_id(
table_id, FALSE, DICT_TABLE_OP_NORMAL);
trx_id_t trx_id;
if (node->table == NULL) {
/* The table has been dropped: no need to do purge */
trx_id = TRX_ID_MAX;
goto err_exit;
}
ut_ad(!dict_table_is_temporary(node->table));
if (!fil_table_accessible(node->table)) {
goto close_exit;
goto inaccessible;
}
if (node->table->n_v_cols && !node->table->vc_templ
......@@ -1036,11 +1012,20 @@ row_purge_parse_undo_rec(
/* The table was corrupt in the data dictionary.
dict_set_corrupted() works on an index, and
we do not have an index to call it with. */
inaccessible:
DBUG_ASSERT(table_id == node->table->id);
trx_id = node->table->def_trx_id;
if (!trx_id) {
trx_id = TRX_ID_MAX;
}
close_exit:
dict_table_close(node->table, FALSE, FALSE);
node->table = NULL;
err_exit:
rw_lock_s_unlock(dict_operation_lock);
if (table_id) {
node->skip(table_id, trx_id);
}
return(false);
}
......@@ -1049,13 +1034,15 @@ row_purge_parse_undo_rec(
&& !*updated_extern) {
/* Purge requires no changes to indexes: we may return */
table_id = 0;
goto close_exit;
}
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
node->heap);
ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
ptr = trx_undo_update_rec_get_update(ptr, clust_index, type,
node->trx_id,
roll_ptr, info_bits,
node->heap, &(node->update));
......
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2018, MariaDB Corporation.
Copyright (c) 2017, 2019, MariaDB Corporation.
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
......@@ -182,7 +182,8 @@ purge_graph_build()
for (ulint i = 0; i < srv_n_purge_threads; ++i) {
que_thr_t* thr = que_thr_create(fork, heap, NULL);
thr->child = row_purge_node_create(thr, heap);
thr->child = new(mem_heap_zalloc(heap, sizeof(purge_node_t)))
purge_node_t(thr);
}
return(fork);
......
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