diff options
author | Aleksey Midenkov <midenok@gmail.com> | 2019-08-09 00:31:35 +0300 |
---|---|---|
committer | Aleksey Midenkov <midenok@gmail.com> | 2019-08-14 19:10:17 +0300 |
commit | 2347ffd843b8e4ee9d8eaafab05368435db59ece (patch) | |
tree | 3a80326f2e22dfffadc0cedea463e39a9c417544 /storage | |
parent | 65296123d0fcaeb122bc3b9d9e387468052c06b6 (diff) | |
download | mariadb-git-2347ffd843b8e4ee9d8eaafab05368435db59ece.tar.gz |
MDEV-20301 InnoDB's MVCC has O(N^2) behaviors
If there're multiple row versions in InnoDB, reading one row from PK
may have O(N) complexity and reading from secondary keys may have
O(N^2) complexity.
The problem occurs when there are many pending versions of the same
row, meaning that the primary key is the same, but a secondary key is
different. The slowdown occurs when the secondary index is
traversed. This patch creates a helper class for the function
row_sel_get_clust_rec_for_mysql() which can remember and re-use
cached_clust_rec & cached_old_vers so that rec_get_offsets() does not
need to be called over and over for the clustered record.
Corrections by Kevin Lewis <kevin.lewis@oracle.com>
MDEV-20341 Unstable innodb.innodb_bug14704286
Removed test that tested the ability of interrupting long query which
is not long anymore.
Diffstat (limited to 'storage')
-rw-r--r-- | storage/innobase/row/row0sel.cc | 55 |
1 files changed, 46 insertions, 9 deletions
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 877d9c3ef78..d5b1a596f01 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -3309,14 +3309,29 @@ row_sel_build_prev_vers_for_mysql( return(err); } +/** Helper class to cache clust_rec and old_ver */ +class Row_sel_get_clust_rec_for_mysql +{ + const rec_t *cached_clust_rec; + rec_t *cached_old_vers; + +public: + Row_sel_get_clust_rec_for_mysql() : + cached_clust_rec(NULL), cached_old_vers(NULL) {} + + dberr_t operator()(row_prebuilt_t *prebuilt, dict_index_t *sec_index, + const rec_t *rec, que_thr_t *thr, const rec_t **out_rec, + ulint **offsets, mem_heap_t **offset_heap, + dtuple_t **vrow, mtr_t *mtr); +}; + /*********************************************************************//** Retrieves the clustered index record corresponding to a record in a non-clustered index. Does the necessary locking. Used in the MySQL interface. @return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */ -static MY_ATTRIBUTE((warn_unused_result)) dberr_t -row_sel_get_clust_rec_for_mysql( +Row_sel_get_clust_rec_for_mysql::operator()( /*============================*/ row_prebuilt_t* prebuilt,/*!< in: prebuilt struct in the handle */ dict_index_t* sec_index,/*!< in: secondary index where rec resides */ @@ -3508,15 +3523,36 @@ row_sel_get_clust_rec_for_mysql( clust_rec, clust_index, *offsets, trx_get_read_view(trx))) { - /* The following call returns 'offsets' associated with - 'old_vers' */ - err = row_sel_build_prev_vers_for_mysql( - trx->read_view, clust_index, prebuilt, - clust_rec, offsets, offset_heap, &old_vers, - vrow, mtr); + if (clust_rec != cached_clust_rec) { + /* The following call returns 'offsets' associated with + 'old_vers' */ + err = row_sel_build_prev_vers_for_mysql( + trx->read_view, clust_index, prebuilt, + clust_rec, offsets, offset_heap, &old_vers, + vrow, mtr); + + if (err != DB_SUCCESS) { + + goto err_exit; + } + cached_clust_rec = clust_rec; + cached_old_vers = old_vers; + } else { + err = DB_SUCCESS; + old_vers = cached_old_vers; + + /* The offsets need not be same for the latest + version of clust_rec and its old version + old_vers. Re-calculate the offsets for old_vers. */ - if (err != DB_SUCCESS || old_vers == NULL) { + if (old_vers != NULL) { + *offsets = rec_get_offsets( + old_vers, clust_index, *offsets, + true, ULINT_UNDEFINED, offset_heap); + } + } + if (old_vers == NULL) { goto err_exit; } @@ -4233,6 +4269,7 @@ row_search_mvcc( dtuple_t* vrow = NULL; const rec_t* result_rec = NULL; const rec_t* clust_rec; + Row_sel_get_clust_rec_for_mysql row_sel_get_clust_rec_for_mysql; dberr_t err = DB_SUCCESS; ibool unique_search = FALSE; ibool mtr_has_extra_clust_latch = FALSE; |