From 6eaa34e664b11e0e7323502f78431515f360a143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 May 2023 15:32:20 +0300 Subject: [PATCH] Remove row_prebuilt_t::fetch_cache[] --- storage/innobase/include/row0mysql.h | 21 -- storage/innobase/row/row0mysql.cc | 21 -- storage/innobase/row/row0sel.cc | 341 +++------------------------ 3 files changed, 28 insertions(+), 355 deletions(-) diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 12197317937..636ed265b0a 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -439,10 +439,6 @@ struct mysql_row_templ_t { ulint is_virtual; /*!< if a column is a virtual column */ }; -#define MYSQL_FETCH_CACHE_SIZE 8 -/* After fetching this many rows, we start caching them in fetch_cache */ -#define MYSQL_FETCH_CACHE_THRESHOLD 4 - #define ROW_PREBUILT_ALLOCATED 78540783 #define ROW_PREBUILT_FREED 26423527 @@ -719,27 +715,10 @@ struct row_prebuilt_t { the last requested column */ ulint mysql_row_len; /*!< length in bytes of a row in the MySQL format */ - ulint n_rows_fetched; /*!< number of rows fetched after - positioning the current cursor */ - ulint fetch_direction;/*!< ROW_SEL_NEXT or ROW_SEL_PREV */ - byte* fetch_cache[MYSQL_FETCH_CACHE_SIZE]; - /*!< a cache for fetched rows if we - fetch many rows from the same cursor: - it saves CPU time to fetch them in a - batch; we reserve mysql_row_len - bytes for each such row; these - pointers point 4 bytes past the - allocated mem buf start, because - there is a 4 byte magic number at the - start and at the end */ bool keep_other_fields_on_keyread; /*!< when using fetch cache with HA_EXTRA_KEYREAD, don't overwrite other fields in mysql row row buffer.*/ - ulint fetch_cache_first;/*!< position of the first not yet - fetched row in fetch_cache */ - ulint n_fetch_cached; /*!< number of not yet fetched rows - in fetch_cache */ mem_heap_t* blob_heap; /*!< in SELECTS BLOB fields are copied to this heap */ mem_heap_t* old_vers_heap; /*!< memory heap where a previous diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index b3014e75466..40cf1bc8d2c 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -948,27 +948,6 @@ void row_prebuilt_free(row_prebuilt_t *prebuilt) mem_heap_free(prebuilt->old_vers_heap); } - if (prebuilt->fetch_cache[0] != NULL) { - byte* base = prebuilt->fetch_cache[0] - 4; - byte* ptr = base; - - for (ulint i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { - ulint magic1 = mach_read_from_4(ptr); - ut_a(magic1 == ROW_PREBUILT_FETCH_MAGIC_N); - ptr += 4; - - byte* row = ptr; - ut_a(row == prebuilt->fetch_cache[i]); - ptr += prebuilt->mysql_row_len; - - ulint magic2 = mach_read_from_4(ptr); - ut_a(magic2 == ROW_PREBUILT_FETCH_MAGIC_N); - ptr += 4; - } - - ut_free(base); - } - if (prebuilt->rtr_info) { rtr_clean_rtr_info(prebuilt->rtr_info, true); } diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 3a8364f2a65..7f9a7b14090 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -3822,142 +3822,6 @@ row_sel_copy_cached_fields_for_mysql( } } -/********************************************************************//** -Pops a cached row for MySQL from the fetch cache. */ -UNIV_INLINE -void -row_sel_dequeue_cached_row_for_mysql( -/*=================================*/ - byte* buf, /*!< in/out: buffer where to copy the - row */ - row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ -{ - ulint i; - const mysql_row_templ_t*templ; - const byte* cached_rec; - ut_ad(prebuilt->n_fetch_cached > 0); - ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len); - - MEM_CHECK_ADDRESSABLE(buf, prebuilt->mysql_row_len); - - cached_rec = prebuilt->fetch_cache[prebuilt->fetch_cache_first]; - - if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) { - row_sel_copy_cached_fields_for_mysql(buf, cached_rec, prebuilt); - } else if (prebuilt->mysql_prefix_len > 63) { - /* The record is long. Copy it field by field, in case - there are some long VARCHAR column of which only a - small length is being used. */ - MEM_UNDEFINED(buf, prebuilt->mysql_prefix_len); - - /* First copy the NULL bits. */ - memcpy(buf, cached_rec, prebuilt->null_bitmap_len); - /* Then copy the requested fields. */ - - for (i = 0; i < prebuilt->n_template; i++) { - templ = prebuilt->mysql_template + i; - - /* Skip virtual columns */ - if (templ->is_virtual - && !(dict_index_has_virtual(prebuilt->index) - && prebuilt->read_just_key)) { - continue; - } - - row_sel_copy_cached_field_for_mysql( - buf, cached_rec, templ); - } - } else { - memcpy(buf, cached_rec, prebuilt->mysql_prefix_len); - } - - prebuilt->n_fetch_cached--; - prebuilt->fetch_cache_first++; - - if (prebuilt->n_fetch_cached == 0) { - prebuilt->fetch_cache_first = 0; - } -} - -/********************************************************************//** -Initialise the prefetch cache. */ -UNIV_INLINE -void -row_sel_prefetch_cache_init( -/*========================*/ - row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */ -{ - ulint i; - ulint sz; - byte* ptr; - - /* Reserve space for the magic number. */ - sz = UT_ARR_SIZE(prebuilt->fetch_cache) * (prebuilt->mysql_row_len + 8); - ptr = static_cast(ut_malloc_nokey(sz)); - - for (i = 0; i < UT_ARR_SIZE(prebuilt->fetch_cache); i++) { - - /* A user has reported memory corruption in these - buffers in Linux. Put magic numbers there to help - to track a possible bug. */ - - mach_write_to_4(ptr, ROW_PREBUILT_FETCH_MAGIC_N); - ptr += 4; - - prebuilt->fetch_cache[i] = ptr; - ptr += prebuilt->mysql_row_len; - - mach_write_to_4(ptr, ROW_PREBUILT_FETCH_MAGIC_N); - ptr += 4; - } -} - -/********************************************************************//** -Get the last fetch cache buffer from the queue. -@return pointer to buffer. */ -UNIV_INLINE -byte* -row_sel_fetch_last_buf( -/*===================*/ - row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */ -{ - ut_ad(!prebuilt->templ_contains_blob); - ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); - - if (prebuilt->fetch_cache[0] == NULL) { - /* Allocate memory for the fetch cache */ - ut_ad(prebuilt->n_fetch_cached == 0); - - row_sel_prefetch_cache_init(prebuilt); - } - - ut_ad(prebuilt->fetch_cache_first == 0); - MEM_UNDEFINED(prebuilt->fetch_cache[prebuilt->n_fetch_cached], - prebuilt->mysql_row_len); - - return(prebuilt->fetch_cache[prebuilt->n_fetch_cached]); -} - -/********************************************************************//** -Pushes a row for MySQL to the fetch cache. */ -UNIV_INLINE -void -row_sel_enqueue_cache_row_for_mysql( -/*================================*/ - byte* mysql_rec, /*!< in/out: MySQL record */ - row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */ -{ - /* For non ICP code path the row should already exist in the - next fetch cache slot. */ - - if (prebuilt->pk_filter || prebuilt->idx_cond) { - memcpy(row_sel_fetch_last_buf(prebuilt), mysql_rec, - prebuilt->mysql_row_len); - } - - ++prebuilt->n_fetch_cached; -} - #ifdef BTR_CUR_HASH_ADAPT /*********************************************************************//** Tries to do a shortcut to fetch a clustered index record with a unique key, @@ -4385,7 +4249,6 @@ row_search_mvcc( ulint next_offs; bool same_user_rec; ibool table_lock_waited = FALSE; - byte* next_buf = 0; bool spatial_search = false; btr_latch_mode batch_latch_mode = BTR_SEARCH_LEAF; @@ -4437,59 +4300,12 @@ row_search_mvcc( if (UNIV_UNLIKELY(direction == 0)) { trx->op_info = "starting index read"; - prebuilt->n_rows_fetched = 0; - prebuilt->n_fetch_cached = 0; - prebuilt->fetch_cache_first = 0; - if (prebuilt->sel_graph == NULL) { /* Build a dummy select query graph */ row_prebuild_sel_graph(prebuilt); } } else { trx->op_info = "fetching rows"; - - if (prebuilt->n_rows_fetched == 0) { - prebuilt->fetch_direction = direction; - } - - if (UNIV_UNLIKELY(direction != prebuilt->fetch_direction)) { - if (UNIV_UNLIKELY(prebuilt->n_fetch_cached > 0)) { - ut_error; - /* TODO: scrollable cursor: restore cursor to - the place of the latest returned row, - or better: prevent caching for a scroll - cursor! */ - } - - prebuilt->n_rows_fetched = 0; - prebuilt->n_fetch_cached = 0; - prebuilt->fetch_cache_first = 0; - - } else if (UNIV_LIKELY(prebuilt->n_fetch_cached > 0)) { - row_sel_dequeue_cached_row_for_mysql(buf, prebuilt); - - prebuilt->n_rows_fetched++; - trx->op_info = ""; - DBUG_RETURN(DB_SUCCESS); - } - - if (prebuilt->fetch_cache_first > 0 - && prebuilt->fetch_cache_first < MYSQL_FETCH_CACHE_SIZE) { -early_not_found: - /* The previous returned row was popped from the fetch - cache, but the cache was not full at the time of the - popping: no more rows can exist in the result set */ - trx->op_info = ""; - DBUG_RETURN(DB_RECORD_NOT_FOUND); - } - - prebuilt->n_rows_fetched++; - - if (prebuilt->n_rows_fetched > 1000000000) { - /* Prevent wrap-over */ - prebuilt->n_rows_fetched = 500000000; - } - mode = pcur->search_mode; } @@ -4524,7 +4340,8 @@ early_not_found: if (UNIV_UNLIKELY(direction != 0 && !prebuilt->used_in_HANDLER)) { - goto early_not_found; + trx->op_info = ""; + DBUG_RETURN(DB_RECORD_NOT_FOUND); } } @@ -5649,116 +5466,35 @@ use_covering_index: offsets)); ut_ad(!rec_get_deleted_flag(result_rec, comp)); - /* Decide whether to prefetch extra rows. - At this point, the clustered index record is protected - by a page latch that was acquired when pcur was positioned. - The latch will not be released until mtr.commit(). */ - - if ((match_mode == ROW_SEL_EXACT - || prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD) - && prebuilt->select_lock_type == LOCK_NONE - && !prebuilt->templ_contains_blob - && !prebuilt->clust_index_was_generated - && !prebuilt->used_in_HANDLER - && !prebuilt->in_fts_query) { - /* Inside an update, for example, we do not cache rows, - since we may use the cursor position to do the actual - update, that is why we require ...lock_type == LOCK_NONE. - Since we keep space in prebuilt only for the BLOBs of - a single row, we cannot cache rows in the case there - are BLOBs in the fields to be fetched. In HANDLER we do - not cache rows because there the cursor is a scrollable - cursor. */ - - ut_a(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); - - /* We only convert from InnoDB row format to MySQL row - format when ICP is disabled. */ - - if (!prebuilt->pk_filter && !prebuilt->idx_cond) { - /* We use next_buf to track the allocation of buffers - where we store and enqueue the buffers for our - pre-fetch optimisation. - - If next_buf == 0 then we store the converted record - directly into the MySQL record buffer (buf). If it is - != 0 then we allocate a pre-fetch buffer and store the - converted record there. - - If the conversion fails and the MySQL record buffer - was not written to then we reset next_buf so that - we can re-use the MySQL record buffer in the next - iteration. */ - - next_buf = next_buf - ? row_sel_fetch_last_buf(prebuilt) : buf; - - if (!row_sel_store_mysql_rec( - next_buf, prebuilt, result_rec, vrow, - result_rec != rec, - result_rec != rec ? clust_index : index, - offsets)) { - - if (next_buf == buf) { - ut_a(prebuilt->n_fetch_cached == 0); - next_buf = 0; - } - - /* Only fresh inserts may contain incomplete - externally stored columns. Pretend that such - records do not exist. Such records may only be - accessed at the READ UNCOMMITTED isolation - level or when rolling back a recovered - transaction. Rollback happens at a lower - level, not here. */ - goto next_rec; - } - - if (next_buf != buf) { - row_sel_enqueue_cache_row_for_mysql( - next_buf, prebuilt); - } - } else { - row_sel_enqueue_cache_row_for_mysql(buf, prebuilt); - } - - if (prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE) { + if (!prebuilt->pk_filter && !prebuilt->idx_cond) { + /* The record was not yet converted to MySQL format. */ + if (!row_sel_store_mysql_rec( + buf, prebuilt, result_rec, vrow, + result_rec != rec, + result_rec != rec ? clust_index : index, + offsets)) { + /* Only fresh inserts may contain incomplete + externally stored columns. Pretend that such + records do not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation + level or when rolling back a recovered + transaction. Rollback happens at a lower + level, not here. */ goto next_rec; } - } else { - if (!prebuilt->pk_filter && !prebuilt->idx_cond) { - /* The record was not yet converted to MySQL format. */ - if (!row_sel_store_mysql_rec( - buf, prebuilt, result_rec, vrow, - result_rec != rec, - result_rec != rec ? clust_index : index, - offsets)) { - /* Only fresh inserts may contain - incomplete externally stored - columns. Pretend that such records do - not exist. Such records may only be - accessed at the READ UNCOMMITTED - isolation level or when rolling back a - recovered transaction. Rollback - happens at a lower level, not here. */ - goto next_rec; - } - } + } - if (!prebuilt->clust_index_was_generated) { - } else if (result_rec != rec || index->is_primary()) { - memcpy(prebuilt->row_id, result_rec, DATA_ROW_ID_LEN); - } else { - ulint len; - const byte* data = rec_get_nth_field( - result_rec, offsets, index->n_fields - 1, - &len); - ut_ad(dict_index_get_nth_col(index, - index->n_fields - 1) - ->prtype == (DATA_ROW_ID | DATA_NOT_NULL)); - ut_ad(len == DATA_ROW_ID_LEN); - memcpy(prebuilt->row_id, data, DATA_ROW_ID_LEN); - } + if (!prebuilt->clust_index_was_generated) { + } else if (result_rec != rec || index->is_primary()) { + memcpy(prebuilt->row_id, result_rec, DATA_ROW_ID_LEN); + } else { + ulint len; + const byte* data = rec_get_nth_field( + result_rec, offsets, index->n_fields - 1, &len); + ut_ad(dict_index_get_nth_col(index, index->n_fields - 1) + ->prtype == (DATA_ROW_ID | DATA_NOT_NULL)); + ut_ad(len == DATA_ROW_ID_LEN); + memcpy(prebuilt->row_id, data, DATA_ROW_ID_LEN); } /* From this point on, 'offsets' are invalid. */ @@ -5961,27 +5697,6 @@ normal_return: DEBUG_SYNC_C("row_search_for_mysql_before_return"); - if (prebuilt->pk_filter || prebuilt->idx_cond) { - /* When ICP is active we don't write to the MySQL buffer - directly, only to buffers that are enqueued in the pre-fetch - queue. We need to dequeue the first buffer and copy the contents - to the record buffer that was passed in by MySQL. */ - - if (prebuilt->n_fetch_cached > 0) { - row_sel_dequeue_cached_row_for_mysql(buf, prebuilt); - err = DB_SUCCESS; - } - - } else if (next_buf != 0) { - - /* We may or may not have enqueued some buffers to the - pre-fetch queue, but we definitely wrote to the record - buffer passed to use by MySQL. */ - - DEBUG_SYNC_C("row_search_cached_row"); - err = DB_SUCCESS; - } - #ifdef UNIV_DEBUG if (dict_index_is_spatial(index) && err != DB_SUCCESS && err != DB_END_OF_INDEX && err != DB_INTERRUPTED) {