Commit 5b62644e authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-31621 Remove ibuf_read_merge_pages() call from ibuf_insert_low()

When InnoDB attempts to buffer a change operation of a secondary index
leaf page (to insert, delete-mark or remove a record) and the
change buffer is too large, InnoDB used to trigger a change buffer merge
that could affect any tables. This could lead to huge variance in
system throughput and potentially unpredictable crashes, in case the
change buffer was corrupted and a crash occurred while attempting to
merge changes to a table that is not being accessed by the current
SQL statement.

ibuf_insert_low(): Simply return DB_STRONG_FAIL when the maximum size
of the change buffer is exceeded.

ibuf_contract_after_insert(): Remove.

ibuf_get_merge_page_nos_func(): Remove a constant parameter.
The function ibuf_contract() will be our only caller, during
shutdown with innodb_fast_shutdown=0.
parent 46b79b8c
...@@ -243,21 +243,11 @@ mysql_mutex_t ibuf_mutex, ...@@ -243,21 +243,11 @@ mysql_mutex_t ibuf_mutex,
ibuf_pessimistic_insert_mutex; ibuf_pessimistic_insert_mutex;
/** The area in pages from which contract looks for page numbers for merge */ /** The area in pages from which contract looks for page numbers for merge */
const ulint IBUF_MERGE_AREA = 8; constexpr ulint IBUF_MERGE_AREA = 8;
/** Inside the merge area, pages which have at most 1 per this number less /** In ibuf_contract() at most this number of pages is read to memory in one
buffered entries compared to maximum volume that can buffered for a single batch, in order to merge the entries for them in the change buffer */
page are merged along with the page whose buffer became full */ constexpr ulint IBUF_MAX_N_PAGES_MERGED = IBUF_MERGE_AREA;
const ulint IBUF_MERGE_THRESHOLD = 4;
/** In ibuf_contract at most this number of pages is read to memory in one
batch, in order to merge the entries for them in the insert buffer */
const ulint IBUF_MAX_N_PAGES_MERGED = IBUF_MERGE_AREA;
/** If the combined size of the ibuf trees exceeds ibuf.max_size by
this many pages, we start to contract it synchronous contract, but do
not insert */
const ulint IBUF_CONTRACT_DO_NOT_INSERT = 10;
/* TODO: how to cope with drop table if there are records in the insert /* TODO: how to cope with drop table if there are records in the insert
buffer for the indexes of the table? Is there actually any problem, buffer for the indexes of the table? Is there actually any problem,
...@@ -2004,11 +1994,11 @@ ibuf_free_excess_pages(void) ...@@ -2004,11 +1994,11 @@ ibuf_free_excess_pages(void)
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
# define ibuf_get_merge_page_nos(contract,rec,mtr,ids,pages,n_stored) \ # define ibuf_get_merge_page_nos(rec,mtr,ids,pages,n_stored) \
ibuf_get_merge_page_nos_func(contract,rec,mtr,ids,pages,n_stored) ibuf_get_merge_page_nos_func(rec,mtr,ids,pages,n_stored)
#else /* UNIV_DEBUG */ #else /* UNIV_DEBUG */
# define ibuf_get_merge_page_nos(contract,rec,mtr,ids,pages,n_stored) \ # define ibuf_get_merge_page_nos(rec,mtr,ids,pages,n_stored) \
ibuf_get_merge_page_nos_func(contract,rec,ids,pages,n_stored) ibuf_get_merge_page_nos_func(rec,ids,pages,n_stored)
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/*********************************************************************//** /*********************************************************************//**
...@@ -2019,10 +2009,6 @@ static ...@@ -2019,10 +2009,6 @@ static
ulint ulint
ibuf_get_merge_page_nos_func( ibuf_get_merge_page_nos_func(
/*=========================*/ /*=========================*/
ibool contract,/*!< in: TRUE if this function is called to
contract the tree, FALSE if this is called
when a single page becomes full and we look
if it pays to read also nearby pages */
const rec_t* rec, /*!< in: insert buffer record */ const rec_t* rec, /*!< in: insert buffer record */
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
mtr_t* mtr, /*!< in: mini-transaction holding rec */ mtr_t* mtr, /*!< in: mini-transaction holding rec */
...@@ -2153,22 +2139,10 @@ ibuf_get_merge_page_nos_func( ...@@ -2153,22 +2139,10 @@ ibuf_get_merge_page_nos_func(
|| rec_page_no != prev_page_no) || rec_page_no != prev_page_no)
&& (prev_space_id != 0 || prev_page_no != 0)) { && (prev_space_id != 0 || prev_page_no != 0)) {
if (contract
|| (prev_page_no == first_page_no
&& prev_space_id == first_space_id)
|| (volume_for_page
> ((IBUF_MERGE_THRESHOLD - 1)
* 4U << srv_page_size_shift
/ IBUF_PAGE_SIZE_PER_FREE_SPACE)
/ IBUF_MERGE_THRESHOLD)) {
space_ids[*n_stored] = prev_space_id; space_ids[*n_stored] = prev_space_id;
page_nos[*n_stored] = prev_page_no; page_nos[*n_stored] = prev_page_no;
(*n_stored)++; (*n_stored)++;
sum_volumes += volume_for_page; sum_volumes += volume_for_page;
}
if (rec_space_id != first_space_id if (rec_space_id != first_space_id
|| rec_page_no / IBUF_MERGE_AREA || rec_page_no / IBUF_MERGE_AREA
...@@ -2428,7 +2402,7 @@ static void ibuf_read_merge_pages(const uint32_t* space_ids, ...@@ -2428,7 +2402,7 @@ static void ibuf_read_merge_pages(const uint32_t* space_ids,
@return a lower limit for the combined size in bytes of entries which @return a lower limit for the combined size in bytes of entries which
will be merged from ibuf trees to the pages read will be merged from ibuf trees to the pages read
@retval 0 if ibuf.empty */ @retval 0 if ibuf.empty */
ulint ibuf_contract() ATTRIBUTE_COLD ulint ibuf_contract()
{ {
if (UNIV_UNLIKELY(!ibuf.index)) return 0; if (UNIV_UNLIKELY(!ibuf.index)) return 0;
mtr_t mtr; mtr_t mtr;
...@@ -2460,10 +2434,8 @@ ulint ibuf_contract() ...@@ -2460,10 +2434,8 @@ ulint ibuf_contract()
} }
ulint n_pages = 0; ulint n_pages = 0;
sum_sizes = ibuf_get_merge_page_nos(TRUE, sum_sizes = ibuf_get_merge_page_nos(btr_cur_get_rec(&cur), &mtr,
btr_cur_get_rec(&cur), &mtr, space_ids, page_nos, &n_pages);
space_ids,
page_nos, &n_pages);
ibuf_mtr_commit(&mtr); ibuf_mtr_commit(&mtr);
ibuf_read_merge_pages(space_ids, page_nos, n_pages); ibuf_read_merge_pages(space_ids, page_nos, n_pages);
...@@ -2553,30 +2525,6 @@ ibuf_merge_space( ...@@ -2553,30 +2525,6 @@ ibuf_merge_space(
return(n_pages); return(n_pages);
} }
/*********************************************************************//**
Contract insert buffer trees after insert if they are too big. */
UNIV_INLINE
void
ibuf_contract_after_insert(
/*=======================*/
ulint entry_size) /*!< in: size of a record which was inserted
into an ibuf tree */
{
/* dirty comparison, to avoid contention on ibuf_mutex */
if (ibuf.size < ibuf.max_size) {
return;
}
/* Contract at least entry_size many bytes */
ulint sum_sizes = 0;
ulint size;
do {
size = ibuf_contract();
sum_sizes += size;
} while (size > 0 && sum_sizes < entry_size);
}
/** Determine if a change buffer record has been encountered already. /** Determine if a change buffer record has been encountered already.
@param rec change buffer record in the MySQL 5.5 format @param rec change buffer record in the MySQL 5.5 format
@param hash hash table of encountered records @param hash hash table of encountered records
...@@ -3175,10 +3123,6 @@ ibuf_insert_low( ...@@ -3175,10 +3123,6 @@ ibuf_insert_low(
buf_block_t* block = NULL; buf_block_t* block = NULL;
page_t* root; page_t* root;
dberr_t err; dberr_t err;
ibool do_merge;
uint32_t space_ids[IBUF_MAX_N_PAGES_MERGED];
uint32_t page_nos[IBUF_MAX_N_PAGES_MERGED];
ulint n_stored;
mtr_t mtr; mtr_t mtr;
mtr_t bitmap_mtr; mtr_t bitmap_mtr;
...@@ -3189,28 +3133,9 @@ ibuf_insert_low( ...@@ -3189,28 +3133,9 @@ ibuf_insert_low(
ut_ad(page_id.space() == index->table->space_id); ut_ad(page_id.space() == index->table->space_id);
ut_a(op < IBUF_OP_COUNT); ut_a(op < IBUF_OP_COUNT);
do_merge = FALSE;
/* Perform dirty comparison of ibuf.max_size and ibuf.size to /* Perform dirty comparison of ibuf.max_size and ibuf.size to
reduce ibuf_mutex contention. This should be OK; at worst we reduce ibuf_mutex contention. */
are doing some excessive ibuf_contract() or occasionally if (ibuf.size >= ibuf.max_size) {
skipping an ibuf_contract(). */
const ulint max_size = ibuf.max_size;
if (max_size == 0) {
return(DB_STRONG_FAIL);
}
if (ibuf.size >= max_size + IBUF_CONTRACT_DO_NOT_INSERT) {
/* Insert buffer is now too big, contract it but do not try
to insert */
#ifdef UNIV_IBUF_DEBUG
fputs("Ibuf too big\n", stderr);
#endif
ibuf_contract();
return(DB_STRONG_FAIL); return(DB_STRONG_FAIL);
} }
...@@ -3262,17 +3187,6 @@ ibuf_insert_low( ...@@ -3262,17 +3187,6 @@ ibuf_insert_low(
ibuf_mtr_commit(&mtr); ibuf_mtr_commit(&mtr);
ut_free(pcur.old_rec_buf); ut_free(pcur.old_rec_buf);
mem_heap_free(heap); mem_heap_free(heap);
if (err == DB_SUCCESS && mode == BTR_INSERT_TREE) {
ibuf_contract_after_insert(entry_size);
}
if (do_merge) {
#ifdef UNIV_IBUF_DEBUG
ut_a(n_stored <= IBUF_MAX_N_PAGES_MERGED);
#endif
ibuf_read_merge_pages(space_ids, page_nos, n_stored);
}
return err; return err;
} }
...@@ -3362,15 +3276,6 @@ ibuf_insert_low( ...@@ -3362,15 +3276,6 @@ ibuf_insert_low(
bits)) { bits)) {
/* Release the bitmap page latch early. */ /* Release the bitmap page latch early. */
ibuf_mtr_commit(&bitmap_mtr); ibuf_mtr_commit(&bitmap_mtr);
/* It may not fit */
do_merge = TRUE;
ibuf_get_merge_page_nos(FALSE,
btr_pcur_get_rec(&pcur), &mtr,
space_ids,
page_nos, &n_stored);
goto fail_exit; goto fail_exit;
} }
} }
......
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