2014-02-26 19:11:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
2016-06-21 14:21:03 +02:00
|
|
|
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
2022-04-07 10:32:56 +03:00
|
|
|
Copyright (c) 2016, 2022, MariaDB Corporation.
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
Foundation; version 2 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
2019-05-11 18:08:32 +03:00
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**************************************************//**
|
|
|
|
@file ibuf/ibuf0ibuf.cc
|
|
|
|
Insert buffer
|
|
|
|
|
|
|
|
Created 7/19/1997 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
#include "ibuf0ibuf.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "sync0sync.h"
|
|
|
|
#include "btr0sea.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** Number of bits describing a single page */
|
|
|
|
#define IBUF_BITS_PER_PAGE 4
|
|
|
|
/** The start address for an insert buffer bitmap page bitmap */
|
|
|
|
#define IBUF_BITMAP PAGE_DATA
|
|
|
|
|
|
|
|
#include "buf0buf.h"
|
|
|
|
#include "buf0rea.h"
|
|
|
|
#include "fsp0fsp.h"
|
|
|
|
#include "trx0sys.h"
|
|
|
|
#include "fil0fil.h"
|
|
|
|
#include "rem0rec.h"
|
|
|
|
#include "btr0cur.h"
|
|
|
|
#include "btr0pcur.h"
|
|
|
|
#include "btr0btr.h"
|
|
|
|
#include "row0upd.h"
|
|
|
|
#include "dict0boot.h"
|
|
|
|
#include "fut0lst.h"
|
|
|
|
#include "lock0lock.h"
|
|
|
|
#include "log0recv.h"
|
|
|
|
#include "que0que.h"
|
|
|
|
#include "srv0start.h" /* srv_shutdown_state */
|
|
|
|
#include "rem0cmp.h"
|
|
|
|
|
|
|
|
/* STRUCTURE OF AN INSERT BUFFER RECORD
|
|
|
|
|
|
|
|
In versions < 4.1.x:
|
|
|
|
|
|
|
|
1. The first field is the page number.
|
|
|
|
2. The second field is an array which stores type info for each subsequent
|
|
|
|
field. We store the information which affects the ordering of records, and
|
|
|
|
also the physical storage size of an SQL NULL value. E.g., for CHAR(10) it
|
|
|
|
is 10 bytes.
|
|
|
|
3. Next we have the fields of the actual index record.
|
|
|
|
|
|
|
|
In versions >= 4.1.x:
|
|
|
|
|
|
|
|
Note that contary to what we planned in the 1990's, there will only be one
|
|
|
|
insert buffer tree, and that is in the system tablespace of InnoDB.
|
|
|
|
|
|
|
|
1. The first field is the space id.
|
|
|
|
2. The second field is a one-byte marker (0) which differentiates records from
|
|
|
|
the < 4.1.x storage format.
|
|
|
|
3. The third field is the page number.
|
|
|
|
4. The fourth field contains the type info, where we have also added 2 bytes to
|
|
|
|
store the charset. In the compressed table format of 5.0.x we must add more
|
|
|
|
information here so that we can build a dummy 'index' struct which 5.0.x
|
|
|
|
can use in the binary search on the index page in the ibuf merge phase.
|
|
|
|
5. The rest of the fields contain the fields of the actual index record.
|
|
|
|
|
|
|
|
In versions >= 5.0.3:
|
|
|
|
|
|
|
|
The first byte of the fourth field is an additional marker (0) if the record
|
|
|
|
is in the compact format. The presence of this marker can be detected by
|
|
|
|
looking at the length of the field modulo DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE.
|
|
|
|
|
|
|
|
The high-order bit of the character set field in the type info is the
|
|
|
|
"nullable" flag for the field.
|
|
|
|
|
|
|
|
In versions >= 5.5:
|
|
|
|
|
|
|
|
The optional marker byte at the start of the fourth field is replaced by
|
|
|
|
mandatory 3 fields, totaling 4 bytes:
|
|
|
|
|
|
|
|
1. 2 bytes: Counter field, used to sort records within a (space id, page
|
|
|
|
no) in the order they were added. This is needed so that for example the
|
|
|
|
sequence of operations "INSERT x, DEL MARK x, INSERT x" is handled
|
|
|
|
correctly.
|
|
|
|
|
|
|
|
2. 1 byte: Operation type (see ibuf_op_t).
|
|
|
|
|
|
|
|
3. 1 byte: Flags. Currently only one flag exists, IBUF_REC_COMPACT.
|
|
|
|
|
|
|
|
To ensure older records, which do not have counters to enforce correct
|
|
|
|
sorting, are merged before any new records, ibuf_insert checks if we're
|
|
|
|
trying to insert to a position that contains old-style records, and if so,
|
|
|
|
refuses the insert. Thus, ibuf pages are gradually converted to the new
|
|
|
|
format as their corresponding buffer pool pages are read into memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* PREVENTING DEADLOCKS IN THE INSERT BUFFER SYSTEM
|
|
|
|
|
|
|
|
If an OS thread performs any operation that brings in disk pages from
|
|
|
|
non-system tablespaces into the buffer pool, or creates such a page there,
|
|
|
|
then the operation may have as a side effect an insert buffer index tree
|
|
|
|
compression. Thus, the tree latch of the insert buffer tree may be acquired
|
|
|
|
in the x-mode, and also the file space latch of the system tablespace may
|
|
|
|
be acquired in the x-mode.
|
|
|
|
|
|
|
|
Also, an insert to an index in a non-system tablespace can have the same
|
|
|
|
effect. How do we know this cannot lead to a deadlock of OS threads? There
|
|
|
|
is a problem with the i\o-handler threads: they break the latching order
|
|
|
|
because they own x-latches to pages which are on a lower level than the
|
|
|
|
insert buffer tree latch, its page latches, and the tablespace latch an
|
|
|
|
insert buffer operation can reserve.
|
|
|
|
|
|
|
|
The solution is the following: Let all the tree and page latches connected
|
|
|
|
with the insert buffer be later in the latching order than the fsp latch and
|
|
|
|
fsp page latches.
|
|
|
|
|
|
|
|
Insert buffer pages must be such that the insert buffer is never invoked
|
|
|
|
when these pages are accessed as this would result in a recursion violating
|
|
|
|
the latching order. We let a special i/o-handler thread take care of i/o to
|
|
|
|
the insert buffer pages and the ibuf bitmap pages, as well as the fsp bitmap
|
|
|
|
pages and the first inode page, which contains the inode of the ibuf tree: let
|
|
|
|
us call all these ibuf pages. To prevent deadlocks, we do not let a read-ahead
|
|
|
|
access both non-ibuf and ibuf pages.
|
|
|
|
|
|
|
|
Then an i/o-handler for the insert buffer never needs to access recursively the
|
|
|
|
insert buffer tree and thus obeys the latching order. On the other hand, other
|
|
|
|
i/o-handlers for other tablespaces may require access to the insert buffer,
|
|
|
|
but because all kinds of latches they need to access there are later in the
|
|
|
|
latching order, no violation of the latching order occurs in this case,
|
|
|
|
either.
|
|
|
|
|
|
|
|
A problem is how to grow and contract an insert buffer tree. As it is later
|
|
|
|
in the latching order than the fsp management, we have to reserve the fsp
|
|
|
|
latch first, before adding or removing pages from the insert buffer tree.
|
|
|
|
We let the insert buffer tree have its own file space management: a free
|
|
|
|
list of pages linked to the tree root. To prevent recursive using of the
|
|
|
|
insert buffer when adding pages to the tree, we must first load these pages
|
|
|
|
to memory, obtaining a latch on them, and only after that add them to the
|
|
|
|
free list of the insert buffer tree. More difficult is removing of pages
|
|
|
|
from the free list. If there is an excess of pages in the free list of the
|
|
|
|
ibuf tree, they might be needed if some thread reserves the fsp latch,
|
|
|
|
intending to allocate more file space. So we do the following: if a thread
|
|
|
|
reserves the fsp latch, we check the writer count field of the latch. If
|
|
|
|
this field has value 1, it means that the thread did not own the latch
|
|
|
|
before entering the fsp system, and the mtr of the thread contains no
|
|
|
|
modifications to the fsp pages. Now we are free to reserve the ibuf latch,
|
|
|
|
and check if there is an excess of pages in the free list. We can then, in a
|
|
|
|
separate mini-transaction, take them out of the free list and free them to
|
|
|
|
the fsp system.
|
|
|
|
|
|
|
|
To avoid deadlocks in the ibuf system, we divide file pages into three levels:
|
|
|
|
|
|
|
|
(1) non-ibuf pages,
|
|
|
|
(2) ibuf tree pages and the pages in the ibuf tree free list, and
|
|
|
|
(3) ibuf bitmap pages.
|
|
|
|
|
|
|
|
No OS thread is allowed to access higher level pages if it has latches to
|
|
|
|
lower level pages; even if the thread owns a B-tree latch it must not access
|
|
|
|
the B-tree non-leaf pages if it has latches on lower level pages. Read-ahead
|
|
|
|
is only allowed for level 1 and 2 pages. Dedicated i/o-handler threads handle
|
|
|
|
exclusively level 1 i/o. A dedicated i/o handler thread handles exclusively
|
|
|
|
level 2 i/o. However, if an OS thread does the i/o handling for itself, i.e.,
|
|
|
|
it uses synchronous aio, it can access any pages, as long as it obeys the
|
|
|
|
access order rules. */
|
|
|
|
|
|
|
|
/** Operations that can currently be buffered. */
|
2018-06-12 12:49:42 +03:00
|
|
|
ulong innodb_change_buffering;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
|
2019-10-19 15:16:47 +03:00
|
|
|
/** Dump the change buffer at startup */
|
|
|
|
my_bool ibuf_dump;
|
2014-02-26 19:11:54 +01:00
|
|
|
/** Flag to control insert buffer debugging. */
|
2016-08-12 11:17:45 +03:00
|
|
|
uint ibuf_debug;
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
|
|
|
|
|
|
|
|
/** The insert buffer control structure */
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf_t ibuf;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** @name Offsets to the per-page bits in the insert buffer bitmap */
|
|
|
|
/* @{ */
|
|
|
|
#define IBUF_BITMAP_FREE 0 /*!< Bits indicating the
|
|
|
|
amount of free space */
|
|
|
|
#define IBUF_BITMAP_BUFFERED 2 /*!< TRUE if there are buffered
|
|
|
|
changes for the page */
|
|
|
|
#define IBUF_BITMAP_IBUF 3 /*!< TRUE if page is a part of
|
|
|
|
the ibuf tree, excluding the
|
|
|
|
root page, or is in the free
|
|
|
|
list of the ibuf */
|
|
|
|
/* @} */
|
|
|
|
|
|
|
|
#define IBUF_REC_FIELD_SPACE 0 /*!< in the pre-4.1 format,
|
|
|
|
the page number. later, the space_id */
|
|
|
|
#define IBUF_REC_FIELD_MARKER 1 /*!< starting with 4.1, a marker
|
|
|
|
consisting of 1 byte that is 0 */
|
|
|
|
#define IBUF_REC_FIELD_PAGE 2 /*!< starting with 4.1, the
|
|
|
|
page number */
|
|
|
|
#define IBUF_REC_FIELD_METADATA 3 /* the metadata field */
|
|
|
|
#define IBUF_REC_FIELD_USER 4 /* first user field */
|
|
|
|
|
|
|
|
/* Various constants for checking the type of an ibuf record and extracting
|
|
|
|
data from it. For details, see the description of the record format at the
|
|
|
|
top of this file. */
|
|
|
|
|
|
|
|
/** @name Format of the IBUF_REC_FIELD_METADATA of an insert buffer record
|
|
|
|
The fourth column in the MySQL 5.5 format contains an operation
|
|
|
|
type, counter, and some flags. */
|
|
|
|
/* @{ */
|
|
|
|
#define IBUF_REC_INFO_SIZE 4 /*!< Combined size of info fields at
|
|
|
|
the beginning of the fourth field */
|
|
|
|
|
|
|
|
/* Offsets for the fields at the beginning of the fourth field */
|
|
|
|
#define IBUF_REC_OFFSET_COUNTER 0 /*!< Operation counter */
|
|
|
|
#define IBUF_REC_OFFSET_TYPE 2 /*!< Type of operation */
|
|
|
|
#define IBUF_REC_OFFSET_FLAGS 3 /*!< Additional flags */
|
|
|
|
|
|
|
|
/* Record flag masks */
|
|
|
|
#define IBUF_REC_COMPACT 0x1 /*!< Set in
|
|
|
|
IBUF_REC_OFFSET_FLAGS if the
|
|
|
|
user index is in COMPACT
|
|
|
|
format or later */
|
|
|
|
|
|
|
|
|
|
|
|
/** The mutex used to block pessimistic inserts to ibuf trees */
|
|
|
|
static ib_mutex_t ibuf_pessimistic_insert_mutex;
|
|
|
|
|
|
|
|
/** The mutex protecting the insert buffer structs */
|
|
|
|
static ib_mutex_t ibuf_mutex;
|
|
|
|
|
|
|
|
/** The area in pages from which contract looks for page numbers for merge */
|
2016-08-12 11:17:45 +03:00
|
|
|
const ulint IBUF_MERGE_AREA = 8;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** Inside the merge area, pages which have at most 1 per this number less
|
|
|
|
buffered entries compared to maximum volume that can buffered for a single
|
|
|
|
page are merged along with the page whose buffer became full */
|
2016-08-12 11:17:45 +03:00
|
|
|
const ulint IBUF_MERGE_THRESHOLD = 4;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** 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 */
|
2016-08-12 11:17:45 +03:00
|
|
|
const ulint IBUF_MAX_N_PAGES_MERGED = IBUF_MERGE_AREA;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
/** If the combined size of the ibuf trees exceeds ibuf.max_size by
|
2014-02-26 19:11:54 +01:00
|
|
|
this many pages, we start to contract it synchronous contract, but do
|
|
|
|
not insert */
|
2016-08-12 11:17:45 +03:00
|
|
|
const ulint IBUF_CONTRACT_DO_NOT_INSERT = 10;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* 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,
|
|
|
|
because ibuf merge is done to a page when it is read in, and it is
|
|
|
|
still physically like the index page even if the index would have been
|
|
|
|
dropped! So, there seems to be no problem. */
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Sets the flag in the current mini-transaction record indicating we're
|
|
|
|
inside an insert buffer routine. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
ibuf_enter(
|
|
|
|
/*=======*/
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(!mtr->is_inside_ibuf());
|
|
|
|
mtr->enter_ibuf();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Sets the flag in the current mini-transaction record indicating we're
|
|
|
|
exiting an insert buffer routine. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
ibuf_exit(
|
|
|
|
/*======*/
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr->is_inside_ibuf());
|
|
|
|
mtr->exit_ibuf();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Commits an insert buffer mini-transaction and sets the persistent
|
|
|
|
cursor latch mode to BTR_NO_LATCHES, that is, detaches the cursor. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
ibuf_btr_pcur_commit_specify_mtr(
|
|
|
|
/*=============================*/
|
|
|
|
btr_pcur_t* pcur, /*!< in/out: persistent cursor */
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
|
|
|
ut_d(ibuf_exit(mtr));
|
|
|
|
btr_pcur_commit_specify_mtr(pcur, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Gets the ibuf header page and x-latches it.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return insert buffer header page */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
page_t*
|
|
|
|
ibuf_header_page_get(
|
|
|
|
/*=================*/
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
|
|
|
buf_block_t* block;
|
|
|
|
|
|
|
|
ut_ad(!ibuf_inside(mtr));
|
2016-02-17 12:32:07 +02:00
|
|
|
page_t* page = NULL;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
block = buf_page_get(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id_t(IBUF_SPACE_ID, FSP_IBUF_HEADER_PAGE_NO),
|
2019-02-06 19:50:11 +02:00
|
|
|
0, RW_X_LATCH, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-10-03 18:12:08 +05:30
|
|
|
if (block) {
|
2016-02-17 12:32:07 +02:00
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_HEADER);
|
|
|
|
page = buf_block_get_frame(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
return page;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
/** Acquire the change buffer root page.
|
|
|
|
@param[in,out] mtr mini-transaction
|
|
|
|
@return change buffer root page, SX-latched */
|
|
|
|
static buf_block_t *ibuf_tree_root_get(mtr_t *mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
buf_block_t* block;
|
|
|
|
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(mutex_own(&ibuf_mutex));
|
|
|
|
|
2019-11-14 14:49:20 +02:00
|
|
|
mtr_sx_lock_index(ibuf.index, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/* only segment list access is exclusive each other */
|
2014-02-26 19:11:54 +01:00
|
|
|
block = buf_page_get(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id_t(IBUF_SPACE_ID, FSP_IBUF_TREE_ROOT_PAGE_NO),
|
2019-02-06 19:50:11 +02:00
|
|
|
0, RW_SX_LATCH, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE_NEW);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ut_ad(page_get_space_id(block->frame) == IBUF_SPACE_ID);
|
|
|
|
ut_ad(page_get_page_no(block->frame) == FSP_IBUF_TREE_ROOT_PAGE_NO);
|
|
|
|
ut_ad(ibuf.empty == page_is_empty(block->frame));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
return block;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Closes insert buffer and frees the data structures. */
|
|
|
|
void
|
|
|
|
ibuf_close(void)
|
|
|
|
/*============*/
|
|
|
|
{
|
2019-07-03 16:05:34 +03:00
|
|
|
if (!ibuf.index) {
|
2018-04-29 09:41:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_free(&ibuf_pessimistic_insert_mutex);
|
|
|
|
|
|
|
|
mutex_free(&ibuf_mutex);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
dict_table_t* ibuf_table = ibuf.index->table;
|
|
|
|
rw_lock_free(&ibuf.index->lock);
|
|
|
|
dict_mem_index_free(ibuf.index);
|
2016-08-12 11:17:45 +03:00
|
|
|
dict_mem_table_free(ibuf_table);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index = NULL;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Updates the size information of the ibuf, assuming the segment size has not
|
|
|
|
changed. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_size_update(
|
|
|
|
/*=============*/
|
2016-08-12 11:17:45 +03:00
|
|
|
const page_t* root) /*!< in: ibuf tree root */
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ut_ad(mutex_own(&ibuf_mutex));
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.free_list_len = flst_get_len(root + PAGE_HEADER
|
2016-08-12 11:17:45 +03:00
|
|
|
+ PAGE_BTR_IBUF_FREE_LIST);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.height = 1 + btr_page_get_level(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* the '1 +' is the ibuf header page */
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.size = ibuf.seg_size - (1 + ibuf.free_list_len);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Creates the insert buffer data structure at a database startup and initializes
|
2016-02-17 12:32:07 +02:00
|
|
|
the data structures for the insert buffer.
|
|
|
|
@return DB_SUCCESS or failure */
|
|
|
|
dberr_t
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_init_at_db_start(void)
|
|
|
|
/*=======================*/
|
|
|
|
{
|
|
|
|
page_t* root;
|
|
|
|
ulint n_used;
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(!ibuf.index);
|
|
|
|
mtr_t mtr;
|
|
|
|
mtr.start();
|
|
|
|
compile_time_assert(IBUF_SPACE_ID == TRX_SYS_SPACE);
|
|
|
|
compile_time_assert(IBUF_SPACE_ID == 0);
|
2019-11-14 14:49:20 +02:00
|
|
|
mtr_x_lock_space(fil_system.sys_space, &mtr);
|
2019-12-03 10:26:53 +02:00
|
|
|
buf_block_t* header_page = buf_page_get(
|
|
|
|
page_id_t(IBUF_SPACE_ID, FSP_IBUF_HEADER_PAGE_NO),
|
|
|
|
0, RW_X_LATCH, &mtr);
|
2019-07-03 16:05:34 +03:00
|
|
|
|
|
|
|
if (!header_page) {
|
|
|
|
mtr.commit();
|
|
|
|
return DB_DECRYPTION_FAILED;
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* At startup we intialize ibuf to have a maximum of
|
|
|
|
CHANGE_BUFFER_DEFAULT_SIZE in terms of percentage of the
|
|
|
|
buffer pool size. Once ibuf struct is initialized this
|
|
|
|
value is updated with the user supplied size by calling
|
|
|
|
ibuf_max_size_update(). */
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.max_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
|
2014-02-26 19:11:54 +01:00
|
|
|
* CHANGE_BUFFER_DEFAULT_SIZE) / 100;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mutex_create(LATCH_ID_IBUF, &ibuf_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mutex_create(LATCH_ID_IBUF_PESSIMISTIC_INSERT,
|
|
|
|
&ibuf_pessimistic_insert_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mutex_enter(&ibuf_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:26:53 +02:00
|
|
|
fseg_n_reserved_pages(*header_page,
|
|
|
|
IBUF_HEADER + IBUF_TREE_SEG_HEADER
|
|
|
|
+ header_page->frame, &n_used, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_ad(n_used >= 2);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.seg_size = n_used;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
buf_block_t* block;
|
|
|
|
|
|
|
|
block = buf_page_get(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id_t(IBUF_SPACE_ID, FSP_IBUF_TREE_ROOT_PAGE_NO),
|
2019-02-06 19:50:11 +02:00
|
|
|
0, RW_X_LATCH, &mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
|
|
|
|
|
|
|
|
root = buf_block_get_frame(block);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ibuf_size_update(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.empty = page_is_empty(root);
|
2017-08-07 13:50:31 +03:00
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index = dict_mem_index_create(
|
2018-03-23 17:25:56 +02:00
|
|
|
dict_mem_table_create("innodb_change_buffer",
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
fil_system.sys_space, 1, 0, 0, 0),
|
2018-03-23 17:25:56 +02:00
|
|
|
"CLUST_IND",
|
2018-03-22 19:40:38 +02:00
|
|
|
DICT_CLUSTERED | DICT_IBUF, 1);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID;
|
|
|
|
ibuf.index->n_uniq = REC_MAX_N_FIELDS;
|
|
|
|
rw_lock_create(index_tree_rw_lock_key, &ibuf.index->lock,
|
2016-08-12 11:17:45 +03:00
|
|
|
SYNC_IBUF_INDEX_TREE);
|
2017-02-23 23:05:12 +02:00
|
|
|
#ifdef BTR_CUR_ADAPT
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index->search_info = btr_search_info_create(ibuf.index->heap);
|
2017-02-23 23:05:12 +02:00
|
|
|
#endif /* BTR_CUR_ADAPT */
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index->page = FSP_IBUF_TREE_ROOT_PAGE_NO;
|
|
|
|
ut_d(ibuf.index->cached = TRUE);
|
2019-10-19 15:16:47 +03:00
|
|
|
|
|
|
|
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
|
|
|
|
if (!ibuf_dump) {
|
2019-11-05 16:15:20 +01:00
|
|
|
return DB_SUCCESS;
|
2019-10-19 15:16:47 +03:00
|
|
|
}
|
|
|
|
ib::info() << "Dumping the change buffer";
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
if (DB_SUCCESS == btr_pcur_open_at_index_side(
|
2019-11-05 16:15:20 +01:00
|
|
|
true, ibuf.index, BTR_SEARCH_LEAF, &pcur,
|
2019-10-19 15:16:47 +03:00
|
|
|
true, 0, &mtr)) {
|
|
|
|
while (btr_pcur_move_to_next_user_rec(&pcur, &mtr)) {
|
|
|
|
rec_print_old(stderr, btr_pcur_get_rec(&pcur));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
ib::info() << "Dumped the change buffer";
|
|
|
|
#endif
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
return DB_SUCCESS;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Updates the max_size value for ibuf. */
|
|
|
|
void
|
|
|
|
ibuf_max_size_update(
|
|
|
|
/*=================*/
|
|
|
|
ulint new_val) /*!< in: new value in terms of
|
|
|
|
percentage of the buffer pool size */
|
|
|
|
{
|
2018-04-27 14:26:43 +03:00
|
|
|
ulint new_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
|
2014-02-26 19:11:54 +01:00
|
|
|
* new_val) / 100;
|
|
|
|
mutex_enter(&ibuf_mutex);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.max_size = new_size;
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
# ifdef UNIV_DEBUG
|
|
|
|
/** Gets the desired bits for a given page from a bitmap page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] page bitmap page
|
|
|
|
@param[in] page_id page id whose bits to get
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] bit IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ...
|
|
|
|
@param[in,out] mtr mini-transaction holding an x-latch on the
|
|
|
|
bitmap page
|
|
|
|
@return value of bits */
|
2019-02-06 19:50:11 +02:00
|
|
|
# define ibuf_bitmap_page_get_bits(page, page_id, zip_size, bit, mtr) \
|
|
|
|
ibuf_bitmap_page_get_bits_low(page, page_id, zip_size, \
|
2014-02-26 19:11:54 +01:00
|
|
|
MTR_MEMO_PAGE_X_FIX, mtr, bit)
|
|
|
|
# else /* UNIV_DEBUG */
|
|
|
|
/** Gets the desired bits for a given page from a bitmap page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] page bitmap page
|
|
|
|
@param[in] page_id page id whose bits to get
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] bit IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ...
|
|
|
|
@param[in,out] mtr mini-transaction holding an x-latch on the
|
|
|
|
bitmap page
|
|
|
|
@return value of bits */
|
2019-02-06 19:50:11 +02:00
|
|
|
# define ibuf_bitmap_page_get_bits(page, page_id, zip_size, bit, mtr) \
|
|
|
|
ibuf_bitmap_page_get_bits_low(page, page_id, zip_size, bit)
|
2014-02-26 19:11:54 +01:00
|
|
|
# endif /* UNIV_DEBUG */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Gets the desired bits for a given page from a bitmap page.
|
|
|
|
@param[in] page bitmap page
|
|
|
|
@param[in] page_id page id whose bits to get
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] latch_type MTR_MEMO_PAGE_X_FIX, MTR_MEMO_BUF_FIX, ...
|
|
|
|
@param[in,out] mtr mini-transaction holding latch_type on the
|
|
|
|
bitmap page
|
|
|
|
@param[in] bit IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ...
|
|
|
|
@return value of bits */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
ibuf_bitmap_page_get_bits_low(
|
2016-08-12 11:17:45 +03:00
|
|
|
const page_t* page,
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint zip_size,
|
2014-02-26 19:11:54 +01:00
|
|
|
#ifdef UNIV_DEBUG
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint latch_type,
|
|
|
|
mtr_t* mtr,
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint bit)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ulint byte_offset;
|
|
|
|
ulint bit_offset;
|
|
|
|
ulint map_byte;
|
|
|
|
ulint value;
|
2019-02-06 19:50:11 +02:00
|
|
|
const ulint size = zip_size ? zip_size : srv_page_size;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
ut_ad(ut_is_2pow(zip_size));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(bit < IBUF_BITS_PER_PAGE);
|
2018-04-30 15:46:09 +03:00
|
|
|
compile_time_assert(!(IBUF_BITS_PER_PAGE % 2));
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(page, latch_type));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
bit_offset = (page_id.page_no() & (size - 1))
|
2016-08-12 11:17:45 +03:00
|
|
|
* IBUF_BITS_PER_PAGE + bit;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
byte_offset = bit_offset / 8;
|
|
|
|
bit_offset = bit_offset % 8;
|
|
|
|
|
2018-04-27 13:49:25 +03:00
|
|
|
ut_ad(byte_offset + IBUF_BITMAP < srv_page_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
map_byte = mach_read_from_1(page + IBUF_BITMAP + byte_offset);
|
|
|
|
|
|
|
|
value = ut_bit_get_nth(map_byte, bit_offset);
|
|
|
|
|
|
|
|
if (bit == IBUF_BITMAP_FREE) {
|
|
|
|
ut_ad(bit_offset + 1 < 8);
|
|
|
|
|
|
|
|
value = value * 2 + ut_bit_get_nth(map_byte, bit_offset + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(value);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Sets the desired bit for a given page in a bitmap page.
|
2019-12-03 10:19:45 +02:00
|
|
|
@tparam bit IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ...
|
|
|
|
@param[in,out] block bitmap page
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] page_id page id whose bits to set
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] physical_size page size
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] val value to set
|
|
|
|
@param[in,out] mtr mtr containing an x-latch to the bitmap page */
|
2019-12-03 10:19:45 +02:00
|
|
|
template<ulint bit>
|
|
|
|
static void
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_bitmap_page_set_bits(
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* block,
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint physical_size,
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint val,
|
|
|
|
mtr_t* mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ulint byte_offset;
|
|
|
|
ulint bit_offset;
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
static_assert(bit < IBUF_BITS_PER_PAGE, "wrong bit");
|
2018-04-30 15:46:09 +03:00
|
|
|
compile_time_assert(!(IBUF_BITS_PER_PAGE % 2));
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_flagged(block, MTR_MEMO_PAGE_X_FIX));
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr->is_named_space(page_id.space()));
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
bit_offset = (page_id.page_no() % physical_size)
|
2016-08-12 11:17:45 +03:00
|
|
|
* IBUF_BITS_PER_PAGE + bit;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
byte_offset = bit_offset / 8;
|
|
|
|
bit_offset = bit_offset % 8;
|
|
|
|
|
2018-04-27 13:49:25 +03:00
|
|
|
ut_ad(byte_offset + IBUF_BITMAP < srv_page_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
byte* map_byte = &block->frame[IBUF_BITMAP + byte_offset];
|
|
|
|
byte b = *map_byte;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (bit == IBUF_BITMAP_FREE) {
|
|
|
|
ut_ad(bit_offset + 1 < 8);
|
|
|
|
ut_ad(val <= 3);
|
MDEV-21907: InnoDB: Enable -Wconversion on clang and GCC
The -Wconversion in GCC seems to be stricter than in clang.
GCC at least since version 4.4.7 issues truncation warnings for
assignments to bitfields, while clang 10 appears to only issue
warnings when the sizes in bytes rounded to the nearest integer
powers of 2 are different.
Before GCC 10.0.0, -Wconversion required more casts and would not
allow some operations, such as x<<=1 or x+=1 on a data type that
is narrower than int.
GCC 5 (but not GCC 4, GCC 6, or any later version) is complaining
about x|=y even when x and y are compatible types that are narrower
than int. Hence, we must rewrite some x|=y as
x=static_cast<byte>(x|y) or similar, or we must disable -Wconversion.
In GCC 6 and later, the warning for assigning wider to bitfields
that are narrower than 8, 16, or 32 bits can be suppressed by
applying a bitwise & with the exact bitmask of the bitfield.
For older GCC, we must disable -Wconversion for GCC 4 or 5 in such
cases.
The bitwise negation operator appears to promote short integers
to a wider type, and hence we must add explicit truncation casts
around them. Microsoft Visual C does not allow a static_cast to
truncate a constant, such as static_cast<byte>(1) truncating int.
Hence, we will use the constructor-style cast byte(~1) for such cases.
This has been tested at least with GCC 4.8.5, 5.4.0, 7.4.0, 9.2.1, 10.0.0,
clang 9.0.1, 10.0.0, and MSVC 14.22.27905 (Microsoft Visual Studio 2019)
on 64-bit and 32-bit targets (IA-32, AMD64, POWER 8, POWER 9, ARMv8).
2020-03-12 19:46:41 +02:00
|
|
|
b &= static_cast<byte>(~(3U << bit_offset));
|
|
|
|
b |= static_cast<byte>(((val & 2) >> 1) << bit_offset
|
|
|
|
| (val & 1) << (bit_offset + 1));
|
2014-02-26 19:11:54 +01:00
|
|
|
} else {
|
|
|
|
ut_ad(val <= 1);
|
MDEV-21907: InnoDB: Enable -Wconversion on clang and GCC
The -Wconversion in GCC seems to be stricter than in clang.
GCC at least since version 4.4.7 issues truncation warnings for
assignments to bitfields, while clang 10 appears to only issue
warnings when the sizes in bytes rounded to the nearest integer
powers of 2 are different.
Before GCC 10.0.0, -Wconversion required more casts and would not
allow some operations, such as x<<=1 or x+=1 on a data type that
is narrower than int.
GCC 5 (but not GCC 4, GCC 6, or any later version) is complaining
about x|=y even when x and y are compatible types that are narrower
than int. Hence, we must rewrite some x|=y as
x=static_cast<byte>(x|y) or similar, or we must disable -Wconversion.
In GCC 6 and later, the warning for assigning wider to bitfields
that are narrower than 8, 16, or 32 bits can be suppressed by
applying a bitwise & with the exact bitmask of the bitfield.
For older GCC, we must disable -Wconversion for GCC 4 or 5 in such
cases.
The bitwise negation operator appears to promote short integers
to a wider type, and hence we must add explicit truncation casts
around them. Microsoft Visual C does not allow a static_cast to
truncate a constant, such as static_cast<byte>(1) truncating int.
Hence, we will use the constructor-style cast byte(~1) for such cases.
This has been tested at least with GCC 4.8.5, 5.4.0, 7.4.0, 9.2.1, 10.0.0,
clang 9.0.1, 10.0.0, and MSVC 14.22.27905 (Microsoft Visual Studio 2019)
on 64-bit and 32-bit targets (IA-32, AMD64, POWER 8, POWER 9, ARMv8).
2020-03-12 19:46:41 +02:00
|
|
|
b &= static_cast<byte>(~(1U << bit_offset));
|
2020-03-13 06:55:00 +02:00
|
|
|
#if defined __GNUC__ && !defined __clang__ && __GNUC__ < 6
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
# pragma GCC diagnostic ignored "-Wconversion" /* GCC 5 may need this here */
|
|
|
|
#endif
|
MDEV-21907: InnoDB: Enable -Wconversion on clang and GCC
The -Wconversion in GCC seems to be stricter than in clang.
GCC at least since version 4.4.7 issues truncation warnings for
assignments to bitfields, while clang 10 appears to only issue
warnings when the sizes in bytes rounded to the nearest integer
powers of 2 are different.
Before GCC 10.0.0, -Wconversion required more casts and would not
allow some operations, such as x<<=1 or x+=1 on a data type that
is narrower than int.
GCC 5 (but not GCC 4, GCC 6, or any later version) is complaining
about x|=y even when x and y are compatible types that are narrower
than int. Hence, we must rewrite some x|=y as
x=static_cast<byte>(x|y) or similar, or we must disable -Wconversion.
In GCC 6 and later, the warning for assigning wider to bitfields
that are narrower than 8, 16, or 32 bits can be suppressed by
applying a bitwise & with the exact bitmask of the bitfield.
For older GCC, we must disable -Wconversion for GCC 4 or 5 in such
cases.
The bitwise negation operator appears to promote short integers
to a wider type, and hence we must add explicit truncation casts
around them. Microsoft Visual C does not allow a static_cast to
truncate a constant, such as static_cast<byte>(1) truncating int.
Hence, we will use the constructor-style cast byte(~1) for such cases.
This has been tested at least with GCC 4.8.5, 5.4.0, 7.4.0, 9.2.1, 10.0.0,
clang 9.0.1, 10.0.0, and MSVC 14.22.27905 (Microsoft Visual Studio 2019)
on 64-bit and 32-bit targets (IA-32, AMD64, POWER 8, POWER 9, ARMv8).
2020-03-12 19:46:41 +02:00
|
|
|
b |= static_cast<byte>(val << bit_offset);
|
2020-03-13 06:55:00 +02:00
|
|
|
#if defined __GNUC__ && !defined __clang__ && __GNUC__ < 6
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
#endif
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 19:34:34 +03:00
|
|
|
mtr->write<1,mtr_t::MAYBE_NOP>(*block, map_byte, b);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Calculates the bitmap page number for a given page number.
|
|
|
|
@param[in] page_id page id
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] size page size
|
2016-08-12 11:17:45 +03:00
|
|
|
@return the bitmap page id where the file page is mapped */
|
2019-02-06 19:50:11 +02:00
|
|
|
inline page_id_t ibuf_bitmap_page_no_calc(const page_id_t page_id, ulint size)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2020-10-15 16:28:19 +03:00
|
|
|
if (!size)
|
|
|
|
size= srv_page_size;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
return page_id_t(page_id.space(), FSP_IBUF_BITMAP_OFFSET
|
|
|
|
+ uint32_t(page_id.page_no() & ~(size - 1)));
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Gets the ibuf bitmap page where the bits describing a given file page are
|
2014-02-26 19:11:54 +01:00
|
|
|
stored.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] page_id page id of the file page
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] file file name
|
|
|
|
@param[in] line line where called
|
|
|
|
@param[in,out] mtr mini-transaction
|
2014-02-26 19:11:54 +01:00
|
|
|
@return bitmap page where the file page is mapped, that is, the bitmap
|
|
|
|
page containing the descriptor bits for the file page; the bitmap page
|
|
|
|
is x-latched */
|
|
|
|
static
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t*
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_bitmap_get_map_page_func(
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint zip_size,
|
2016-08-12 11:17:45 +03:00
|
|
|
const char* file,
|
2017-03-01 08:27:39 +02:00
|
|
|
unsigned line,
|
2016-08-12 11:17:45 +03:00
|
|
|
mtr_t* mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2021-03-01 23:07:12 +05:30
|
|
|
buf_block_t* block = buf_page_get_gen(
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ibuf_bitmap_page_no_calc(page_id, zip_size),
|
2021-03-01 23:07:12 +05:30
|
|
|
zip_size, RW_X_LATCH, NULL, BUF_GET_POSSIBLY_FREED,
|
|
|
|
file, line, mtr);
|
2015-09-24 14:02:18 +03:00
|
|
|
|
2021-03-01 23:07:12 +05:30
|
|
|
if (block) {
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_BITMAP);
|
2015-09-24 14:02:18 +03:00
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
return block;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Gets the ibuf bitmap page where the bits describing a given file page are
|
2014-02-26 19:11:54 +01:00
|
|
|
stored.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] page_id page id of the file page
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in,out] mtr mini-transaction
|
2014-02-26 19:11:54 +01:00
|
|
|
@return bitmap page where the file page is mapped, that is, the bitmap
|
|
|
|
page containing the descriptor bits for the file page; the bitmap page
|
2016-08-12 11:17:45 +03:00
|
|
|
is x-latched */
|
2019-02-06 19:50:11 +02:00
|
|
|
#define ibuf_bitmap_get_map_page(page_id, zip_size, mtr) \
|
|
|
|
ibuf_bitmap_get_map_page_func(page_id, zip_size, \
|
2014-02-26 19:11:54 +01:00
|
|
|
__FILE__, __LINE__, mtr)
|
|
|
|
|
|
|
|
/************************************************************************//**
|
|
|
|
Sets the free bits of the page in the ibuf bitmap. This is done in a separate
|
|
|
|
mini-transaction, hence this operation does not restrict further work to only
|
|
|
|
ibuf bitmap operations, which would result if the latch to the bitmap page
|
|
|
|
were kept. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
ibuf_set_free_bits_low(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* block, /*!< in: index page; free bits are set if
|
|
|
|
the index is non-clustered and page
|
|
|
|
level is 0 */
|
|
|
|
ulint val, /*!< in: value to set: < 4 */
|
|
|
|
mtr_t* mtr) /*!< in/out: mtr */
|
|
|
|
{
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(mtr->is_named_space(block->page.id().space()));
|
2019-12-03 10:19:45 +02:00
|
|
|
if (!page_is_leaf(block->frame)) {
|
2016-09-26 12:29:31 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ut_a(val <= ibuf_index_page_calc_free(block));
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_IBUF_DEBUG */
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
const page_id_t id(block->page.id());
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2021-03-01 23:07:12 +05:30
|
|
|
if (buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(
|
|
|
|
id, block->zip_size(), mtr)) {
|
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_FREE>(
|
|
|
|
bitmap_page, id, block->physical_size(),
|
|
|
|
val, mtr);
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************//**
|
|
|
|
Sets the free bit of the page in the ibuf bitmap. This is done in a separate
|
|
|
|
mini-transaction, hence this operation does not restrict further work to only
|
|
|
|
ibuf bitmap operations, which would result if the latch to the bitmap page
|
|
|
|
were kept. */
|
|
|
|
void
|
|
|
|
ibuf_set_free_bits_func(
|
|
|
|
/*====================*/
|
|
|
|
buf_block_t* block, /*!< in: index page of a non-clustered index;
|
|
|
|
free bit is reset if page level is 0 */
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ulint max_val,/*!< in: ULINT_UNDEFINED or a maximum
|
|
|
|
value which the bits must have before
|
|
|
|
setting; this is for debugging */
|
|
|
|
#endif /* UNIV_IBUF_DEBUG */
|
|
|
|
ulint val) /*!< in: value to set: < 4 */
|
|
|
|
{
|
2019-12-03 10:19:45 +02:00
|
|
|
if (!page_is_leaf(block->frame)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
mtr_t mtr;
|
|
|
|
mtr.start();
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
const page_id_t id(block->page.id());
|
|
|
|
|
|
|
|
const fil_space_t* space = mtr.set_named_space_id(id.space());
|
2016-08-12 11:17:45 +03:00
|
|
|
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(id,
|
2019-12-03 10:19:45 +02:00
|
|
|
block->zip_size(),
|
|
|
|
&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
if (space->purpose != FIL_TYPE_TABLESPACE) {
|
|
|
|
mtr.set_log_mode(MTR_LOG_NO_REDO);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
if (max_val != ULINT_UNDEFINED) {
|
|
|
|
ulint old_val;
|
|
|
|
|
|
|
|
old_val = ibuf_bitmap_page_get_bits(
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
bitmap_page, id,
|
2014-02-26 19:11:54 +01:00
|
|
|
IBUF_BITMAP_FREE, &mtr);
|
|
|
|
ut_a(old_val <= max_val);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_a(val <= ibuf_index_page_calc_free(block));
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_IBUF_DEBUG */
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_FREE>(
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
bitmap_page, id, block->physical_size(),
|
2019-12-03 10:19:45 +02:00
|
|
|
val, &mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************//**
|
|
|
|
Resets the free bits of the page in the ibuf bitmap. This is done in a
|
|
|
|
separate mini-transaction, hence this operation does not restrict
|
|
|
|
further work to only ibuf bitmap operations, which would result if the
|
|
|
|
latch to the bitmap page were kept. NOTE: The free bits in the insert
|
|
|
|
buffer bitmap must never exceed the free space on a page. It is safe
|
|
|
|
to decrement or reset the bits in the bitmap in a mini-transaction
|
|
|
|
that is committed before the mini-transaction that affects the free
|
|
|
|
space. */
|
|
|
|
void
|
|
|
|
ibuf_reset_free_bits(
|
|
|
|
/*=================*/
|
|
|
|
buf_block_t* block) /*!< in: index page; free bits are set to 0
|
|
|
|
if the index is a non-clustered
|
|
|
|
non-unique, and page level is 0 */
|
|
|
|
{
|
|
|
|
ibuf_set_free_bits(block, 0, ULINT_UNDEFINED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************//**
|
|
|
|
Updates the free bits for an uncompressed page to reflect the present
|
|
|
|
state. Does this in the mtr given, which means that the latching
|
|
|
|
order rules virtually prevent any further operations for this OS
|
|
|
|
thread until mtr is committed. NOTE: The free bits in the insert
|
|
|
|
buffer bitmap must never exceed the free space on a page. It is safe
|
|
|
|
to set the free bits in the same mini-transaction that updated the
|
|
|
|
page. */
|
|
|
|
void
|
|
|
|
ibuf_update_free_bits_low(
|
|
|
|
/*======================*/
|
|
|
|
const buf_block_t* block, /*!< in: index page */
|
|
|
|
ulint max_ins_size, /*!< in: value of
|
|
|
|
maximum insert size
|
|
|
|
with reorganize before
|
|
|
|
the latest operation
|
|
|
|
performed to the page */
|
|
|
|
mtr_t* mtr) /*!< in/out: mtr */
|
|
|
|
{
|
|
|
|
ulint before;
|
|
|
|
ulint after;
|
|
|
|
|
2019-08-08 22:53:33 +03:00
|
|
|
ut_a(!is_buf_block_get_page_zip(block));
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(mtr->is_named_space(block->page.id().space()));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
before = ibuf_index_page_calc_free_bits(srv_page_size,
|
2016-08-12 11:17:45 +03:00
|
|
|
max_ins_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
after = ibuf_index_page_calc_free(block);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* This approach cannot be used on compressed pages, since the
|
|
|
|
computed value of "before" often does not match the current
|
|
|
|
state of the bitmap. This is because the free space may
|
|
|
|
increase or decrease when a compressed page is reorganized. */
|
|
|
|
if (before != after) {
|
2016-08-12 11:17:45 +03:00
|
|
|
ibuf_set_free_bits_low(block, after, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************//**
|
|
|
|
Updates the free bits for a compressed page to reflect the present
|
|
|
|
state. Does this in the mtr given, which means that the latching
|
|
|
|
order rules virtually prevent any further operations for this OS
|
|
|
|
thread until mtr is committed. NOTE: The free bits in the insert
|
|
|
|
buffer bitmap must never exceed the free space on a page. It is safe
|
|
|
|
to set the free bits in the same mini-transaction that updated the
|
|
|
|
page. */
|
|
|
|
void
|
|
|
|
ibuf_update_free_bits_zip(
|
|
|
|
/*======================*/
|
|
|
|
buf_block_t* block, /*!< in/out: index page */
|
|
|
|
mtr_t* mtr) /*!< in/out: mtr */
|
|
|
|
{
|
2019-12-03 10:19:45 +02:00
|
|
|
ut_ad(page_is_leaf(block->frame));
|
|
|
|
ut_ad(block->zip_size());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ulint after = ibuf_index_page_calc_free_zip(block);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (after == 0) {
|
|
|
|
/* We move the page to the front of the buffer pool LRU list:
|
|
|
|
the purpose of this is to prevent those pages to which we
|
|
|
|
cannot make inserts using the insert buffer from slipping
|
|
|
|
out of the buffer pool */
|
|
|
|
|
|
|
|
buf_page_make_young(&block->page);
|
|
|
|
}
|
|
|
|
|
2021-03-01 23:07:12 +05:30
|
|
|
if (buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(
|
|
|
|
block->page.id(), block->zip_size(), mtr)) {
|
|
|
|
|
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_FREE>(
|
|
|
|
bitmap_page, block->page.id(),
|
|
|
|
block->physical_size(), after, mtr);
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************//**
|
|
|
|
Updates the free bits for the two pages to reflect the present state.
|
|
|
|
Does this in the mtr given, which means that the latching order rules
|
|
|
|
virtually prevent any further operations until mtr is committed.
|
|
|
|
NOTE: The free bits in the insert buffer bitmap must never exceed the
|
|
|
|
free space on a page. It is safe to set the free bits in the same
|
|
|
|
mini-transaction that updated the pages. */
|
|
|
|
void
|
|
|
|
ibuf_update_free_bits_for_two_pages_low(
|
|
|
|
/*====================================*/
|
|
|
|
buf_block_t* block1, /*!< in: index page */
|
|
|
|
buf_block_t* block2, /*!< in: index page */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
2022-04-21 15:33:50 +03:00
|
|
|
ut_ad(mtr->is_named_space(block1->page.id().space()));
|
|
|
|
ut_ad(block1->page.id().space() == block2->page.id().space());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2022-04-21 09:15:18 +03:00
|
|
|
/* Avoid deadlocks by acquiring multiple bitmap page latches in
|
|
|
|
a consistent order (smaller pointer first). */
|
|
|
|
if (block1 > block2)
|
|
|
|
std::swap(block1, block2);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2022-04-21 09:15:18 +03:00
|
|
|
ibuf_set_free_bits_low(block1, ibuf_index_page_calc_free(block1), mtr);
|
|
|
|
ibuf_set_free_bits_low(block2, ibuf_index_page_calc_free(block2), mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Returns TRUE if the page is one of the fixed address ibuf pages.
|
|
|
|
@param[in] page_id page id
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if a fixed address ibuf i/o page */
|
2019-02-06 19:50:11 +02:00
|
|
|
inline bool ibuf_fixed_addr_page(const page_id_t page_id, ulint zip_size)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2020-10-15 13:21:52 +03:00
|
|
|
return(page_id == page_id_t(IBUF_SPACE_ID, IBUF_TREE_ROOT_PAGE_NO)
|
2019-02-06 19:50:11 +02:00
|
|
|
|| ibuf_bitmap_page(page_id, zip_size));
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Checks if a page is a level 2 or 3 page in the ibuf hierarchy of pages.
|
|
|
|
Must not be called when recv_no_ibuf_operations==true.
|
|
|
|
@param[in] page_id page id
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] x_latch FALSE if relaxed check (avoid latching the
|
|
|
|
bitmap page)
|
|
|
|
@param[in] file file name
|
|
|
|
@param[in] line line where called
|
|
|
|
@param[in,out] mtr mtr which will contain an x-latch to the
|
|
|
|
bitmap page if the page is not one of the fixed address ibuf pages, or NULL,
|
|
|
|
in which case a new transaction is created.
|
|
|
|
@return TRUE if level 2 or level 3 page */
|
2019-02-06 19:50:11 +02:00
|
|
|
bool
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_page_low(
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint zip_size,
|
2014-02-26 19:11:54 +01:00
|
|
|
#ifdef UNIV_DEBUG
|
2019-02-06 19:50:11 +02:00
|
|
|
bool x_latch,
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
2016-08-12 11:17:45 +03:00
|
|
|
const char* file,
|
2017-03-01 08:27:39 +02:00
|
|
|
unsigned line,
|
2016-08-12 11:17:45 +03:00
|
|
|
mtr_t* mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ibool ret;
|
|
|
|
mtr_t local_mtr;
|
|
|
|
|
|
|
|
ut_ad(!recv_no_ibuf_operations);
|
|
|
|
ut_ad(x_latch || mtr == NULL);
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
if (ibuf_fixed_addr_page(page_id, zip_size)) {
|
|
|
|
return(true);
|
2016-08-12 11:17:45 +03:00
|
|
|
} else if (page_id.space() != IBUF_SPACE_ID) {
|
2019-02-06 19:50:11 +02:00
|
|
|
return(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-03-28 09:29:14 +03:00
|
|
|
compile_time_assert(IBUF_SPACE_ID == 0);
|
|
|
|
ut_ad(fil_system.sys_space->purpose == FIL_TYPE_TABLESPACE);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
if (!x_latch) {
|
|
|
|
mtr_start(&local_mtr);
|
|
|
|
|
|
|
|
/* Get the bitmap page without a page latch, so that
|
|
|
|
we will not be violating the latching order when
|
|
|
|
another bitmap page has already been latched by this
|
|
|
|
thread. The page will be buffer-fixed, and thus it
|
|
|
|
cannot be removed or relocated while we are looking at
|
|
|
|
it. The contents of the page could change, but the
|
|
|
|
IBUF_BITMAP_IBUF bit that we are interested in should
|
|
|
|
not be modified by any other thread. Nobody should be
|
|
|
|
calling ibuf_add_free_page() or ibuf_remove_free_page()
|
|
|
|
while the page is linked to the insert buffer b-tree. */
|
2016-08-12 11:17:45 +03:00
|
|
|
dberr_t err = DB_SUCCESS;
|
|
|
|
|
|
|
|
buf_block_t* block = buf_page_get_gen(
|
2019-02-06 19:50:11 +02:00
|
|
|
ibuf_bitmap_page_no_calc(page_id, zip_size),
|
|
|
|
zip_size, RW_NO_LATCH, NULL, BUF_GET_NO_LATCH,
|
|
|
|
file, line, &local_mtr, &err);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ret = ibuf_bitmap_page_get_bits_low(
|
2019-12-03 10:19:45 +02:00
|
|
|
block->frame, page_id, zip_size,
|
2014-02-26 19:11:54 +01:00
|
|
|
MTR_MEMO_BUF_FIX, &local_mtr, IBUF_BITMAP_IBUF);
|
|
|
|
|
|
|
|
mtr_commit(&local_mtr);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
if (mtr == NULL) {
|
|
|
|
mtr = &local_mtr;
|
|
|
|
mtr_start(mtr);
|
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ret = ibuf_bitmap_page_get_bits(ibuf_bitmap_get_map_page_func(
|
|
|
|
page_id, zip_size, file, line,
|
|
|
|
mtr)->frame,
|
|
|
|
page_id, zip_size,
|
2014-02-26 19:11:54 +01:00
|
|
|
IBUF_BITMAP_IBUF, mtr);
|
|
|
|
|
|
|
|
if (mtr == &local_mtr) {
|
|
|
|
mtr_commit(mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_rec_get_page_no(mtr,rec) ibuf_rec_get_page_no_func(mtr,rec)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_rec_get_page_no(mtr,rec) ibuf_rec_get_page_no_func(rec)
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Returns the page number field of an ibuf record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return page number */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_rec_get_page_no_func(
|
|
|
|
/*======================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec) /*!< in: ibuf record */
|
|
|
|
{
|
|
|
|
const byte* field;
|
|
|
|
ulint len;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(rec_get_n_fields_old(rec) > 2);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
|
|
|
|
ut_a(len == 1);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_PAGE, &len);
|
|
|
|
|
|
|
|
ut_a(len == 4);
|
|
|
|
|
|
|
|
return(mach_read_from_4(field));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_rec_get_space(mtr,rec) ibuf_rec_get_space_func(mtr,rec)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_rec_get_space(mtr,rec) ibuf_rec_get_space_func(rec)
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Returns the space id field of an ibuf record. For < 4.1.x format records
|
|
|
|
returns 0.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return space id */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_rec_get_space_func(
|
|
|
|
/*====================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec) /*!< in: ibuf record */
|
|
|
|
{
|
|
|
|
const byte* field;
|
|
|
|
ulint len;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(rec_get_n_fields_old(rec) > 2);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
|
|
|
|
ut_a(len == 1);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
|
|
|
|
|
|
|
|
ut_a(len == 4);
|
|
|
|
|
|
|
|
return(mach_read_from_4(field));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_rec_get_info(mtr,rec,op,comp,info_len,counter) \
|
|
|
|
ibuf_rec_get_info_func(mtr,rec,op,comp,info_len,counter)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_rec_get_info(mtr,rec,op,comp,info_len,counter) \
|
|
|
|
ibuf_rec_get_info_func(rec,op,comp,info_len,counter)
|
|
|
|
#endif
|
|
|
|
/****************************************************************//**
|
|
|
|
Get various information about an ibuf record in >= 4.1.x format. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_rec_get_info_func(
|
|
|
|
/*===================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec, /*!< in: ibuf record */
|
|
|
|
ibuf_op_t* op, /*!< out: operation type, or NULL */
|
|
|
|
ibool* comp, /*!< out: compact flag, or NULL */
|
|
|
|
ulint* info_len, /*!< out: length of info fields at the
|
|
|
|
start of the fourth field, or
|
|
|
|
NULL */
|
|
|
|
ulint* counter) /*!< in: counter value, or NULL */
|
|
|
|
{
|
|
|
|
const byte* types;
|
|
|
|
ulint fields;
|
|
|
|
ulint len;
|
|
|
|
|
|
|
|
/* Local variables to shadow arguments. */
|
|
|
|
ibuf_op_t op_local;
|
|
|
|
ibool comp_local;
|
|
|
|
ulint info_len_local;
|
|
|
|
ulint counter_local;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
fields = rec_get_n_fields_old(rec);
|
|
|
|
ut_a(fields > IBUF_REC_FIELD_USER);
|
|
|
|
|
|
|
|
types = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
|
|
|
info_len_local = len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
|
2018-04-30 15:46:09 +03:00
|
|
|
compile_time_assert(IBUF_REC_INFO_SIZE
|
|
|
|
< DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
switch (info_len_local) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
op_local = IBUF_OP_INSERT;
|
|
|
|
comp_local = info_len_local;
|
|
|
|
ut_ad(!counter);
|
|
|
|
counter_local = ULINT_UNDEFINED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IBUF_REC_INFO_SIZE:
|
|
|
|
op_local = (ibuf_op_t) types[IBUF_REC_OFFSET_TYPE];
|
|
|
|
comp_local = types[IBUF_REC_OFFSET_FLAGS] & IBUF_REC_COMPACT;
|
|
|
|
counter_local = mach_read_from_2(
|
|
|
|
types + IBUF_REC_OFFSET_COUNTER);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(op_local < IBUF_OP_COUNT);
|
|
|
|
ut_a((len - info_len_local) ==
|
|
|
|
(fields - IBUF_REC_FIELD_USER)
|
|
|
|
* DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
|
|
|
|
|
|
|
|
if (op) {
|
|
|
|
*op = op_local;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (comp) {
|
|
|
|
*comp = comp_local;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info_len) {
|
|
|
|
*info_len = info_len_local;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (counter) {
|
|
|
|
*counter = counter_local;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_rec_get_op_type(mtr,rec) ibuf_rec_get_op_type_func(mtr,rec)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_rec_get_op_type(mtr,rec) ibuf_rec_get_op_type_func(rec)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************//**
|
|
|
|
Returns the operation type field of an ibuf record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return operation type */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
ibuf_op_t
|
|
|
|
ibuf_rec_get_op_type_func(
|
|
|
|
/*======================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec) /*!< in: ibuf record */
|
|
|
|
{
|
|
|
|
ulint len;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(rec_get_n_fields_old(rec) > 2);
|
|
|
|
|
|
|
|
(void) rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
|
|
|
|
if (len > 1) {
|
|
|
|
/* This is a < 4.1.x format record */
|
|
|
|
|
|
|
|
return(IBUF_OP_INSERT);
|
|
|
|
} else {
|
|
|
|
ibuf_op_t op;
|
|
|
|
|
|
|
|
ibuf_rec_get_info(mtr, rec, &op, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
return(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************//**
|
|
|
|
Read the first two bytes from a record's fourth field (counter field in new
|
|
|
|
records; something else in older records).
|
|
|
|
@return "counter" field, or ULINT_UNDEFINED if for some reason it
|
|
|
|
can't be read */
|
|
|
|
ulint
|
|
|
|
ibuf_rec_get_counter(
|
|
|
|
/*=================*/
|
|
|
|
const rec_t* rec) /*!< in: ibuf record */
|
|
|
|
{
|
|
|
|
const byte* ptr;
|
|
|
|
ulint len;
|
|
|
|
|
|
|
|
if (rec_get_n_fields_old(rec) <= IBUF_REC_FIELD_METADATA) {
|
|
|
|
|
|
|
|
return(ULINT_UNDEFINED);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
|
|
|
if (len >= 2) {
|
|
|
|
|
|
|
|
return(mach_read_from_2(ptr));
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return(ULINT_UNDEFINED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-28 21:00:23 +04:00
|
|
|
/**
|
|
|
|
Add accumulated operation counts to a permanent array.
|
|
|
|
Both arrays must be of size IBUF_OP_COUNT.
|
|
|
|
*/
|
|
|
|
static void ibuf_add_ops(Atomic_counter<ulint> *out, const ulint *in)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2018-12-28 21:00:23 +04:00
|
|
|
for (auto i = 0; i < IBUF_OP_COUNT; i++)
|
|
|
|
out[i]+= in[i];
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-12-28 21:00:23 +04:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/****************************************************************//**
|
|
|
|
Print operation counts. The array must be of size IBUF_OP_COUNT. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_print_ops(
|
|
|
|
/*===========*/
|
2018-12-28 21:00:23 +04:00
|
|
|
const Atomic_counter<ulint>* ops, /*!< in: operation counts */
|
|
|
|
FILE* file) /*!< in: file where to print */
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
static const char* op_names[] = {
|
|
|
|
"insert",
|
|
|
|
"delete mark",
|
|
|
|
"delete"
|
|
|
|
};
|
|
|
|
ulint i;
|
|
|
|
|
|
|
|
ut_a(UT_ARR_SIZE(op_names) == IBUF_OP_COUNT);
|
|
|
|
|
|
|
|
for (i = 0; i < IBUF_OP_COUNT; i++) {
|
2017-04-21 05:51:27 +03:00
|
|
|
fprintf(file, "%s " ULINTPF "%s", op_names[i],
|
2018-12-28 21:00:23 +04:00
|
|
|
ulint{ops[i]}, (i < (IBUF_OP_COUNT - 1)) ? ", " : "");
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
putc('\n', file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Creates a dummy index for inserting a record to a non-clustered index.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return dummy index */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
dict_index_t*
|
|
|
|
ibuf_dummy_index_create(
|
|
|
|
/*====================*/
|
|
|
|
ulint n, /*!< in: number of fields */
|
|
|
|
ibool comp) /*!< in: TRUE=use compact record format */
|
|
|
|
{
|
|
|
|
dict_table_t* table;
|
|
|
|
dict_index_t* index;
|
|
|
|
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
table = dict_mem_table_create("IBUF_DUMMY", NULL, n, 0,
|
2014-02-26 19:11:54 +01:00
|
|
|
comp ? DICT_TF_COMPACT : 0, 0);
|
|
|
|
|
2018-03-23 17:25:56 +02:00
|
|
|
index = dict_mem_index_create(table, "IBUF_DUMMY", 0, n);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */
|
|
|
|
index->cached = TRUE;
|
2017-09-19 19:20:11 +03:00
|
|
|
ut_d(index->is_dummy = true);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(index);
|
|
|
|
}
|
|
|
|
/********************************************************************//**
|
|
|
|
Add a column to the dummy index */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_dummy_index_add_col(
|
|
|
|
/*=====================*/
|
|
|
|
dict_index_t* index, /*!< in: dummy index */
|
|
|
|
const dtype_t* type, /*!< in: the data type of the column */
|
|
|
|
ulint len) /*!< in: length of the column */
|
|
|
|
{
|
|
|
|
ulint i = index->table->n_def;
|
|
|
|
dict_mem_table_add_col(index->table, NULL, NULL,
|
|
|
|
dtype_get_mtype(type),
|
|
|
|
dtype_get_prtype(type),
|
|
|
|
dtype_get_len(type));
|
|
|
|
dict_index_add_col(index, index->table,
|
|
|
|
dict_table_get_nth_col(index->table, i), len);
|
|
|
|
}
|
|
|
|
/********************************************************************//**
|
|
|
|
Deallocates a dummy index for inserting a record to a non-clustered index. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_dummy_index_free(
|
|
|
|
/*==================*/
|
|
|
|
dict_index_t* index) /*!< in, own: dummy index */
|
|
|
|
{
|
|
|
|
dict_table_t* table = index->table;
|
|
|
|
|
|
|
|
dict_mem_index_free(index);
|
|
|
|
dict_mem_table_free(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_build_entry_from_ibuf_rec(mtr,ibuf_rec,heap,pindex) \
|
|
|
|
ibuf_build_entry_from_ibuf_rec_func(mtr,ibuf_rec,heap,pindex)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_build_entry_from_ibuf_rec(mtr,ibuf_rec,heap,pindex) \
|
|
|
|
ibuf_build_entry_from_ibuf_rec_func(ibuf_rec,heap,pindex)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Builds the entry used to
|
|
|
|
|
|
|
|
1) IBUF_OP_INSERT: insert into a non-clustered index
|
|
|
|
|
|
|
|
2) IBUF_OP_DELETE_MARK: find the record whose delete-mark flag we need to
|
|
|
|
activate
|
|
|
|
|
|
|
|
3) IBUF_OP_DELETE: find the record we need to delete
|
|
|
|
|
|
|
|
when we have the corresponding record in an ibuf index.
|
|
|
|
|
|
|
|
NOTE that as we copy pointers to fields in ibuf_rec, the caller must
|
|
|
|
hold a latch to the ibuf_rec page as long as the entry is used!
|
|
|
|
|
|
|
|
@return own: entry to insert to a non-clustered index */
|
|
|
|
static
|
|
|
|
dtuple_t*
|
|
|
|
ibuf_build_entry_from_ibuf_rec_func(
|
|
|
|
/*================================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* ibuf_rec, /*!< in: record in an insert buffer */
|
|
|
|
mem_heap_t* heap, /*!< in: heap where built */
|
|
|
|
dict_index_t** pindex) /*!< out, own: dummy index that
|
|
|
|
describes the entry */
|
|
|
|
{
|
|
|
|
dtuple_t* tuple;
|
|
|
|
dfield_t* field;
|
|
|
|
ulint n_fields;
|
|
|
|
const byte* types;
|
|
|
|
const byte* data;
|
|
|
|
ulint len;
|
|
|
|
ulint info_len;
|
|
|
|
ulint i;
|
|
|
|
ulint comp;
|
|
|
|
dict_index_t* index;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(ibuf_rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
|
|
|
|
data = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
|
|
|
|
ut_a(len == 1);
|
|
|
|
ut_a(*data == 0);
|
|
|
|
ut_a(rec_get_n_fields_old(ibuf_rec) > IBUF_REC_FIELD_USER);
|
|
|
|
|
|
|
|
n_fields = rec_get_n_fields_old(ibuf_rec) - IBUF_REC_FIELD_USER;
|
|
|
|
|
|
|
|
tuple = dtuple_create(heap, n_fields);
|
|
|
|
|
|
|
|
types = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
|
|
|
ibuf_rec_get_info(mtr, ibuf_rec, NULL, &comp, &info_len, NULL);
|
|
|
|
|
|
|
|
index = ibuf_dummy_index_create(n_fields, comp);
|
|
|
|
|
|
|
|
len -= info_len;
|
|
|
|
types += info_len;
|
|
|
|
|
|
|
|
ut_a(len == n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
|
|
|
|
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
|
|
field = dtuple_get_nth_field(tuple, i);
|
|
|
|
|
|
|
|
data = rec_get_nth_field_old(
|
|
|
|
ibuf_rec, i + IBUF_REC_FIELD_USER, &len);
|
|
|
|
|
|
|
|
dfield_set_data(field, data, len);
|
|
|
|
|
|
|
|
dtype_new_read_for_order_and_null_size(
|
|
|
|
dfield_get_type(field),
|
|
|
|
types + i * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
|
|
|
|
|
|
|
|
ibuf_dummy_index_add_col(index, dfield_get_type(field), len);
|
|
|
|
}
|
|
|
|
|
MDEV-21907: InnoDB: Enable -Wconversion on clang and GCC
The -Wconversion in GCC seems to be stricter than in clang.
GCC at least since version 4.4.7 issues truncation warnings for
assignments to bitfields, while clang 10 appears to only issue
warnings when the sizes in bytes rounded to the nearest integer
powers of 2 are different.
Before GCC 10.0.0, -Wconversion required more casts and would not
allow some operations, such as x<<=1 or x+=1 on a data type that
is narrower than int.
GCC 5 (but not GCC 4, GCC 6, or any later version) is complaining
about x|=y even when x and y are compatible types that are narrower
than int. Hence, we must rewrite some x|=y as
x=static_cast<byte>(x|y) or similar, or we must disable -Wconversion.
In GCC 6 and later, the warning for assigning wider to bitfields
that are narrower than 8, 16, or 32 bits can be suppressed by
applying a bitwise & with the exact bitmask of the bitfield.
For older GCC, we must disable -Wconversion for GCC 4 or 5 in such
cases.
The bitwise negation operator appears to promote short integers
to a wider type, and hence we must add explicit truncation casts
around them. Microsoft Visual C does not allow a static_cast to
truncate a constant, such as static_cast<byte>(1) truncating int.
Hence, we will use the constructor-style cast byte(~1) for such cases.
This has been tested at least with GCC 4.8.5, 5.4.0, 7.4.0, 9.2.1, 10.0.0,
clang 9.0.1, 10.0.0, and MSVC 14.22.27905 (Microsoft Visual Studio 2019)
on 64-bit and 32-bit targets (IA-32, AMD64, POWER 8, POWER 9, ARMv8).
2020-03-12 19:46:41 +02:00
|
|
|
index->n_core_null_bytes = static_cast<uint8_t>(
|
|
|
|
UT_BITS_IN_BYTES(unsigned(index->n_nullable)));
|
MDEV-11369 Instant ADD COLUMN for InnoDB
For InnoDB tables, adding, dropping and reordering columns has
required a rebuild of the table and all its indexes. Since MySQL 5.6
(and MariaDB 10.0) this has been supported online (LOCK=NONE), allowing
concurrent modification of the tables.
This work revises the InnoDB ROW_FORMAT=REDUNDANT, ROW_FORMAT=COMPACT
and ROW_FORMAT=DYNAMIC so that columns can be appended instantaneously,
with only minor changes performed to the table structure. The counter
innodb_instant_alter_column in INFORMATION_SCHEMA.GLOBAL_STATUS
is incremented whenever a table rebuild operation is converted into
an instant ADD COLUMN operation.
ROW_FORMAT=COMPRESSED tables will not support instant ADD COLUMN.
Some usability limitations will be addressed in subsequent work:
MDEV-13134 Introduce ALTER TABLE attributes ALGORITHM=NOCOPY
and ALGORITHM=INSTANT
MDEV-14016 Allow instant ADD COLUMN, ADD INDEX, LOCK=NONE
The format of the clustered index (PRIMARY KEY) is changed as follows:
(1) The FIL_PAGE_TYPE of the root page will be FIL_PAGE_TYPE_INSTANT,
and a new field PAGE_INSTANT will contain the original number of fields
in the clustered index ('core' fields).
If instant ADD COLUMN has not been used or the table becomes empty,
or the very first instant ADD COLUMN operation is rolled back,
the fields PAGE_INSTANT and FIL_PAGE_TYPE will be reset
to 0 and FIL_PAGE_INDEX.
(2) A special 'default row' record is inserted into the leftmost leaf,
between the page infimum and the first user record. This record is
distinguished by the REC_INFO_MIN_REC_FLAG, and it is otherwise in the
same format as records that contain values for the instantly added
columns. This 'default row' always has the same number of fields as
the clustered index according to the table definition. The values of
'core' fields are to be ignored. For other fields, the 'default row'
will contain the default values as they were during the ALTER TABLE
statement. (If the column default values are changed later, those
values will only be stored in the .frm file. The 'default row' will
contain the original evaluated values, which must be the same for
every row.) The 'default row' must be completely hidden from
higher-level access routines. Assertions have been added to ensure
that no 'default row' is ever present in the adaptive hash index
or in locked records. The 'default row' is never delete-marked.
(3) In clustered index leaf page records, the number of fields must
reside between the number of 'core' fields (dict_index_t::n_core_fields
introduced in this work) and dict_index_t::n_fields. If the number
of fields is less than dict_index_t::n_fields, the missing fields
are replaced with the column value of the 'default row'.
Note: The number of fields in the record may shrink if some of the
last instantly added columns are updated to the value that is
in the 'default row'. The function btr_cur_trim() implements this
'compression' on update and rollback; dtuple::trim() implements it
on insert.
(4) In ROW_FORMAT=COMPACT and ROW_FORMAT=DYNAMIC records, the new
status value REC_STATUS_COLUMNS_ADDED will indicate the presence of
a new record header that will encode n_fields-n_core_fields-1 in
1 or 2 bytes. (In ROW_FORMAT=REDUNDANT records, the record header
always explicitly encodes the number of fields.)
We introduce the undo log record type TRX_UNDO_INSERT_DEFAULT for
covering the insert of the 'default row' record when instant ADD COLUMN
is used for the first time. Subsequent instant ADD COLUMN can use
TRX_UNDO_UPD_EXIST_REC.
This is joint work with Vin Chen (陈福荣) from Tencent. The design
that was discussed in April 2017 would not have allowed import or
export of data files, because instead of the 'default row' it would
have introduced a data dictionary table. The test
rpl.rpl_alter_instant is exactly as contributed in pull request #408.
The test innodb.instant_alter is based on a contributed test.
The redo log record format changes for ROW_FORMAT=DYNAMIC and
ROW_FORMAT=COMPACT are as contributed. (With this change present,
crash recovery from MariaDB 10.3.1 will fail in spectacular ways!)
Also the semantics of higher-level redo log records that modify the
PAGE_INSTANT field is changed. The redo log format version identifier
was already changed to LOG_HEADER_FORMAT_CURRENT=103 in MariaDB 10.3.1.
Everything else has been rewritten by me. Thanks to Elena Stepanova,
the code has been tested extensively.
When rolling back an instant ADD COLUMN operation, we must empty the
PAGE_FREE list after deleting or shortening the 'default row' record,
by calling either btr_page_empty() or btr_page_reorganize(). We must
know the size of each entry in the PAGE_FREE list. If rollback left a
freed copy of the 'default row' in the PAGE_FREE list, we would be
unable to determine its size (if it is in ROW_FORMAT=COMPACT or
ROW_FORMAT=DYNAMIC) because it would contain more fields than the
rolled-back definition of the clustered index.
UNIV_SQL_DEFAULT: A new special constant that designates an instantly
added column that is not present in the clustered index record.
len_is_stored(): Check if a length is an actual length. There are
two magic length values: UNIV_SQL_DEFAULT, UNIV_SQL_NULL.
dict_col_t::def_val: The 'default row' value of the column. If the
column is not added instantly, def_val.len will be UNIV_SQL_DEFAULT.
dict_col_t: Add the accessors is_virtual(), is_nullable(), is_instant(),
instant_value().
dict_col_t::remove_instant(): Remove the 'instant ADD' status of
a column.
dict_col_t::name(const dict_table_t& table): Replaces
dict_table_get_col_name().
dict_index_t::n_core_fields: The original number of fields.
For secondary indexes and if instant ADD COLUMN has not been used,
this will be equal to dict_index_t::n_fields.
dict_index_t::n_core_null_bytes: Number of bytes needed to
represent the null flags; usually equal to UT_BITS_IN_BYTES(n_nullable).
dict_index_t::NO_CORE_NULL_BYTES: Magic value signalling that
n_core_null_bytes was not initialized yet from the clustered index
root page.
dict_index_t: Add the accessors is_instant(), is_clust(),
get_n_nullable(), instant_field_value().
dict_index_t::instant_add_field(): Adjust clustered index metadata
for instant ADD COLUMN.
dict_index_t::remove_instant(): Remove the 'instant ADD' status
of a clustered index when the table becomes empty, or the very first
instant ADD COLUMN operation is rolled back.
dict_table_t: Add the accessors is_instant(), is_temporary(),
supports_instant().
dict_table_t::instant_add_column(): Adjust metadata for
instant ADD COLUMN.
dict_table_t::rollback_instant(): Adjust metadata on the rollback
of instant ADD COLUMN.
prepare_inplace_alter_table_dict(): First create the ctx->new_table,
and only then decide if the table really needs to be rebuilt.
We must split the creation of table or index metadata from the
creation of the dictionary table records and the creation of
the data. In this way, we can transform a table-rebuilding operation
into an instant ADD COLUMN operation. Dictionary objects will only
be added to cache when table rebuilding or index creation is needed.
The ctx->instant_table will never be added to cache.
dict_table_t::add_to_cache(): Modified and renamed from
dict_table_add_to_cache(). Do not modify the table metadata.
Let the callers invoke dict_table_add_system_columns() and if needed,
set can_be_evicted.
dict_create_sys_tables_tuple(), dict_create_table_step(): Omit the
system columns (which will now exist in the dict_table_t object
already at this point).
dict_create_table_step(): Expect the callers to invoke
dict_table_add_system_columns().
pars_create_table(): Before creating the table creation execution
graph, invoke dict_table_add_system_columns().
row_create_table_for_mysql(): Expect all callers to invoke
dict_table_add_system_columns().
create_index_dict(): Replaces row_merge_create_index_graph().
innodb_update_n_cols(): Renamed from innobase_update_n_virtual().
Call my_error() if an error occurs.
btr_cur_instant_init(), btr_cur_instant_init_low(),
btr_cur_instant_root_init():
Load additional metadata from the clustered index and set
dict_index_t::n_core_null_bytes. This is invoked
when table metadata is first loaded into the data dictionary.
dict_boot(): Initialize n_core_null_bytes for the four hard-coded
dictionary tables.
dict_create_index_step(): Initialize n_core_null_bytes. This is
executed as part of CREATE TABLE.
dict_index_build_internal_clust(): Initialize n_core_null_bytes to
NO_CORE_NULL_BYTES if table->supports_instant().
row_create_index_for_mysql(): Initialize n_core_null_bytes for
CREATE TEMPORARY TABLE.
commit_cache_norebuild(): Call the code to rename or enlarge columns
in the cache only if instant ADD COLUMN is not being used.
(Instant ADD COLUMN would copy all column metadata from
instant_table to old_table, including the names and lengths.)
PAGE_INSTANT: A new 13-bit field for storing dict_index_t::n_core_fields.
This is repurposing the 16-bit field PAGE_DIRECTION, of which only the
least significant 3 bits were used. The original byte containing
PAGE_DIRECTION will be accessible via the new constant PAGE_DIRECTION_B.
page_get_instant(), page_set_instant(): Accessors for the PAGE_INSTANT.
page_ptr_get_direction(), page_get_direction(),
page_ptr_set_direction(): Accessors for PAGE_DIRECTION.
page_direction_reset(): Reset PAGE_DIRECTION, PAGE_N_DIRECTION.
page_direction_increment(): Increment PAGE_N_DIRECTION
and set PAGE_DIRECTION.
rec_get_offsets(): Use the 'leaf' parameter for non-debug purposes,
and assume that heap_no is always set.
Initialize all dict_index_t::n_fields for ROW_FORMAT=REDUNDANT records,
even if the record contains fewer fields.
rec_offs_make_valid(): Add the parameter 'leaf'.
rec_copy_prefix_to_dtuple(): Assert that the tuple is only built
on the core fields. Instant ADD COLUMN only applies to the
clustered index, and we should never build a search key that has
more than the PRIMARY KEY and possibly DB_TRX_ID,DB_ROLL_PTR.
All these columns are always present.
dict_index_build_data_tuple(): Remove assertions that would be
duplicated in rec_copy_prefix_to_dtuple().
rec_init_offsets(): Support ROW_FORMAT=REDUNDANT records whose
number of fields is between n_core_fields and n_fields.
cmp_rec_rec_with_match(): Implement the comparison between two
MIN_REC_FLAG records.
trx_t::in_rollback: Make the field available in non-debug builds.
trx_start_for_ddl_low(): Remove dangerous error-tolerance.
A dictionary transaction must be flagged as such before it has generated
any undo log records. This is because trx_undo_assign_undo() will mark
the transaction as a dictionary transaction in the undo log header
right before the very first undo log record is being written.
btr_index_rec_validate(): Account for instant ADD COLUMN
row_undo_ins_remove_clust_rec(): On the rollback of an insert into
SYS_COLUMNS, revert instant ADD COLUMN in the cache by removing the
last column from the table and the clustered index.
row_search_on_row_ref(), row_undo_mod_parse_undo_rec(), row_undo_mod(),
trx_undo_update_rec_get_update(): Handle the 'default row'
as a special case.
dtuple_t::trim(index): Omit a redundant suffix of an index tuple right
before insert or update. After instant ADD COLUMN, if the last fields
of a clustered index tuple match the 'default row', there is no
need to store them. While trimming the entry, we must hold a page latch,
so that the table cannot be emptied and the 'default row' be deleted.
btr_cur_optimistic_update(), btr_cur_pessimistic_update(),
row_upd_clust_rec_by_insert(), row_ins_clust_index_entry_low():
Invoke dtuple_t::trim() if needed.
row_ins_clust_index_entry(): Restore dtuple_t::n_fields after calling
row_ins_clust_index_entry_low().
rec_get_converted_size(), rec_get_converted_size_comp(): Allow the number
of fields to be between n_core_fields and n_fields. Do not support
infimum,supremum. They are never supposed to be stored in dtuple_t,
because page creation nowadays uses a lower-level method for initializing
them.
rec_convert_dtuple_to_rec_comp(): Assign the status bits based on the
number of fields.
btr_cur_trim(): In an update, trim the index entry as needed. For the
'default row', handle rollback specially. For user records, omit
fields that match the 'default row'.
btr_cur_optimistic_delete_func(), btr_cur_pessimistic_delete():
Skip locking and adaptive hash index for the 'default row'.
row_log_table_apply_convert_mrec(): Replace 'default row' values if needed.
In the temporary file that is applied by row_log_table_apply(),
we must identify whether the records contain the extra header for
instantly added columns. For now, we will allocate an additional byte
for this for ROW_T_INSERT and ROW_T_UPDATE records when the source table
has been subject to instant ADD COLUMN. The ROW_T_DELETE records are
fine, as they will be converted and will only contain 'core' columns
(PRIMARY KEY and some system columns) that are converted from dtuple_t.
rec_get_converted_size_temp(), rec_init_offsets_temp(),
rec_convert_dtuple_to_temp(): Add the parameter 'status'.
REC_INFO_DEFAULT_ROW = REC_INFO_MIN_REC_FLAG | REC_STATUS_COLUMNS_ADDED:
An info_bits constant for distinguishing the 'default row' record.
rec_comp_status_t: An enum of the status bit values.
rec_leaf_format: An enum that replaces the bool parameter of
rec_init_offsets_comp_ordinary().
2017-10-06 07:00:05 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Prevent an ut_ad() failure in page_zip_write_rec() by
|
|
|
|
adding system columns to the dummy table pointed to by the
|
|
|
|
dummy secondary index. The insert buffer is only used for
|
|
|
|
secondary indexes, whose records never contain any system
|
|
|
|
columns, such as DB_TRX_ID. */
|
|
|
|
ut_d(dict_table_add_system_columns(index->table, index->table->heap));
|
|
|
|
|
|
|
|
*pindex = index;
|
|
|
|
|
|
|
|
return(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Get the data size.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return size of fields */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
ibuf_rec_get_size(
|
|
|
|
/*==============*/
|
|
|
|
const rec_t* rec, /*!< in: ibuf record */
|
|
|
|
const byte* types, /*!< in: fields */
|
|
|
|
ulint n_fields, /*!< in: number of fields */
|
|
|
|
ulint comp) /*!< in: 0=ROW_FORMAT=REDUNDANT,
|
|
|
|
nonzero=ROW_FORMAT=COMPACT */
|
|
|
|
{
|
|
|
|
ulint i;
|
|
|
|
ulint field_offset;
|
|
|
|
ulint types_offset;
|
|
|
|
ulint size = 0;
|
|
|
|
|
|
|
|
field_offset = IBUF_REC_FIELD_USER;
|
|
|
|
types_offset = DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
|
|
|
|
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
|
|
ulint len;
|
|
|
|
dtype_t dtype;
|
|
|
|
|
|
|
|
rec_get_nth_field_offs_old(rec, i + field_offset, &len);
|
|
|
|
|
|
|
|
if (len != UNIV_SQL_NULL) {
|
|
|
|
size += len;
|
|
|
|
} else {
|
|
|
|
dtype_new_read_for_order_and_null_size(&dtype, types);
|
|
|
|
|
|
|
|
size += dtype_get_sql_null_size(&dtype, comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
types += types_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_rec_get_volume(mtr,rec) ibuf_rec_get_volume_func(mtr,rec)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_rec_get_volume(mtr,rec) ibuf_rec_get_volume_func(rec)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Returns the space taken by a stored non-clustered index entry if converted to
|
|
|
|
an index record.
|
|
|
|
@return size of index record in bytes + an upper limit of the space
|
|
|
|
taken in the page directory */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
ibuf_rec_get_volume_func(
|
|
|
|
/*=====================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* ibuf_rec)/*!< in: ibuf record */
|
|
|
|
{
|
|
|
|
ulint len;
|
|
|
|
const byte* data;
|
|
|
|
const byte* types;
|
|
|
|
ulint n_fields;
|
|
|
|
ulint data_size;
|
|
|
|
ulint comp;
|
|
|
|
ibuf_op_t op;
|
|
|
|
ulint info_len;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(ibuf_rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(rec_get_n_fields_old(ibuf_rec) > 2);
|
|
|
|
|
|
|
|
data = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
ut_a(len == 1);
|
|
|
|
ut_a(*data == 0);
|
|
|
|
|
|
|
|
types = rec_get_nth_field_old(
|
|
|
|
ibuf_rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
|
|
|
ibuf_rec_get_info(mtr, ibuf_rec, &op, &comp, &info_len, NULL);
|
|
|
|
|
|
|
|
if (op == IBUF_OP_DELETE_MARK || op == IBUF_OP_DELETE) {
|
|
|
|
/* Delete-marking a record doesn't take any
|
|
|
|
additional space, and while deleting a record
|
|
|
|
actually frees up space, we have to play it safe and
|
|
|
|
pretend it takes no additional space (the record
|
|
|
|
might not exist, etc.). */
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
} else if (comp) {
|
|
|
|
dtuple_t* entry;
|
|
|
|
ulint volume;
|
|
|
|
dict_index_t* dummy_index;
|
|
|
|
mem_heap_t* heap = mem_heap_create(500);
|
|
|
|
|
|
|
|
entry = ibuf_build_entry_from_ibuf_rec(mtr, ibuf_rec,
|
|
|
|
heap, &dummy_index);
|
|
|
|
|
|
|
|
volume = rec_get_converted_size(dummy_index, entry, 0);
|
|
|
|
|
|
|
|
ibuf_dummy_index_free(dummy_index);
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
|
|
|
return(volume + page_dir_calc_reserved_space(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
types += info_len;
|
|
|
|
n_fields = rec_get_n_fields_old(ibuf_rec)
|
|
|
|
- IBUF_REC_FIELD_USER;
|
|
|
|
|
|
|
|
data_size = ibuf_rec_get_size(ibuf_rec, types, n_fields, comp);
|
|
|
|
|
|
|
|
return(data_size + rec_get_converted_extra_size(data_size, n_fields, 0)
|
|
|
|
+ page_dir_calc_reserved_space(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Builds the tuple to insert to an ibuf tree when we have an entry for a
|
|
|
|
non-clustered index.
|
|
|
|
|
|
|
|
NOTE that the original entry must be kept because we copy pointers to
|
|
|
|
its fields.
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
@return own: entry to insert into an ibuf index tree */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
dtuple_t*
|
|
|
|
ibuf_entry_build(
|
|
|
|
/*=============*/
|
|
|
|
ibuf_op_t op, /*!< in: operation type */
|
|
|
|
dict_index_t* index, /*!< in: non-clustered index */
|
|
|
|
const dtuple_t* entry, /*!< in: entry for a non-clustered index */
|
|
|
|
ulint space, /*!< in: space id */
|
|
|
|
ulint page_no,/*!< in: index page number where entry should
|
|
|
|
be inserted */
|
|
|
|
ulint counter,/*!< in: counter value;
|
|
|
|
ULINT_UNDEFINED=not used */
|
|
|
|
mem_heap_t* heap) /*!< in: heap into which to build */
|
|
|
|
{
|
|
|
|
dtuple_t* tuple;
|
|
|
|
dfield_t* field;
|
|
|
|
const dfield_t* entry_field;
|
|
|
|
ulint n_fields;
|
|
|
|
byte* buf;
|
|
|
|
byte* ti;
|
|
|
|
byte* type_info;
|
|
|
|
ulint i;
|
|
|
|
|
|
|
|
ut_ad(counter != ULINT_UNDEFINED || op == IBUF_OP_INSERT);
|
|
|
|
ut_ad(counter == ULINT_UNDEFINED || counter <= 0xFFFF);
|
|
|
|
ut_ad(op < IBUF_OP_COUNT);
|
|
|
|
|
|
|
|
/* We have to build a tuple with the following fields:
|
|
|
|
|
|
|
|
1-4) These are described at the top of this file.
|
|
|
|
|
|
|
|
5) The rest of the fields are copied from the entry.
|
|
|
|
|
|
|
|
All fields in the tuple are ordered like the type binary in our
|
|
|
|
insert buffer tree. */
|
|
|
|
|
|
|
|
n_fields = dtuple_get_n_fields(entry);
|
|
|
|
|
|
|
|
tuple = dtuple_create(heap, n_fields + IBUF_REC_FIELD_USER);
|
|
|
|
|
|
|
|
/* 1) Space Id */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_SPACE);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
|
|
|
|
|
|
|
|
mach_write_to_4(buf, space);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 4);
|
|
|
|
|
|
|
|
/* 2) Marker byte */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_MARKER);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 1));
|
|
|
|
|
|
|
|
/* We set the marker byte zero */
|
|
|
|
|
|
|
|
mach_write_to_1(buf, 0);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 1);
|
|
|
|
|
|
|
|
/* 3) Page number */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_PAGE);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
|
|
|
|
|
|
|
|
mach_write_to_4(buf, page_no);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 4);
|
|
|
|
|
|
|
|
/* 4) Type info, part #1 */
|
|
|
|
|
|
|
|
if (counter == ULINT_UNDEFINED) {
|
|
|
|
i = dict_table_is_comp(index->table) ? 1 : 0;
|
|
|
|
} else {
|
|
|
|
ut_ad(counter <= 0xFFFF);
|
|
|
|
i = IBUF_REC_INFO_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ti = type_info = static_cast<byte*>(
|
|
|
|
mem_heap_alloc(
|
|
|
|
heap,
|
|
|
|
i + n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE));
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* set the flag for ROW_FORMAT=COMPACT */
|
|
|
|
*ti++ = 0;
|
|
|
|
/* fall through */
|
|
|
|
case 0:
|
|
|
|
/* the old format does not allow delete buffering */
|
|
|
|
ut_ad(op == IBUF_OP_INSERT);
|
|
|
|
break;
|
|
|
|
case IBUF_REC_INFO_SIZE:
|
|
|
|
mach_write_to_2(ti + IBUF_REC_OFFSET_COUNTER, counter);
|
|
|
|
|
|
|
|
ti[IBUF_REC_OFFSET_TYPE] = (byte) op;
|
|
|
|
ti[IBUF_REC_OFFSET_FLAGS] = dict_table_is_comp(index->table)
|
|
|
|
? IBUF_REC_COMPACT : 0;
|
|
|
|
ti += IBUF_REC_INFO_SIZE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 5+) Fields from the entry */
|
|
|
|
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
|
|
ulint fixed_len;
|
|
|
|
const dict_field_t* ifield;
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, i + IBUF_REC_FIELD_USER);
|
|
|
|
entry_field = dtuple_get_nth_field(entry, i);
|
|
|
|
dfield_copy(field, entry_field);
|
|
|
|
|
|
|
|
ifield = dict_index_get_nth_field(index, i);
|
|
|
|
/* Prefix index columns of fixed-length columns are of
|
|
|
|
fixed length. However, in the function call below,
|
|
|
|
dfield_get_type(entry_field) contains the fixed length
|
|
|
|
of the column in the clustered index. Replace it with
|
|
|
|
the fixed length of the secondary index column. */
|
|
|
|
fixed_len = ifield->fixed_len;
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
if (fixed_len) {
|
|
|
|
/* dict_index_add_col() should guarantee these */
|
|
|
|
ut_ad(fixed_len <= (ulint)
|
|
|
|
dfield_get_type(entry_field)->len);
|
|
|
|
if (ifield->prefix_len) {
|
|
|
|
ut_ad(ifield->prefix_len == fixed_len);
|
|
|
|
} else {
|
|
|
|
ut_ad(fixed_len == (ulint)
|
|
|
|
dfield_get_type(entry_field)->len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
dtype_new_store_for_order_and_null_size(
|
|
|
|
ti, dfield_get_type(entry_field), fixed_len);
|
|
|
|
ti += DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 4) Type info, part #2 */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_METADATA);
|
|
|
|
|
2018-04-28 15:49:09 +03:00
|
|
|
dfield_set_data(field, type_info, ulint(ti - type_info));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Set all the types in the new tuple binary */
|
|
|
|
|
|
|
|
dtuple_set_types_binary(tuple, n_fields + IBUF_REC_FIELD_USER);
|
|
|
|
|
|
|
|
return(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Builds a search tuple used to search buffered inserts for an index page.
|
|
|
|
This is for >= 4.1.x format records.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return own: search tuple */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
dtuple_t*
|
|
|
|
ibuf_search_tuple_build(
|
|
|
|
/*====================*/
|
|
|
|
ulint space, /*!< in: space id */
|
|
|
|
ulint page_no,/*!< in: index page number */
|
|
|
|
mem_heap_t* heap) /*!< in: heap into which to build */
|
|
|
|
{
|
|
|
|
dtuple_t* tuple;
|
|
|
|
dfield_t* field;
|
|
|
|
byte* buf;
|
|
|
|
|
|
|
|
tuple = dtuple_create(heap, IBUF_REC_FIELD_METADATA);
|
|
|
|
|
|
|
|
/* Store the space id in tuple */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_SPACE);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
|
|
|
|
|
|
|
|
mach_write_to_4(buf, space);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 4);
|
|
|
|
|
|
|
|
/* Store the new format record marker byte */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_MARKER);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 1));
|
|
|
|
|
|
|
|
mach_write_to_1(buf, 0);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 1);
|
|
|
|
|
|
|
|
/* Store the page number in tuple */
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_PAGE);
|
|
|
|
|
|
|
|
buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
|
|
|
|
|
|
|
|
mach_write_to_4(buf, page_no);
|
|
|
|
|
|
|
|
dfield_set_data(field, buf, 4);
|
|
|
|
|
|
|
|
dtuple_set_types_binary(tuple, IBUF_REC_FIELD_METADATA);
|
|
|
|
|
|
|
|
return(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Checks if there are enough pages in the free list of the ibuf tree that we
|
|
|
|
dare to start a pessimistic insert to the insert buffer.
|
2018-04-28 15:49:09 +03:00
|
|
|
@return whether enough free pages in list */
|
|
|
|
static inline bool ibuf_data_enough_free_for_insert()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ut_ad(mutex_own(&ibuf_mutex));
|
|
|
|
|
|
|
|
/* We want a big margin of free pages, because a B-tree can sometimes
|
|
|
|
grow in size also if records are deleted from it, as the node pointers
|
|
|
|
can change, and we must make sure that we are able to delete the
|
|
|
|
inserts buffered for pages that we read to the buffer pool, without
|
|
|
|
any risk of running out of free space in the insert buffer. */
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
return(ibuf.free_list_len >= (ibuf.size / 2) + 3 * ibuf.height);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Checks if there are enough pages in the free list of the ibuf tree that we
|
|
|
|
should remove them and free to the file space management.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if enough free pages in list */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
ibuf_data_too_much_free(void)
|
|
|
|
/*=========================*/
|
|
|
|
{
|
|
|
|
ut_ad(mutex_own(&ibuf_mutex));
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
return(ibuf.free_list_len >= 3 + (ibuf.size / 2) + 3 * ibuf.height);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
/** Allocate a change buffer page.
|
|
|
|
@retval true on success
|
|
|
|
@retval false if no space left */
|
|
|
|
static bool ibuf_add_free_page()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
page_t* header_page;
|
|
|
|
buf_block_t* block;
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
mtr.start();
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Acquire the fsp latch before the ibuf header, obeying the latching
|
|
|
|
order */
|
2019-11-14 11:40:33 +02:00
|
|
|
mtr_x_lock_space(fil_system.sys_space, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
header_page = ibuf_header_page_get(&mtr);
|
|
|
|
|
|
|
|
/* Allocate a new page: NOTE that if the page has been a part of a
|
|
|
|
non-clustered index which has subsequently been dropped, then the
|
|
|
|
page may have buffered inserts in the insert buffer, and these
|
|
|
|
should be deleted from there. These get deleted when the page
|
|
|
|
allocation creates the page in buffer. Thus the call below may end
|
|
|
|
up calling the insert buffer routines and, as we yet have no latches
|
|
|
|
to insert buffer tree pages, these routines can run without a risk
|
|
|
|
of a deadlock. This is the reason why we created a special ibuf
|
|
|
|
header page apart from the ibuf tree. */
|
|
|
|
|
|
|
|
block = fseg_alloc_free_page(
|
|
|
|
header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, 0, FSP_UP,
|
|
|
|
&mtr);
|
|
|
|
|
|
|
|
if (block == NULL) {
|
2019-12-03 10:19:45 +02:00
|
|
|
mtr.commit();
|
|
|
|
return false;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(rw_lock_get_x_lock_count(&block->lock) == 1);
|
|
|
|
ibuf_enter(&mtr);
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE_NEW);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
mtr.write<2>(*block, block->frame + FIL_PAGE_TYPE,
|
|
|
|
FIL_PAGE_IBUF_FREE_LIST);
|
2019-10-11 14:12:36 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Add the page to the free list and update the ibuf size data */
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
flst_add_last(ibuf_tree_root_get(&mtr),
|
|
|
|
PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST,
|
|
|
|
block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.seg_size++;
|
|
|
|
ibuf.free_list_len++;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Set the bit indicating that this page is now an ibuf tree page
|
|
|
|
(level 2 page) */
|
|
|
|
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
const page_id_t page_id(block->page.id());
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(page_id, 0, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_IBUF>(bitmap_page, page_id,
|
|
|
|
srv_page_size, true,
|
|
|
|
&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
2019-12-03 10:19:45 +02:00
|
|
|
return true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Removes a page from the free list and frees it to the fsp system. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_remove_free_page(void)
|
|
|
|
/*=======================*/
|
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
mtr_t mtr2;
|
|
|
|
page_t* header_page;
|
|
|
|
|
MDEV-13637 InnoDB change buffer housekeeping can cause redo log overrun and possibly deadlocks
The function ibuf_remove_free_page() may be called while the caller
is holding several mutexes or rw-locks. Because of this, this
housekeeping loop may cause performance glitches for operations that
involve tables that are stored in the InnoDB system tablespace.
Also deadlocks might be possible.
The worst impact of all is that due to the mutexes being held, calls to
log_free_check() had to be skipped during this housekeeping.
This means that the cyclic InnoDB redo log may be overwritten.
If the system crashes during this, it would be unable to recover.
The entry point to the problematic code is ibuf_free_excess_pages().
It would make sense to call it before acquiring any mutexes or rw-locks,
in any 'pessimistic' operation that involves the system tablespace.
fseg_create_general(), fseg_alloc_free_page_general(): Do not call
ibuf_free_excess_pages() while potentially holding some latches.
ibuf_remove_free_page(): Do call log_free_check(), like every operation
that is about to generate redo log should do.
ibuf_free_excess_pages(): Remove some assertions that are replaced
by stricter assertions in the log_free_check() that is now called by
ibuf_remove_free_page().
row_mtr_start(): New function, to perform necessary preparations when
starting a mini-transaction for row operations. For pessimistic operations
on secondary indexes that are located in the system tablespace,
this includes calling ibuf_free_excess_pages().
row_undo_ins_remove_sec_low(), row_undo_mod_del_mark_or_remove_sec_low(),
row_undo_mod_del_unmark_sec_and_undo_update(): Call row_mtr_start().
row_ins_sec_index_entry(): Call ibuf_free_excess_pages() if the operation
may involve allocating pages and change buffering in the system tablespace.
row_upd_sec_index_entry(): Slightly refactor the code. The
delete-marking of the old entry is done in-place. It could be
change-buffered, but the old code should be unlikely to have
invoked ibuf_free_excess_pages() in this case.
2017-08-28 08:57:51 +03:00
|
|
|
log_free_check();
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_start(&mtr);
|
|
|
|
/* Acquire the fsp latch before the ibuf header, obeying the latching
|
|
|
|
order */
|
|
|
|
|
2019-11-14 11:40:33 +02:00
|
|
|
mtr_x_lock_space(fil_system.sys_space, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
header_page = ibuf_header_page_get(&mtr);
|
|
|
|
|
|
|
|
/* Prevent pessimistic inserts to insert buffer trees for a while */
|
|
|
|
ibuf_enter(&mtr);
|
|
|
|
mutex_enter(&ibuf_pessimistic_insert_mutex);
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
|
|
|
if (!ibuf_data_too_much_free()) {
|
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
mutex_exit(&ibuf_pessimistic_insert_mutex);
|
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr2);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* root = ibuf_tree_root_get(&mtr2);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t page_no = flst_get_last(PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST
|
|
|
|
+ root->frame).page;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* NOTE that we must release the latch on the ibuf tree root
|
|
|
|
because in fseg_free_page we access level 1 pages, and the root
|
|
|
|
is a level 2 page. */
|
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr2);
|
|
|
|
ibuf_exit(&mtr);
|
|
|
|
|
|
|
|
/* Since pessimistic inserts were prevented, we know that the
|
|
|
|
page is still in the free list. NOTE that also deletes may take
|
|
|
|
pages from the free list, but they take them from the start, and
|
|
|
|
the free list was so long that they cannot have taken the last
|
|
|
|
page from it. */
|
|
|
|
|
2019-04-08 17:13:37 +03:00
|
|
|
compile_time_assert(IBUF_SPACE_ID == 0);
|
2014-02-26 19:11:54 +01:00
|
|
|
fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER,
|
2020-05-18 17:30:02 +03:00
|
|
|
fil_system.sys_space, page_no, &mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
const page_id_t page_id(IBUF_SPACE_ID, page_no);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_enter(&mtr);
|
|
|
|
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
|
|
|
root = ibuf_tree_root_get(&mtr);
|
|
|
|
|
2019-11-28 11:44:40 +02:00
|
|
|
ut_ad(page_no == flst_get_last(PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST
|
2019-12-03 10:19:45 +02:00
|
|
|
+ root->frame).page);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* block = buf_page_get(page_id, 0, RW_X_LATCH, &mtr);
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Remove the page from the free list and update the ibuf size data */
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
flst_remove(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST,
|
|
|
|
block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_pessimistic_insert_mutex);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.seg_size--;
|
|
|
|
ibuf.free_list_len--;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Set the bit indicating that this page is no more an ibuf tree page
|
|
|
|
(level 2 page) */
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(page_id, 0, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_IBUF>(
|
|
|
|
bitmap_page, page_id, srv_page_size, false, &mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2020-12-18 17:12:57 +02:00
|
|
|
buf_page_free(fil_system.sys_space, page_no, &mtr, __FILE__, __LINE__);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Frees excess pages from the ibuf free list. This function is called when an OS
|
|
|
|
thread calls fsp services to allocate a new file segment, or a new page to a
|
|
|
|
file segment, and the thread did not own the fsp latch before this call. */
|
|
|
|
void
|
|
|
|
ibuf_free_excess_pages(void)
|
|
|
|
/*========================*/
|
|
|
|
{
|
|
|
|
/* Free at most a few pages at a time, so that we do not delay the
|
|
|
|
requested service too much */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
for (ulint i = 0; i < 4; i++) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibool too_much_free;
|
|
|
|
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
too_much_free = ibuf_data_too_much_free();
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
|
|
|
if (!too_much_free) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_remove_free_page();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
2016-09-06 09:43:16 +03:00
|
|
|
# define ibuf_get_merge_page_nos(contract,rec,mtr,ids,pages,n_stored) \
|
|
|
|
ibuf_get_merge_page_nos_func(contract,rec,mtr,ids,pages,n_stored)
|
2014-02-26 19:11:54 +01:00
|
|
|
#else /* UNIV_DEBUG */
|
2016-09-06 09:43:16 +03:00
|
|
|
# define ibuf_get_merge_page_nos(contract,rec,mtr,ids,pages,n_stored) \
|
|
|
|
ibuf_get_merge_page_nos_func(contract,rec,ids,pages,n_stored)
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Reads page numbers from a leaf in an ibuf tree.
|
|
|
|
@return a lower limit for the combined volume of records which will be
|
|
|
|
merged */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
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 */
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction holding rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t* space_ids,/*!< in/out: space id's of the pages */
|
|
|
|
uint32_t* page_nos,/*!< in/out: buffer for at least
|
2014-02-26 19:11:54 +01:00
|
|
|
IBUF_MAX_N_PAGES_MERGED many page numbers;
|
|
|
|
the page numbers are in an ascending order */
|
|
|
|
ulint* n_stored)/*!< out: number of page numbers stored to
|
|
|
|
page_nos in this function */
|
|
|
|
{
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t prev_page_no;
|
|
|
|
uint32_t prev_space_id;
|
|
|
|
uint32_t first_page_no;
|
|
|
|
uint32_t first_space_id;
|
|
|
|
uint32_t rec_page_no;
|
|
|
|
uint32_t rec_space_id;
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint sum_volumes;
|
|
|
|
ulint volume_for_page;
|
|
|
|
ulint rec_volume;
|
|
|
|
ulint limit;
|
|
|
|
ulint n_pages;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
|
|
|
|
*n_stored = 0;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
limit = ut_min(IBUF_MAX_N_PAGES_MERGED,
|
|
|
|
buf_pool_get_curr_size() / 4);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
|
|
|
|
rec = page_rec_get_prev_const(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_rec_is_infimum(rec)) {
|
|
|
|
|
|
|
|
rec = page_rec_get_next_const(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
first_page_no = ibuf_rec_get_page_no(mtr, rec);
|
|
|
|
first_space_id = ibuf_rec_get_space(mtr, rec);
|
|
|
|
n_pages = 0;
|
|
|
|
prev_page_no = 0;
|
|
|
|
prev_space_id = 0;
|
|
|
|
|
|
|
|
/* Go backwards from the first rec until we reach the border of the
|
|
|
|
'merge area', or the page start or the limit of storeable pages is
|
|
|
|
reached */
|
|
|
|
|
|
|
|
while (!page_rec_is_infimum(rec) && UNIV_LIKELY(n_pages < limit)) {
|
|
|
|
|
|
|
|
rec_page_no = ibuf_rec_get_page_no(mtr, rec);
|
|
|
|
rec_space_id = ibuf_rec_get_space(mtr, rec);
|
|
|
|
|
|
|
|
if (rec_space_id != first_space_id
|
|
|
|
|| (rec_page_no / IBUF_MERGE_AREA)
|
|
|
|
!= (first_page_no / IBUF_MERGE_AREA)) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rec_page_no != prev_page_no
|
|
|
|
|| rec_space_id != prev_space_id) {
|
|
|
|
n_pages++;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_page_no = rec_page_no;
|
|
|
|
prev_space_id = rec_space_id;
|
|
|
|
|
|
|
|
rec = page_rec_get_prev_const(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = page_rec_get_next_const(rec);
|
|
|
|
|
|
|
|
/* At the loop start there is no prev page; we mark this with a pair
|
|
|
|
of space id, page no (0, 0) for which there can never be entries in
|
|
|
|
the insert buffer */
|
|
|
|
|
|
|
|
prev_page_no = 0;
|
|
|
|
prev_space_id = 0;
|
|
|
|
sum_volumes = 0;
|
|
|
|
volume_for_page = 0;
|
|
|
|
|
|
|
|
while (*n_stored < limit) {
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
/* When no more records available, mark this with
|
|
|
|
another 'impossible' pair of space id, page no */
|
|
|
|
rec_page_no = 1;
|
|
|
|
rec_space_id = 0;
|
|
|
|
} else {
|
|
|
|
rec_page_no = ibuf_rec_get_page_no(mtr, rec);
|
|
|
|
rec_space_id = ibuf_rec_get_space(mtr, rec);
|
2016-08-12 11:17:45 +03:00
|
|
|
/* In the system tablespace the smallest
|
2014-02-26 19:11:54 +01:00
|
|
|
possible secondary index leaf page number is
|
2016-08-12 11:17:45 +03:00
|
|
|
bigger than FSP_DICT_HDR_PAGE_NO (7).
|
|
|
|
In all tablespaces, pages 0 and 1 are reserved
|
|
|
|
for the allocation bitmap and the change
|
|
|
|
buffer bitmap. In file-per-table tablespaces,
|
|
|
|
a file segment inode page will be created at
|
|
|
|
page 2 and the clustered index tree is created
|
|
|
|
at page 3. So for file-per-table tablespaces,
|
|
|
|
page 4 is the smallest possible secondary
|
|
|
|
index leaf page. CREATE TABLESPACE also initially
|
|
|
|
uses pages 2 and 3 for the first created table,
|
|
|
|
but that table may be dropped, allowing page 2
|
|
|
|
to be reused for a secondary index leaf page.
|
|
|
|
To keep this assertion simple, just
|
|
|
|
make sure the page is >= 2. */
|
|
|
|
ut_ad(rec_page_no >= FSP_FIRST_INODE_PAGE_NO);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ut_a(*n_stored < IBUF_MAX_N_PAGES_MERGED);
|
|
|
|
#endif
|
|
|
|
if ((rec_space_id != prev_space_id
|
|
|
|
|| rec_page_no != prev_page_no)
|
|
|
|
&& (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)
|
2018-04-27 14:26:43 +03:00
|
|
|
* 4U << srv_page_size_shift
|
2014-02-26 19:11:54 +01:00
|
|
|
/ IBUF_PAGE_SIZE_PER_FREE_SPACE)
|
|
|
|
/ IBUF_MERGE_THRESHOLD)) {
|
|
|
|
|
|
|
|
space_ids[*n_stored] = prev_space_id;
|
|
|
|
page_nos[*n_stored] = prev_page_no;
|
|
|
|
|
|
|
|
(*n_stored)++;
|
|
|
|
|
|
|
|
sum_volumes += volume_for_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rec_space_id != first_space_id
|
|
|
|
|| rec_page_no / IBUF_MERGE_AREA
|
|
|
|
!= first_page_no / IBUF_MERGE_AREA) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume_for_page = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rec_page_no == 1 && rec_space_id == 0) {
|
|
|
|
/* Supremum record */
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rec_volume = ibuf_rec_get_volume(mtr, rec);
|
|
|
|
|
|
|
|
volume_for_page += rec_volume;
|
|
|
|
|
|
|
|
prev_page_no = rec_page_no;
|
|
|
|
prev_space_id = rec_space_id;
|
|
|
|
|
|
|
|
rec = page_rec_get_next_const(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ut_a(*n_stored <= IBUF_MAX_N_PAGES_MERGED);
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
fprintf(stderr, "Ibuf merge batch %lu pages %lu volume\n",
|
|
|
|
*n_stored, sum_volumes);
|
|
|
|
#endif
|
|
|
|
return(sum_volumes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Get the matching records for space id.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return current rec or NULL */
|
2016-06-21 14:21:03 +02:00
|
|
|
static MY_ATTRIBUTE((nonnull, warn_unused_result))
|
2014-02-26 19:11:54 +01:00
|
|
|
const rec_t*
|
|
|
|
ibuf_get_user_rec(
|
|
|
|
/*===============*/
|
|
|
|
btr_pcur_t* pcur, /*!< in: the current cursor */
|
|
|
|
mtr_t* mtr) /*!< in: mini transaction */
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
const rec_t* rec = btr_pcur_get_rec(pcur);
|
|
|
|
|
|
|
|
if (page_rec_is_user_rec(rec)) {
|
|
|
|
return(rec);
|
|
|
|
}
|
|
|
|
} while (btr_pcur_move_to_next(pcur, mtr));
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Reads page numbers for a space id from an ibuf tree.
|
|
|
|
@return a lower limit for the combined volume of records which will be
|
|
|
|
merged */
|
2016-06-21 14:21:03 +02:00
|
|
|
static MY_ATTRIBUTE((nonnull, warn_unused_result))
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
ibuf_get_merge_pages(
|
|
|
|
/*=================*/
|
|
|
|
btr_pcur_t* pcur, /*!< in/out: cursor */
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t space, /*!< in: space for which to merge */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint limit, /*!< in: max page numbers to read */
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t* pages, /*!< out: pages read */
|
|
|
|
uint32_t* spaces, /*!< out: spaces read */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint* n_pages,/*!< out: number of pages read */
|
|
|
|
mtr_t* mtr) /*!< in: mini transaction */
|
|
|
|
{
|
|
|
|
const rec_t* rec;
|
|
|
|
ulint volume = 0;
|
|
|
|
|
|
|
|
*n_pages = 0;
|
|
|
|
|
|
|
|
while ((rec = ibuf_get_user_rec(pcur, mtr)) != 0
|
|
|
|
&& ibuf_rec_get_space(mtr, rec) == space
|
|
|
|
&& *n_pages < limit) {
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t page_no = ibuf_rec_get_page_no(mtr, rec);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (*n_pages == 0 || pages[*n_pages - 1] != page_no) {
|
|
|
|
spaces[*n_pages] = space;
|
|
|
|
pages[*n_pages] = page_no;
|
|
|
|
++*n_pages;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume += ibuf_rec_get_volume(mtr, rec);
|
|
|
|
|
|
|
|
btr_pcur_move_to_next(pcur, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(volume);
|
|
|
|
}
|
|
|
|
|
2019-11-07 10:34:33 +02:00
|
|
|
/**
|
|
|
|
Delete a change buffer record.
|
2020-10-15 16:28:19 +03:00
|
|
|
@param[in] page_id page identifier
|
2019-11-07 10:34:33 +02:00
|
|
|
@param[in,out] pcur persistent cursor positioned on the record
|
|
|
|
@param[in] search_tuple search key for (space,page_no)
|
|
|
|
@param[in,out] mtr mini-transaction
|
|
|
|
@return whether mtr was committed (due to pessimistic operation) */
|
|
|
|
static MY_ATTRIBUTE((warn_unused_result, nonnull))
|
2020-10-15 16:28:19 +03:00
|
|
|
bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur,
|
2019-11-07 10:34:33 +02:00
|
|
|
const dtuple_t* search_tuple, mtr_t* mtr);
|
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
/** Merge the change buffer to some pages. */
|
2020-10-15 16:28:19 +03:00
|
|
|
static void ibuf_read_merge_pages(const uint32_t* space_ids,
|
|
|
|
const uint32_t* page_nos, ulint n_stored)
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
{
|
2021-06-07 19:07:45 +03:00
|
|
|
#ifndef DBUG_OFF
|
2019-11-07 10:34:33 +02:00
|
|
|
mem_heap_t* heap = mem_heap_create(512);
|
|
|
|
ulint dops[IBUF_OP_COUNT];
|
|
|
|
memset(dops, 0, sizeof(dops));
|
2021-06-07 19:07:45 +03:00
|
|
|
#endif
|
2019-11-07 10:34:33 +02:00
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
for (ulint i = 0; i < n_stored; i++) {
|
|
|
|
const ulint space_id = space_ids[i];
|
2020-10-26 16:04:12 +02:00
|
|
|
fil_space_t* s = fil_space_t::get(space_id);
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
if (!s) {
|
|
|
|
tablespace_deleted:
|
|
|
|
/* The tablespace was not found: remove all
|
|
|
|
entries for it */
|
|
|
|
ibuf_delete_for_discarded_space(space_id);
|
|
|
|
while (i + 1 < n_stored
|
|
|
|
&& space_ids[i + 1] == space_id) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:45:28 +08:00
|
|
|
const ulint zip_size = s->zip_size(), size = s->size;
|
2020-10-26 16:04:12 +02:00
|
|
|
s->release();
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
mtr_t mtr;
|
2019-11-19 10:45:28 +08:00
|
|
|
|
|
|
|
if (UNIV_LIKELY(page_nos[i] < size)) {
|
|
|
|
mtr.start();
|
|
|
|
dberr_t err;
|
|
|
|
buf_page_get_gen(page_id_t(space_id, page_nos[i]),
|
2020-05-07 17:57:03 +03:00
|
|
|
zip_size, RW_X_LATCH, nullptr,
|
|
|
|
BUF_GET_POSSIBLY_FREED,
|
2019-11-19 10:45:28 +08:00
|
|
|
__FILE__, __LINE__, &mtr, &err, true);
|
|
|
|
mtr.commit();
|
|
|
|
if (err == DB_TABLESPACE_DELETED) {
|
|
|
|
goto tablespace_deleted;
|
|
|
|
}
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
}
|
2021-06-07 19:07:45 +03:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
DBUG_EXECUTE_IF("ibuf_merge_corruption", goto work_around;);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* The following code works around a hang when the
|
|
|
|
change buffer is corrupted, likely due to the race
|
|
|
|
condition in crash recovery that was fixed in
|
|
|
|
MDEV-24449. But, it also introduces corruption by
|
|
|
|
itself in the following scenario:
|
|
|
|
|
|
|
|
(1) We merged buffered changes in buf_page_get_gen()
|
|
|
|
(2) We committed the mini-transaction
|
|
|
|
(3) Redo log and the page with the merged changes is written
|
|
|
|
(4) A write completion callback thread evicts the page.
|
|
|
|
(5) Other threads buffer changes for that page.
|
|
|
|
(6) We will wrongly discard those newly buffered changes below.
|
|
|
|
|
|
|
|
This code will be available in debug builds, so that
|
|
|
|
users may try to fix a shutdown hang that occurs due
|
|
|
|
to a corrupted change buffer. */
|
|
|
|
|
|
|
|
work_around:
|
2019-11-07 10:34:33 +02:00
|
|
|
/* Prevent an infinite loop, by removing entries from
|
|
|
|
the change buffer also in the case the bitmap bits were
|
|
|
|
wrongly clear even though buffered changes exist. */
|
|
|
|
const dtuple_t* tuple = ibuf_search_tuple_build(
|
|
|
|
space_id, page_nos[i], heap);
|
|
|
|
loop:
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
btr_pcur_open(ibuf.index, tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
|
|
|
|
&pcur, &mtr);
|
|
|
|
if (!btr_pcur_is_on_user_rec(&pcur)) {
|
2019-11-26 16:32:51 +02:00
|
|
|
ut_ad(btr_pcur_is_after_last_on_page(&pcur));
|
2019-11-07 10:34:33 +02:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ut_ad(btr_pcur_is_on_user_rec(&pcur));
|
|
|
|
|
|
|
|
const rec_t* ibuf_rec = btr_pcur_get_rec(&pcur);
|
|
|
|
if (ibuf_rec_get_space(&mtr, ibuf_rec) != space_id
|
|
|
|
|| ibuf_rec_get_page_no(&mtr, ibuf_rec)
|
|
|
|
!= page_nos[i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dops[ibuf_rec_get_op_type(&mtr, ibuf_rec)]++;
|
|
|
|
/* Delete the record from ibuf */
|
2020-10-15 16:28:19 +03:00
|
|
|
if (ibuf_delete_rec(page_id_t(space_id, page_nos[i]),
|
2019-11-07 10:34:33 +02:00
|
|
|
&pcur, tuple, &mtr)) {
|
|
|
|
/* Deletion was pessimistic and mtr
|
|
|
|
was committed: we start from the
|
|
|
|
beginning again */
|
|
|
|
ut_ad(mtr.has_committed());
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (btr_pcur_is_after_last_on_page(&pcur)) {
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
mem_heap_empty(heap);
|
2021-06-07 19:07:45 +03:00
|
|
|
#endif
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
}
|
2019-11-07 10:34:33 +02:00
|
|
|
|
2021-06-07 19:07:45 +03:00
|
|
|
#ifndef DBUG_OFF
|
2019-11-07 10:34:33 +02:00
|
|
|
ibuf_add_ops(ibuf.n_discarded_ops, dops);
|
|
|
|
mem_heap_free(heap);
|
2021-06-07 19:07:45 +03:00
|
|
|
#endif
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
}
|
|
|
|
|
2022-11-14 15:40:28 +02:00
|
|
|
/** Contract the change buffer by reading pages to the buffer pool.
|
2014-02-26 19:11:54 +01:00
|
|
|
@return a lower limit for the combined size in bytes of entries which
|
2022-11-14 15:40:28 +02:00
|
|
|
will be merged from ibuf trees to the pages read
|
|
|
|
@retval 0 if ibuf.empty */
|
|
|
|
ulint ibuf_contract()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
ulint sum_sizes;
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t page_nos[IBUF_MAX_N_PAGES_MERGED];
|
|
|
|
uint32_t space_ids[IBUF_MAX_N_PAGES_MERGED];
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
|
|
|
/* Open a cursor to a randomly chosen leaf of the tree, at a random
|
|
|
|
position within the leaf */
|
2016-08-12 11:17:45 +03:00
|
|
|
bool available;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
available = btr_pcur_open_at_rnd_pos(ibuf.index, BTR_SEARCH_LEAF,
|
2016-08-12 11:17:45 +03:00
|
|
|
&pcur, &mtr);
|
|
|
|
/* No one should make this index unavailable when server is running */
|
|
|
|
ut_a(available);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(btr_pcur_get_page(&pcur), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_is_empty(btr_pcur_get_page(&pcur))) {
|
|
|
|
/* If a B-tree page is empty, it must be the root page
|
|
|
|
and the whole B-tree must be empty. InnoDB does not
|
|
|
|
allow empty B-tree pages other than the root. */
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(ibuf.empty);
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(btr_pcur_get_block(&pcur)->page.id()
|
2019-12-03 10:20:44 +02:00
|
|
|
== page_id_t(IBUF_SPACE_ID, FSP_IBUF_TREE_ROOT_PAGE_NO));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2022-11-14 15:40:28 +02:00
|
|
|
ulint n_pages = 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
sum_sizes = ibuf_get_merge_page_nos(TRUE,
|
|
|
|
btr_pcur_get_rec(&pcur), &mtr,
|
2016-08-12 11:17:45 +03:00
|
|
|
space_ids,
|
2022-11-14 15:40:28 +02:00
|
|
|
page_nos, &n_pages);
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
2022-11-14 15:40:28 +02:00
|
|
|
ibuf_read_merge_pages(space_ids, page_nos, n_pages);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(sum_sizes + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
2016-06-21 14:21:03 +02:00
|
|
|
Contracts insert buffer trees by reading pages referring to space_id
|
|
|
|
to the buffer pool.
|
|
|
|
@returns number of pages merged.*/
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
ibuf_merge_space(
|
|
|
|
/*=============*/
|
2016-06-21 14:21:03 +02:00
|
|
|
ulint space) /*!< in: tablespace id to merge */
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
mem_heap_t* heap = mem_heap_create(512);
|
|
|
|
dtuple_t* tuple = ibuf_search_tuple_build(space, 0, heap);
|
2016-06-21 14:21:03 +02:00
|
|
|
ulint n_pages = 0;
|
|
|
|
|
2019-11-25 22:32:24 +07:00
|
|
|
ut_ad(space < SRV_SPACE_ID_UPPER_BOUND);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2022-11-08 11:37:43 +02:00
|
|
|
log_free_check();
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
|
|
|
/* Position the cursor on the first matching record. */
|
|
|
|
|
|
|
|
btr_pcur_open(
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur,
|
2014-02-26 19:11:54 +01:00
|
|
|
&mtr);
|
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(btr_pcur_get_page(&pcur), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ulint sum_sizes = 0;
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t pages[IBUF_MAX_N_PAGES_MERGED];
|
|
|
|
uint32_t spaces[IBUF_MAX_N_PAGES_MERGED];
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_is_empty(btr_pcur_get_page(&pcur))) {
|
|
|
|
/* If a B-tree page is empty, it must be the root page
|
|
|
|
and the whole B-tree must be empty. InnoDB does not
|
|
|
|
allow empty B-tree pages other than the root. */
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(ibuf.empty);
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(btr_pcur_get_block(&pcur)->page.id()
|
2019-12-03 10:20:44 +02:00
|
|
|
== page_id_t(IBUF_SPACE_ID, FSP_IBUF_TREE_ROOT_PAGE_NO));
|
2014-02-26 19:11:54 +01:00
|
|
|
} else {
|
|
|
|
|
|
|
|
sum_sizes = ibuf_get_merge_pages(
|
2020-10-15 16:28:19 +03:00
|
|
|
&pcur, uint32_t(space), IBUF_MAX_N_PAGES_MERGED,
|
2016-09-06 09:43:16 +03:00
|
|
|
&pages[0], &spaces[0], &n_pages,
|
|
|
|
&mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::info() << "Size of pages merged " << sum_sizes;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
2016-06-21 14:21:03 +02:00
|
|
|
if (n_pages > 0) {
|
|
|
|
ut_ad(n_pages <= UT_ARR_SIZE(pages));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-09-06 09:43:16 +03:00
|
|
|
#ifdef UNIV_DEBUG
|
2016-06-21 14:21:03 +02:00
|
|
|
for (ulint i = 0; i < n_pages; ++i) {
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(spaces[i] == space);
|
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ibuf_read_merge_pages(spaces, pages, n_pages);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 14:21:03 +02:00
|
|
|
return(n_pages);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
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 */
|
|
|
|
{
|
2022-11-14 15:40:28 +02:00
|
|
|
/* dirty comparison, to avoid contention on ibuf_mutex */
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
if (ibuf.size < ibuf.max_size) {
|
2014-02-26 19:11:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Contract at least entry_size many bytes */
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ulint sum_sizes = 0;
|
|
|
|
ulint size;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
do {
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
size = ibuf_contract();
|
2014-02-26 19:11:54 +01:00
|
|
|
sum_sizes += size;
|
|
|
|
} while (size > 0 && sum_sizes < entry_size);
|
|
|
|
}
|
|
|
|
|
MDEV-22497 [ERROR] InnoDB: Unable to purge a record
The InnoDB insert buffer was upgraded in MySQL 5.5 into a change
buffer that also covers delete-mark and delete (purge) operations.
There is an important constraint for delete operations: a B-tree
leaf page must not become empty unless the entire tree becomes empty,
consisting of an empty root page. Because change buffer merges only
occur on a single leaf page at a time, delete operations must not be
buffered if it is possible that the last record of the page could be
deleted. (In that case, we would refuse to use the change buffer, and
if we really delete the last record, we would shrink the index tree.)
The function ibuf_get_volume_buffered_hash() is part of our insurance
that the page would not become empty. It is supposed to map each
buffered INSERT or DELETE_MARK record payload into a hash value.
We will only count each such record as a distinct key if there is no
hash collision. DELETE operations will always decrement the predicted
number fo records in the page.
Due to a bug in the function, we would actually compute the hash value
not only on the record payload, but also on some following bytes,
in case the record contains NULL values. In MySQL Bug #61104, we had
some examples of this dating back to 2012. But back then, we failed to
reproduce the bug, and in commit d84c95579ba1eca2f9bf5b0be9f14040e4441227
we simply demoted the hard assertion to a message printout and a debug
assertion failure.
ibuf_get_volume_buffered_hash(): Correctly compute the hash value
of the payload bytes only. Note: we will consider
('foo','bar'),(NULL,'foobar'),('foob','ar') to be equal, but this
is not a problem, because in case of a hash collision, we could
also consider ('boo','far') to be equal, and underestimate the number
of records in the page, leading to refusing to buffer a DELETE.
2020-05-07 20:44:33 +03:00
|
|
|
/** Determine if a change buffer record has been encountered already.
|
|
|
|
@param rec change buffer record in the MySQL 5.5 format
|
|
|
|
@param hash hash table of encountered records
|
|
|
|
@param size number of elements in hash
|
|
|
|
@retval true if a distinct record
|
|
|
|
@retval false if this may be duplicating an earlier record */
|
|
|
|
static bool ibuf_get_volume_buffered_hash(const rec_t *rec, ulint *hash,
|
|
|
|
ulint size)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
MDEV-22497 [ERROR] InnoDB: Unable to purge a record
The InnoDB insert buffer was upgraded in MySQL 5.5 into a change
buffer that also covers delete-mark and delete (purge) operations.
There is an important constraint for delete operations: a B-tree
leaf page must not become empty unless the entire tree becomes empty,
consisting of an empty root page. Because change buffer merges only
occur on a single leaf page at a time, delete operations must not be
buffered if it is possible that the last record of the page could be
deleted. (In that case, we would refuse to use the change buffer, and
if we really delete the last record, we would shrink the index tree.)
The function ibuf_get_volume_buffered_hash() is part of our insurance
that the page would not become empty. It is supposed to map each
buffered INSERT or DELETE_MARK record payload into a hash value.
We will only count each such record as a distinct key if there is no
hash collision. DELETE operations will always decrement the predicted
number fo records in the page.
Due to a bug in the function, we would actually compute the hash value
not only on the record payload, but also on some following bytes,
in case the record contains NULL values. In MySQL Bug #61104, we had
some examples of this dating back to 2012. But back then, we failed to
reproduce the bug, and in commit d84c95579ba1eca2f9bf5b0be9f14040e4441227
we simply demoted the hard assertion to a message printout and a debug
assertion failure.
ibuf_get_volume_buffered_hash(): Correctly compute the hash value
of the payload bytes only. Note: we will consider
('foo','bar'),(NULL,'foobar'),('foob','ar') to be equal, but this
is not a problem, because in case of a hash collision, we could
also consider ('boo','far') to be equal, and underestimate the number
of records in the page, leading to refusing to buffer a DELETE.
2020-05-07 20:44:33 +03:00
|
|
|
ut_ad(rec_get_n_fields_old(rec) > IBUF_REC_FIELD_USER);
|
|
|
|
const ulint start= rec_get_field_start_offs(rec, IBUF_REC_FIELD_USER);
|
|
|
|
const ulint len= rec_get_data_size_old(rec) - start;
|
|
|
|
const uint32_t fold= ut_crc32(rec + start, len);
|
|
|
|
hash+= (fold / (CHAR_BIT * sizeof *hash)) % size;
|
|
|
|
ulint bitmask= static_cast<ulint>(1) << (fold % (CHAR_BIT * sizeof(*hash)));
|
|
|
|
|
|
|
|
if (*hash & bitmask)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* We have not seen this record yet. Remember it. */
|
|
|
|
*hash|= bitmask;
|
|
|
|
return true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_get_volume_buffered_count(mtr,rec,hash,size,n_recs) \
|
|
|
|
ibuf_get_volume_buffered_count_func(mtr,rec,hash,size,n_recs)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_get_volume_buffered_count(mtr,rec,hash,size,n_recs) \
|
|
|
|
ibuf_get_volume_buffered_count_func(rec,hash,size,n_recs)
|
2016-08-12 11:17:45 +03:00
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Update the estimate of the number of records on a page, and
|
|
|
|
get the space taken by merging the buffered record to the index page.
|
|
|
|
@return size of index record in bytes + an upper limit of the space
|
|
|
|
taken in the page directory */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
ibuf_get_volume_buffered_count_func(
|
|
|
|
/*================================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction owning rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec, /*!< in: insert buffer record */
|
|
|
|
ulint* hash, /*!< in/out: hash array */
|
|
|
|
ulint size, /*!< in: number of elements in hash array */
|
|
|
|
lint* n_recs) /*!< in/out: estimated number of records
|
|
|
|
on the page that rec points to */
|
|
|
|
{
|
|
|
|
ulint len;
|
|
|
|
ibuf_op_t ibuf_op;
|
|
|
|
const byte* types;
|
|
|
|
ulint n_fields;
|
|
|
|
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
|
|
|
|
n_fields = rec_get_n_fields_old(rec);
|
|
|
|
ut_ad(n_fields > IBUF_REC_FIELD_USER);
|
|
|
|
n_fields -= IBUF_REC_FIELD_USER;
|
|
|
|
|
|
|
|
rec_get_nth_field_offs_old(rec, 1, &len);
|
|
|
|
/* This function is only invoked when buffering new
|
|
|
|
operations. All pre-4.1 records should have been merged
|
|
|
|
when the database was started up. */
|
|
|
|
ut_a(len == 1);
|
|
|
|
|
|
|
|
if (rec_get_deleted_flag(rec, 0)) {
|
|
|
|
/* This record has been merged already,
|
|
|
|
but apparently the system crashed before
|
|
|
|
the change was discarded from the buffer.
|
|
|
|
Pretend that the record does not exist. */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
types = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
2018-04-28 15:49:09 +03:00
|
|
|
switch (UNIV_EXPECT(int(len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE),
|
2014-02-26 19:11:54 +01:00
|
|
|
IBUF_REC_INFO_SIZE)) {
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
case 0:
|
|
|
|
/* This ROW_TYPE=REDUNDANT record does not include an
|
|
|
|
operation counter. Exclude it from the *n_recs,
|
|
|
|
because deletes cannot be buffered if there are
|
|
|
|
old-style inserts buffered for the page. */
|
|
|
|
|
|
|
|
len = ibuf_rec_get_size(rec, types, n_fields, 0);
|
|
|
|
|
|
|
|
return(len
|
|
|
|
+ rec_get_converted_extra_size(len, n_fields, 0)
|
|
|
|
+ page_dir_calc_reserved_space(1));
|
|
|
|
case 1:
|
|
|
|
/* This ROW_TYPE=COMPACT record does not include an
|
|
|
|
operation counter. Exclude it from the *n_recs,
|
|
|
|
because deletes cannot be buffered if there are
|
|
|
|
old-style inserts buffered for the page. */
|
|
|
|
goto get_volume_comp;
|
|
|
|
|
|
|
|
case IBUF_REC_INFO_SIZE:
|
|
|
|
ibuf_op = (ibuf_op_t) types[IBUF_REC_OFFSET_TYPE];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ibuf_op) {
|
|
|
|
case IBUF_OP_INSERT:
|
|
|
|
/* Inserts can be done by updating a delete-marked record.
|
|
|
|
Because delete-mark and insert operations can be pointing to
|
|
|
|
the same records, we must not count duplicates. */
|
|
|
|
case IBUF_OP_DELETE_MARK:
|
|
|
|
/* There must be a record to delete-mark.
|
|
|
|
See if this record has been already buffered. */
|
MDEV-22497 [ERROR] InnoDB: Unable to purge a record
The InnoDB insert buffer was upgraded in MySQL 5.5 into a change
buffer that also covers delete-mark and delete (purge) operations.
There is an important constraint for delete operations: a B-tree
leaf page must not become empty unless the entire tree becomes empty,
consisting of an empty root page. Because change buffer merges only
occur on a single leaf page at a time, delete operations must not be
buffered if it is possible that the last record of the page could be
deleted. (In that case, we would refuse to use the change buffer, and
if we really delete the last record, we would shrink the index tree.)
The function ibuf_get_volume_buffered_hash() is part of our insurance
that the page would not become empty. It is supposed to map each
buffered INSERT or DELETE_MARK record payload into a hash value.
We will only count each such record as a distinct key if there is no
hash collision. DELETE operations will always decrement the predicted
number fo records in the page.
Due to a bug in the function, we would actually compute the hash value
not only on the record payload, but also on some following bytes,
in case the record contains NULL values. In MySQL Bug #61104, we had
some examples of this dating back to 2012. But back then, we failed to
reproduce the bug, and in commit d84c95579ba1eca2f9bf5b0be9f14040e4441227
we simply demoted the hard assertion to a message printout and a debug
assertion failure.
ibuf_get_volume_buffered_hash(): Correctly compute the hash value
of the payload bytes only. Note: we will consider
('foo','bar'),(NULL,'foobar'),('foob','ar') to be equal, but this
is not a problem, because in case of a hash collision, we could
also consider ('boo','far') to be equal, and underestimate the number
of records in the page, leading to refusing to buffer a DELETE.
2020-05-07 20:44:33 +03:00
|
|
|
if (n_recs && ibuf_get_volume_buffered_hash(rec, hash, size)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
(*n_recs)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ibuf_op == IBUF_OP_DELETE_MARK) {
|
|
|
|
/* Setting the delete-mark flag does not
|
|
|
|
affect the available space on the page. */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IBUF_OP_DELETE:
|
|
|
|
/* A record will be removed from the page. */
|
|
|
|
if (n_recs) {
|
|
|
|
(*n_recs)--;
|
|
|
|
}
|
|
|
|
/* While deleting a record actually frees up space,
|
|
|
|
we have to play it safe and pretend that it takes no
|
|
|
|
additional space (the record might not exist, etc.). */
|
|
|
|
return(0);
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(ibuf_op == IBUF_OP_INSERT);
|
|
|
|
|
|
|
|
get_volume_comp:
|
|
|
|
{
|
|
|
|
dtuple_t* entry;
|
|
|
|
ulint volume;
|
|
|
|
dict_index_t* dummy_index;
|
|
|
|
mem_heap_t* heap = mem_heap_create(500);
|
|
|
|
|
|
|
|
entry = ibuf_build_entry_from_ibuf_rec(
|
|
|
|
mtr, rec, heap, &dummy_index);
|
|
|
|
|
|
|
|
volume = rec_get_converted_size(dummy_index, entry, 0);
|
|
|
|
|
|
|
|
ibuf_dummy_index_free(dummy_index);
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
|
|
|
return(volume + page_dir_calc_reserved_space(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Gets an upper limit for the combined size of entries buffered in the insert
|
|
|
|
buffer for a given page.
|
|
|
|
@return upper limit for the volume of buffered inserts for the index
|
2018-04-27 13:49:25 +03:00
|
|
|
page, in bytes; srv_page_size, if the entries for the index page span
|
2014-02-26 19:11:54 +01:00
|
|
|
several pages in the insert buffer */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
ibuf_get_volume_buffered(
|
|
|
|
/*=====================*/
|
|
|
|
const btr_pcur_t*pcur, /*!< in: pcur positioned at a place in an
|
|
|
|
insert buffer tree where we would insert an
|
|
|
|
entry for the index page whose number is
|
|
|
|
page_no, latch mode has to be BTR_MODIFY_PREV
|
|
|
|
or BTR_MODIFY_TREE */
|
|
|
|
ulint space, /*!< in: space id */
|
|
|
|
ulint page_no,/*!< in: page number of an index page */
|
|
|
|
lint* n_recs, /*!< in/out: minimum number of records on the
|
|
|
|
page after the buffered changes have been
|
|
|
|
applied, or NULL to disable the counting */
|
|
|
|
mtr_t* mtr) /*!< in: mini-transaction of pcur */
|
|
|
|
{
|
|
|
|
ulint volume;
|
|
|
|
const rec_t* rec;
|
|
|
|
const page_t* page;
|
|
|
|
const page_t* prev_page;
|
|
|
|
const page_t* next_page;
|
|
|
|
/* bitmap of buffered recs */
|
|
|
|
ulint hash_bitmap[128 / sizeof(ulint)];
|
|
|
|
|
|
|
|
ut_ad((pcur->latch_mode == BTR_MODIFY_PREV)
|
|
|
|
|| (pcur->latch_mode == BTR_MODIFY_TREE));
|
|
|
|
|
|
|
|
/* Count the volume of inserts earlier in the alphabetical order than
|
|
|
|
pcur */
|
|
|
|
|
|
|
|
volume = 0;
|
|
|
|
|
|
|
|
if (n_recs) {
|
|
|
|
memset(hash_bitmap, 0, sizeof hash_bitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = btr_pcur_get_rec(pcur);
|
|
|
|
page = page_align(rec);
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(page, ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
rec = page_rec_get_prev_const(rec);
|
|
|
|
}
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t prev_page_no;
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
for (; !page_rec_is_infimum(rec);
|
|
|
|
rec = page_rec_get_prev_const(rec)) {
|
|
|
|
ut_ad(page_align(rec) == page);
|
|
|
|
|
|
|
|
if (page_no != ibuf_rec_get_page_no(mtr, rec)
|
|
|
|
|| space != ibuf_rec_get_space(mtr, rec)) {
|
|
|
|
|
|
|
|
goto count_later;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume += ibuf_get_volume_buffered_count(
|
|
|
|
mtr, rec,
|
|
|
|
hash_bitmap, UT_ARR_SIZE(hash_bitmap), n_recs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look at the previous page */
|
|
|
|
|
2019-11-11 13:36:21 +02:00
|
|
|
prev_page_no = btr_page_get_prev(page);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (prev_page_no == FIL_NULL) {
|
|
|
|
|
|
|
|
goto count_later;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
buf_block_t* block;
|
|
|
|
|
|
|
|
block = buf_page_get(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id_t(IBUF_SPACE_ID, prev_page_no),
|
2019-02-06 19:50:11 +02:00
|
|
|
0, RW_X_LATCH, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
|
|
|
|
|
|
|
|
prev_page = buf_block_get_frame(block);
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(prev_page, ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_BTR_DEBUG
|
2019-11-26 10:14:07 +02:00
|
|
|
static_assert(FIL_PAGE_NEXT % 4 == 0, "alignment");
|
|
|
|
static_assert(FIL_PAGE_OFFSET % 4 == 0, "alignment");
|
|
|
|
ut_a(!memcmp_aligned<4>(prev_page + FIL_PAGE_NEXT,
|
|
|
|
page + FIL_PAGE_OFFSET, 4));
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_BTR_DEBUG */
|
|
|
|
|
|
|
|
rec = page_get_supremum_rec(prev_page);
|
|
|
|
rec = page_rec_get_prev_const(rec);
|
|
|
|
|
|
|
|
for (;; rec = page_rec_get_prev_const(rec)) {
|
|
|
|
ut_ad(page_align(rec) == prev_page);
|
|
|
|
|
|
|
|
if (page_rec_is_infimum(rec)) {
|
|
|
|
|
|
|
|
/* We cannot go to yet a previous page, because we
|
|
|
|
do not have the x-latch on it, and cannot acquire one
|
|
|
|
because of the latching order: we have to give up */
|
|
|
|
|
2018-04-27 13:49:25 +03:00
|
|
|
return(srv_page_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (page_no != ibuf_rec_get_page_no(mtr, rec)
|
|
|
|
|| space != ibuf_rec_get_space(mtr, rec)) {
|
|
|
|
|
|
|
|
goto count_later;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume += ibuf_get_volume_buffered_count(
|
|
|
|
mtr, rec,
|
|
|
|
hash_bitmap, UT_ARR_SIZE(hash_bitmap), n_recs);
|
|
|
|
}
|
|
|
|
|
|
|
|
count_later:
|
|
|
|
rec = btr_pcur_get_rec(pcur);
|
|
|
|
|
|
|
|
if (!page_rec_is_supremum(rec)) {
|
|
|
|
rec = page_rec_get_next_const(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; !page_rec_is_supremum(rec);
|
|
|
|
rec = page_rec_get_next_const(rec)) {
|
|
|
|
if (page_no != ibuf_rec_get_page_no(mtr, rec)
|
|
|
|
|| space != ibuf_rec_get_space(mtr, rec)) {
|
|
|
|
|
|
|
|
return(volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
volume += ibuf_get_volume_buffered_count(
|
|
|
|
mtr, rec,
|
|
|
|
hash_bitmap, UT_ARR_SIZE(hash_bitmap), n_recs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look at the next page */
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t next_page_no = btr_page_get_next(page);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (next_page_no == FIL_NULL) {
|
|
|
|
|
|
|
|
return(volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
buf_block_t* block;
|
|
|
|
|
|
|
|
block = buf_page_get(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id_t(IBUF_SPACE_ID, next_page_no),
|
2019-02-06 19:50:11 +02:00
|
|
|
0, RW_X_LATCH, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
|
|
|
|
|
|
|
|
next_page = buf_block_get_frame(block);
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(next_page, ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_BTR_DEBUG
|
2019-11-26 10:14:07 +02:00
|
|
|
static_assert(FIL_PAGE_PREV % 4 == 0, "alignment");
|
|
|
|
static_assert(FIL_PAGE_OFFSET % 4 == 0, "alignment");
|
|
|
|
ut_a(!memcmp_aligned<4>(next_page + FIL_PAGE_PREV,
|
|
|
|
page + FIL_PAGE_OFFSET, 4));
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_BTR_DEBUG */
|
|
|
|
|
|
|
|
rec = page_get_infimum_rec(next_page);
|
|
|
|
rec = page_rec_get_next_const(rec);
|
|
|
|
|
|
|
|
for (;; rec = page_rec_get_next_const(rec)) {
|
|
|
|
ut_ad(page_align(rec) == next_page);
|
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
|
|
|
|
/* We give up */
|
|
|
|
|
2018-04-27 13:49:25 +03:00
|
|
|
return(srv_page_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (page_no != ibuf_rec_get_page_no(mtr, rec)
|
|
|
|
|| space != ibuf_rec_get_space(mtr, rec)) {
|
|
|
|
|
|
|
|
return(volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
volume += ibuf_get_volume_buffered_count(
|
|
|
|
mtr, rec,
|
|
|
|
hash_bitmap, UT_ARR_SIZE(hash_bitmap), n_recs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Reads the biggest tablespace id from the high end of the insert buffer
|
|
|
|
tree and updates the counter in fil_system. */
|
|
|
|
void
|
|
|
|
ibuf_update_max_tablespace_id(void)
|
|
|
|
/*===============================*/
|
|
|
|
{
|
|
|
|
ulint max_space_id;
|
|
|
|
const rec_t* rec;
|
|
|
|
const byte* field;
|
|
|
|
ulint len;
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
mtr_t mtr;
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_a(!dict_table_is_comp(ibuf.index->table));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
|
|
|
btr_pcur_open_at_index_side(
|
2019-07-03 16:05:34 +03:00
|
|
|
false, ibuf.index, BTR_SEARCH_LEAF, &pcur, true, 0, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(btr_pcur_get_page(&pcur), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
btr_pcur_move_to_prev(&pcur, &mtr);
|
|
|
|
|
|
|
|
if (btr_pcur_is_before_first_on_page(&pcur)) {
|
|
|
|
/* The tree is empty */
|
|
|
|
|
|
|
|
max_space_id = 0;
|
|
|
|
} else {
|
|
|
|
rec = btr_pcur_get_rec(&pcur);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
|
|
|
|
|
|
|
|
ut_a(len == 4);
|
|
|
|
|
|
|
|
max_space_id = mach_read_from_4(field);
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
|
|
|
|
/* printf("Maximum space id in insert buffer %lu\n", max_space_id); */
|
|
|
|
|
|
|
|
fil_set_max_space_id_if_bigger(max_space_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_get_entry_counter_low(mtr,rec,space,page_no) \
|
|
|
|
ibuf_get_entry_counter_low_func(mtr,rec,space,page_no)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_get_entry_counter_low(mtr,rec,space,page_no) \
|
|
|
|
ibuf_get_entry_counter_low_func(rec,space,page_no)
|
|
|
|
#endif
|
|
|
|
/****************************************************************//**
|
|
|
|
Helper function for ibuf_get_entry_counter_func. Checks if rec is for
|
|
|
|
(space, page_no), and if so, reads counter value from it and returns
|
|
|
|
that + 1.
|
|
|
|
@retval ULINT_UNDEFINED if the record does not contain any counter
|
|
|
|
@retval 0 if the record is not for (space, page_no)
|
|
|
|
@retval 1 + previous counter value, otherwise */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
ibuf_get_entry_counter_low_func(
|
|
|
|
/*============================*/
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction of rec */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
const rec_t* rec, /*!< in: insert buffer record */
|
|
|
|
ulint space, /*!< in: space id */
|
|
|
|
ulint page_no) /*!< in: page number */
|
|
|
|
{
|
|
|
|
ulint counter;
|
|
|
|
const byte* field;
|
|
|
|
ulint len;
|
|
|
|
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX
|
|
|
|
| MTR_MEMO_PAGE_S_FIX));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(rec_get_n_fields_old(rec) > 2);
|
|
|
|
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
|
|
|
|
|
|
|
|
ut_a(len == 1);
|
|
|
|
|
|
|
|
/* Check the tablespace identifier. */
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
|
|
|
|
|
|
|
|
ut_a(len == 4);
|
|
|
|
|
|
|
|
if (mach_read_from_4(field) != space) {
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the page offset. */
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_PAGE, &len);
|
|
|
|
ut_a(len == 4);
|
|
|
|
|
|
|
|
if (mach_read_from_4(field) != page_no) {
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the record contains a counter field. */
|
|
|
|
field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
|
|
|
|
|
|
|
|
switch (len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE) {
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
case 0: /* ROW_FORMAT=REDUNDANT */
|
|
|
|
case 1: /* ROW_FORMAT=COMPACT */
|
|
|
|
return(ULINT_UNDEFINED);
|
|
|
|
|
|
|
|
case IBUF_REC_INFO_SIZE:
|
|
|
|
counter = mach_read_from_2(field + IBUF_REC_OFFSET_COUNTER);
|
|
|
|
ut_a(counter < 0xFFFF);
|
|
|
|
return(counter + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# define ibuf_get_entry_counter(space,page_no,rec,mtr,exact_leaf) \
|
|
|
|
ibuf_get_entry_counter_func(space,page_no,rec,mtr,exact_leaf)
|
|
|
|
#else /* UNIV_DEBUG */
|
|
|
|
# define ibuf_get_entry_counter(space,page_no,rec,mtr,exact_leaf) \
|
|
|
|
ibuf_get_entry_counter_func(space,page_no,rec,exact_leaf)
|
2016-08-12 11:17:45 +03:00
|
|
|
#endif /* UNIV_DEBUG */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/****************************************************************//**
|
|
|
|
Calculate the counter field for an entry based on the current
|
|
|
|
last record in ibuf for (space, page_no).
|
2016-08-12 11:17:45 +03:00
|
|
|
@return the counter field, or ULINT_UNDEFINED
|
2014-02-26 19:11:54 +01:00
|
|
|
if we should abort this insertion to ibuf */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
ibuf_get_entry_counter_func(
|
|
|
|
/*========================*/
|
|
|
|
ulint space, /*!< in: space id of entry */
|
|
|
|
ulint page_no, /*!< in: page number of entry */
|
|
|
|
const rec_t* rec, /*!< in: the record preceding the
|
|
|
|
insertion point */
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
mtr_t* mtr, /*!< in: mini-transaction */
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
ibool only_leaf) /*!< in: TRUE if this is the only
|
|
|
|
leaf page that can contain entries
|
|
|
|
for (space,page_no), that is, there
|
|
|
|
was no exact match for (space,page_no)
|
|
|
|
in the node pointer */
|
|
|
|
{
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
2020-06-10 07:43:58 +03:00
|
|
|
ut_ad(mtr->memo_contains_page_flagged(rec, MTR_MEMO_PAGE_X_FIX));
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(page_align(rec), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
|
|
|
/* This is just for safety. The record should be a
|
|
|
|
page infimum or a user record. */
|
|
|
|
ut_ad(0);
|
|
|
|
return(ULINT_UNDEFINED);
|
|
|
|
} else if (!page_rec_is_infimum(rec)) {
|
|
|
|
return(ibuf_get_entry_counter_low(mtr, rec, space, page_no));
|
2018-02-08 22:34:21 +02:00
|
|
|
} else if (only_leaf || !page_has_prev(page_align(rec))) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* The parent node pointer did not contain the
|
|
|
|
searched for (space, page_no), which means that the
|
|
|
|
search ended on the correct page regardless of the
|
|
|
|
counter value, and since we're at the infimum record,
|
|
|
|
there are no existing records. */
|
|
|
|
return(0);
|
|
|
|
} else {
|
|
|
|
/* We used to read the previous page here. It would
|
|
|
|
break the latching order, because the caller has
|
|
|
|
buffer-fixed an insert buffer bitmap page. */
|
|
|
|
return(ULINT_UNDEFINED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
|
|
|
|
/** Translates the ibuf free bits to the free space on a page in bytes.
|
|
|
|
@param[in] physical_size page_size
|
|
|
|
@param[in] bits value for ibuf bitmap bits
|
|
|
|
@return maximum insert size after reorganize for the page */
|
|
|
|
inline ulint
|
|
|
|
ibuf_index_page_calc_free_from_bits(ulint physical_size, ulint bits)
|
|
|
|
{
|
|
|
|
ut_ad(bits < 4);
|
|
|
|
ut_ad(physical_size > IBUF_PAGE_SIZE_PER_FREE_SPACE);
|
|
|
|
|
|
|
|
if (bits == 3) {
|
|
|
|
bits = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bits * physical_size / IBUF_PAGE_SIZE_PER_FREE_SPACE;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Buffer an operation in the insert/delete buffer, instead of doing it
|
2014-02-26 19:11:54 +01:00
|
|
|
directly to the disk page, if this is possible.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] mode BTR_MODIFY_PREV or BTR_MODIFY_TREE
|
|
|
|
@param[in] op operation type
|
|
|
|
@param[in] no_counter TRUE=use 5.0.3 format; FALSE=allow delete
|
|
|
|
buffering
|
|
|
|
@param[in] entry index entry to insert
|
|
|
|
@param[in] entry_size rec_get_converted_size(index, entry)
|
|
|
|
@param[in,out] index index where to insert; must not be unique
|
|
|
|
or clustered
|
|
|
|
@param[in] page_id page id where to insert
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in,out] thr query thread
|
|
|
|
@return DB_SUCCESS, DB_STRONG_FAIL or other error */
|
2016-09-06 09:43:16 +03:00
|
|
|
static MY_ATTRIBUTE((warn_unused_result))
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
ibuf_insert_low(
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint mode,
|
|
|
|
ibuf_op_t op,
|
|
|
|
ibool no_counter,
|
|
|
|
const dtuple_t* entry,
|
|
|
|
ulint entry_size,
|
|
|
|
dict_index_t* index,
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint zip_size,
|
2016-08-12 11:17:45 +03:00
|
|
|
que_thr_t* thr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
big_rec_t* dummy_big_rec;
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
btr_cur_t* cursor;
|
|
|
|
dtuple_t* ibuf_entry;
|
|
|
|
mem_heap_t* offsets_heap = NULL;
|
|
|
|
mem_heap_t* heap;
|
2020-04-28 10:46:51 +10:00
|
|
|
rec_offs* offsets = NULL;
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint buffered;
|
|
|
|
lint min_n_recs;
|
|
|
|
rec_t* ins_rec;
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* bitmap_page;
|
2014-02-26 19:11:54 +01:00
|
|
|
buf_block_t* block;
|
|
|
|
page_t* root;
|
|
|
|
dberr_t err;
|
|
|
|
ibool do_merge;
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t space_ids[IBUF_MAX_N_PAGES_MERGED];
|
|
|
|
uint32_t page_nos[IBUF_MAX_N_PAGES_MERGED];
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint n_stored;
|
|
|
|
mtr_t mtr;
|
|
|
|
mtr_t bitmap_mtr;
|
|
|
|
|
|
|
|
ut_a(!dict_index_is_clust(index));
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(!dict_index_is_spatial(index));
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(dtuple_check_typed(entry));
|
|
|
|
ut_ad(!no_counter || op == IBUF_OP_INSERT);
|
2018-11-22 17:07:35 +02:00
|
|
|
ut_ad(page_id.space() == index->table->space_id);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_a(op < IBUF_OP_COUNT);
|
|
|
|
|
|
|
|
do_merge = FALSE;
|
|
|
|
|
2022-11-14 15:40:28 +02:00
|
|
|
/* Perform dirty comparison of ibuf.max_size and ibuf.size to
|
|
|
|
reduce ibuf_mutex contention. This should be OK; at worst we
|
|
|
|
are doing some excessive ibuf_contract() or occasionally
|
2014-02-26 19:11:54 +01:00
|
|
|
skipping an ibuf_contract(). */
|
2022-11-14 15:40:28 +02:00
|
|
|
const ulint max_size = ibuf.max_size;
|
|
|
|
|
|
|
|
if (max_size == 0) {
|
2014-02-26 19:11:54 +01:00
|
|
|
return(DB_STRONG_FAIL);
|
|
|
|
}
|
|
|
|
|
2022-11-14 15:40:28 +02:00
|
|
|
if (ibuf.size >= max_size + IBUF_CONTRACT_DO_NOT_INSERT) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* 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
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ibuf_contract();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(DB_STRONG_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap = mem_heap_create(1024);
|
|
|
|
|
|
|
|
/* Build the entry which contains the space id and the page number
|
|
|
|
as the first fields and the type information for other fields, and
|
|
|
|
which will be inserted to the insert buffer. Using a counter value
|
|
|
|
of 0xFFFF we find the last record for (space, page_no), from which
|
|
|
|
we can then read the counter value N and use N + 1 in the record we
|
|
|
|
insert. (We patch the ibuf_entry's counter field to the correct
|
|
|
|
value just before actually inserting the entry.) */
|
|
|
|
|
|
|
|
ibuf_entry = ibuf_entry_build(
|
2016-08-12 11:17:45 +03:00
|
|
|
op, index, entry, page_id.space(), page_id.page_no(),
|
2014-02-26 19:11:54 +01:00
|
|
|
no_counter ? ULINT_UNDEFINED : 0xFFFF, heap);
|
|
|
|
|
|
|
|
/* Open a cursor to the insert buffer tree to calculate if we can add
|
|
|
|
the new entry to it without exceeding the free space limit for the
|
|
|
|
page. */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (BTR_LATCH_MODE_WITHOUT_INTENTION(mode) == BTR_MODIFY_TREE) {
|
2014-02-26 19:11:54 +01:00
|
|
|
for (;;) {
|
|
|
|
mutex_enter(&ibuf_pessimistic_insert_mutex);
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
|
|
|
if (UNIV_LIKELY(ibuf_data_enough_free_for_insert())) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
mutex_exit(&ibuf_pessimistic_insert_mutex);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (!ibuf_add_free_page()) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
return(DB_STRONG_FAIL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
btr_pcur_open(ibuf.index, ibuf_entry, PAGE_CUR_LE, mode, &pcur, &mtr);
|
|
|
|
ut_ad(page_validate(btr_pcur_get_page(&pcur), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Find out the volume of already buffered inserts for the same index
|
|
|
|
page */
|
|
|
|
min_n_recs = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
buffered = ibuf_get_volume_buffered(&pcur,
|
|
|
|
page_id.space(),
|
|
|
|
page_id.page_no(),
|
2014-02-26 19:11:54 +01:00
|
|
|
op == IBUF_OP_DELETE
|
|
|
|
? &min_n_recs
|
|
|
|
: NULL, &mtr);
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
const ulint physical_size = zip_size ? zip_size : srv_page_size;
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
if (op == IBUF_OP_DELETE
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
&& (min_n_recs < 2 || buf_pool.watch_occurred(page_id))) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* The page could become empty after the record is
|
|
|
|
deleted, or the page has been read in to the buffer
|
|
|
|
pool. Refuse to buffer the operation. */
|
|
|
|
|
|
|
|
/* The buffer pool watch is needed for IBUF_OP_DELETE
|
|
|
|
because of latching order considerations. We can
|
|
|
|
check buf_pool_watch_occurred() only after latching
|
|
|
|
the insert buffer B-tree pages that contain buffered
|
|
|
|
changes for the page. We never buffer IBUF_OP_DELETE,
|
|
|
|
unless some IBUF_OP_INSERT or IBUF_OP_DELETE_MARK have
|
|
|
|
been previously buffered for the page. Because there
|
|
|
|
are buffered operations for the page, the insert
|
|
|
|
buffer B-tree page latches held by mtr will guarantee
|
|
|
|
that no changes for the user page will be merged
|
|
|
|
before mtr_commit(&mtr). We must not mtr_commit(&mtr)
|
|
|
|
until after the IBUF_OP_DELETE has been buffered. */
|
|
|
|
|
|
|
|
fail_exit:
|
2016-08-12 11:17:45 +03:00
|
|
|
if (BTR_LATCH_MODE_WITHOUT_INTENTION(mode) == BTR_MODIFY_TREE) {
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
mutex_exit(&ibuf_pessimistic_insert_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = DB_STRONG_FAIL;
|
|
|
|
goto func_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* After this point, the page could still be loaded to the
|
|
|
|
buffer pool, but we do not have to care about it, since we are
|
|
|
|
holding a latch on the insert buffer leaf page that contains
|
|
|
|
buffered changes for (space, page_no). If the page enters the
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
buffer pool, buf_page_read_complete() for (space, page_no) will
|
2014-02-26 19:11:54 +01:00
|
|
|
have to acquire a latch on the same insert buffer leaf page,
|
|
|
|
which it cannot do until we have buffered the IBUF_OP_DELETE
|
|
|
|
and done mtr_commit(&mtr) to release the latch. */
|
|
|
|
|
|
|
|
ibuf_mtr_start(&bitmap_mtr);
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
bitmap_page = ibuf_bitmap_get_map_page(page_id, zip_size, &bitmap_mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* We check if the index page is suitable for buffered entries */
|
|
|
|
|
2020-09-11 15:55:30 +03:00
|
|
|
if (buf_pool.page_hash_contains(page_id)) {
|
|
|
|
commit_exit:
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_commit(&bitmap_mtr);
|
|
|
|
goto fail_exit;
|
2020-09-11 15:55:30 +03:00
|
|
|
} else {
|
|
|
|
lock_mutex_enter();
|
|
|
|
const auto lock_exists = lock_sys.get_first(page_id);
|
|
|
|
lock_mutex_exit();
|
|
|
|
if (lock_exists) {
|
|
|
|
goto commit_exit;
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (op == IBUF_OP_INSERT) {
|
|
|
|
ulint bits = ibuf_bitmap_page_get_bits(
|
2019-12-03 10:19:45 +02:00
|
|
|
bitmap_page->frame, page_id, physical_size,
|
|
|
|
IBUF_BITMAP_FREE, &bitmap_mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (buffered + entry_size + page_dir_calc_reserved_space(1)
|
2019-02-06 19:50:11 +02:00
|
|
|
> ibuf_index_page_calc_free_from_bits(physical_size,
|
|
|
|
bits)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Release the bitmap page latch early. */
|
|
|
|
ibuf_mtr_commit(&bitmap_mtr);
|
|
|
|
|
|
|
|
/* It may not fit */
|
|
|
|
do_merge = TRUE;
|
|
|
|
|
|
|
|
ibuf_get_merge_page_nos(FALSE,
|
|
|
|
btr_pcur_get_rec(&pcur), &mtr,
|
2016-08-12 11:17:45 +03:00
|
|
|
space_ids,
|
2016-09-06 09:43:16 +03:00
|
|
|
page_nos, &n_stored);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
goto fail_exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!no_counter) {
|
|
|
|
/* Patch correct counter value to the entry to
|
|
|
|
insert. This can change the insert position, which can
|
|
|
|
result in the need to abort in some cases. */
|
|
|
|
ulint counter = ibuf_get_entry_counter(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id.space(), page_id.page_no(),
|
|
|
|
btr_pcur_get_rec(&pcur), &mtr,
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_get_btr_cur(&pcur)->low_match
|
|
|
|
< IBUF_REC_FIELD_METADATA);
|
|
|
|
dfield_t* field;
|
|
|
|
|
|
|
|
if (counter == ULINT_UNDEFINED) {
|
2020-09-11 15:55:30 +03:00
|
|
|
goto commit_exit;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
field = dtuple_get_nth_field(
|
|
|
|
ibuf_entry, IBUF_REC_FIELD_METADATA);
|
|
|
|
mach_write_to_2(
|
|
|
|
(byte*) dfield_get_data(field)
|
|
|
|
+ IBUF_REC_OFFSET_COUNTER, counter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the bitmap bit denoting that the insert buffer contains
|
|
|
|
buffered entries for this index page, if the bit is not set yet */
|
2020-09-11 15:55:30 +03:00
|
|
|
index->set_modified(bitmap_mtr);
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_BUFFERED>(
|
|
|
|
bitmap_page, page_id, physical_size, true, &bitmap_mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_commit(&bitmap_mtr);
|
|
|
|
|
|
|
|
cursor = btr_pcur_get_btr_cur(&pcur);
|
|
|
|
|
|
|
|
if (mode == BTR_MODIFY_PREV) {
|
|
|
|
err = btr_cur_optimistic_insert(
|
MDEV-12358 Work around what looks like a bug in GCC 7.1.0
The parameter thr of the function btr_cur_optimistic_insert()
is not declared as nonnull, but GCC 7.1.0 with -O3 is wrongly
optimizing away the first part of the condition
UNIV_UNLIKELY(thr && thr_get_trx(thr)->fake_changes)
when the function is being called by row_merge_insert_index_tuples()
with thr==NULL.
The fake_changes is an XtraDB addition. This GCC bug only appears
to have an impact on XtraDB, not InnoDB.
We work around the problem by not attempting to dereference thr
when both BTR_NO_LOCKING_FLAG and BTR_NO_UNDO_LOG_FLAG are set
in the flags. Probably BTR_NO_LOCKING_FLAG alone should suffice.
btr_cur_optimistic_insert(), btr_cur_pessimistic_insert(),
btr_cur_pessimistic_update(): Correct comments that disagree with
usage and with nonnull attributes. No other parameter than thr can
actually be NULL.
row_ins_duplicate_error_in_clust(): Remove an unused parameter.
innobase_is_fake_change(): Unused function; remove.
ibuf_insert_low(), row_log_table_apply(), row_log_apply(),
row_undo_mod_clust_low():
Because we will be passing BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG
in the flags, the trx->fake_changes flag will be treated as false,
which is the right thing to do at these low-level operations
(change buffer merge, ALTER TABLE…LOCK=NONE, or ROLLBACK).
This might be fixing actual XtraDB bugs.
Other callers that pass these two flags are also passing thr=NULL,
implying fake_changes=false. (Some callers in ROLLBACK are passing
BTR_NO_LOCKING_FLAG and a nonnull thr. In these callers, fake_changes
better be false, to avoid corruption.)
2017-05-17 14:08:08 +03:00
|
|
|
BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG,
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor, &offsets, &offsets_heap,
|
|
|
|
ibuf_entry, &ins_rec,
|
|
|
|
&dummy_big_rec, 0, thr, &mtr);
|
|
|
|
block = btr_cur_get_block(cursor);
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(block->page.id().space() == IBUF_SPACE_ID);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
/* If this is the root page, update ibuf.empty. */
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
if (block->page.id().page_no() == FSP_IBUF_TREE_ROOT_PAGE_NO) {
|
2014-02-26 19:11:54 +01:00
|
|
|
const page_t* root = buf_block_get_frame(block);
|
|
|
|
|
|
|
|
ut_ad(page_get_space_id(root) == IBUF_SPACE_ID);
|
|
|
|
ut_ad(page_get_page_no(root)
|
|
|
|
== FSP_IBUF_TREE_ROOT_PAGE_NO);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.empty = page_is_empty(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
} else {
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(BTR_LATCH_MODE_WITHOUT_INTENTION(mode)
|
|
|
|
== BTR_MODIFY_TREE);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/* We acquire an sx-latch to the root page before the insert,
|
2014-02-26 19:11:54 +01:00
|
|
|
because a pessimistic insert releases the tree x-latch,
|
2016-08-12 11:17:45 +03:00
|
|
|
which would cause the sx-latching of the root after that to
|
2014-02-26 19:11:54 +01:00
|
|
|
break the latching order. */
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
root = ibuf_tree_root_get(&mtr)->frame;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
err = btr_cur_optimistic_insert(
|
|
|
|
BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG,
|
|
|
|
cursor, &offsets, &offsets_heap,
|
|
|
|
ibuf_entry, &ins_rec,
|
|
|
|
&dummy_big_rec, 0, thr, &mtr);
|
|
|
|
|
|
|
|
if (err == DB_FAIL) {
|
|
|
|
err = btr_cur_pessimistic_insert(
|
|
|
|
BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG,
|
|
|
|
cursor, &offsets, &offsets_heap,
|
|
|
|
ibuf_entry, &ins_rec,
|
|
|
|
&dummy_big_rec, 0, thr, &mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&ibuf_pessimistic_insert_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
ibuf_size_update(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&ibuf_mutex);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.empty = page_is_empty(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
block = btr_cur_get_block(cursor);
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(block->page.id().space() == IBUF_SPACE_ID);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (offsets_heap) {
|
|
|
|
mem_heap_free(offsets_heap);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err == DB_SUCCESS && op != IBUF_OP_DELETE) {
|
|
|
|
/* Update the page max trx id field */
|
|
|
|
page_update_max_trx_id(block, NULL,
|
|
|
|
thr_get_trx(thr)->id, &mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
func_exit:
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (err == DB_SUCCESS
|
|
|
|
&& BTR_LATCH_MODE_WITHOUT_INTENTION(mode) == BTR_MODIFY_TREE) {
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_contract_after_insert(entry_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_merge) {
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ut_a(n_stored <= IBUF_MAX_N_PAGES_MERGED);
|
|
|
|
#endif
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ibuf_read_merge_pages(space_ids, page_nos, n_stored);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
/** Buffer an operation in the change buffer, instead of applying it
|
|
|
|
directly to the file page, if this is possible. Does not do it if the index
|
2014-02-26 19:11:54 +01:00
|
|
|
is clustered or unique.
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in] op operation type
|
|
|
|
@param[in] entry index entry to insert
|
|
|
|
@param[in,out] index index where to insert
|
|
|
|
@param[in] page_id page id where to insert
|
2019-02-06 19:50:11 +02:00
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
2016-08-12 11:17:45 +03:00
|
|
|
@param[in,out] thr query thread
|
2019-02-06 19:50:11 +02:00
|
|
|
@return true if success */
|
|
|
|
bool
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_insert(
|
2016-08-12 11:17:45 +03:00
|
|
|
ibuf_op_t op,
|
|
|
|
const dtuple_t* entry,
|
|
|
|
dict_index_t* index,
|
2018-10-18 18:23:12 +03:00
|
|
|
const page_id_t page_id,
|
2019-02-06 19:50:11 +02:00
|
|
|
ulint zip_size,
|
2016-08-12 11:17:45 +03:00
|
|
|
que_thr_t* thr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
dberr_t err;
|
|
|
|
ulint entry_size;
|
|
|
|
ibool no_counter;
|
2018-06-12 12:49:42 +03:00
|
|
|
/* Read the settable global variable only once in
|
2014-02-26 19:11:54 +01:00
|
|
|
this function, so that we will have a consistent view of it. */
|
2018-06-12 12:49:42 +03:00
|
|
|
ibuf_use_t use = ibuf_use_t(innodb_change_buffering);
|
2014-02-26 19:11:54 +01:00
|
|
|
DBUG_ENTER("ibuf_insert");
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_PRINT("ibuf", ("op: %d, space: " UINT32PF ", page_no: " UINT32PF,
|
|
|
|
op, page_id.space(), page_id.page_no()));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_ad(dtuple_check_typed(entry));
|
2016-12-16 16:36:54 +02:00
|
|
|
ut_ad(page_id.space() != SRV_TMP_SPACE_ID);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_a(!dict_index_is_clust(index));
|
2018-05-12 09:38:46 +03:00
|
|
|
ut_ad(!index->table->is_temporary());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
no_counter = use <= IBUF_USE_INSERT;
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case IBUF_OP_INSERT:
|
|
|
|
switch (use) {
|
|
|
|
case IBUF_USE_NONE:
|
|
|
|
case IBUF_USE_DELETE:
|
|
|
|
case IBUF_USE_DELETE_MARK:
|
2019-02-06 19:50:11 +02:00
|
|
|
DBUG_RETURN(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
case IBUF_USE_INSERT:
|
|
|
|
case IBUF_USE_INSERT_DELETE_MARK:
|
|
|
|
case IBUF_USE_ALL:
|
|
|
|
goto check_watch;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IBUF_OP_DELETE_MARK:
|
|
|
|
switch (use) {
|
|
|
|
case IBUF_USE_NONE:
|
|
|
|
case IBUF_USE_INSERT:
|
2019-02-06 19:50:11 +02:00
|
|
|
DBUG_RETURN(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
case IBUF_USE_DELETE_MARK:
|
|
|
|
case IBUF_USE_DELETE:
|
|
|
|
case IBUF_USE_INSERT_DELETE_MARK:
|
|
|
|
case IBUF_USE_ALL:
|
|
|
|
ut_ad(!no_counter);
|
|
|
|
goto check_watch;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IBUF_OP_DELETE:
|
|
|
|
switch (use) {
|
|
|
|
case IBUF_USE_NONE:
|
|
|
|
case IBUF_USE_INSERT:
|
|
|
|
case IBUF_USE_INSERT_DELETE_MARK:
|
2019-02-06 19:50:11 +02:00
|
|
|
DBUG_RETURN(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
case IBUF_USE_DELETE_MARK:
|
|
|
|
case IBUF_USE_DELETE:
|
|
|
|
case IBUF_USE_ALL:
|
|
|
|
ut_ad(!no_counter);
|
|
|
|
goto skip_watch;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IBUF_OP_COUNT:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unknown op or use */
|
|
|
|
ut_error;
|
|
|
|
|
|
|
|
check_watch:
|
|
|
|
/* If a thread attempts to buffer an insert on a page while a
|
|
|
|
purge is in progress on the same page, the purge must not be
|
|
|
|
buffered, because it could remove a record that was
|
|
|
|
re-inserted later. For simplicity, we block the buffering of
|
|
|
|
all operations on a page that has a purge pending.
|
|
|
|
|
|
|
|
We do not check this in the IBUF_OP_DELETE case, because that
|
|
|
|
would always trigger the buffer pool watch during purge and
|
|
|
|
thus prevent the buffering of delete operations. We assume
|
|
|
|
that the issuer of IBUF_OP_DELETE has called
|
2020-06-12 12:50:31 +03:00
|
|
|
buf_pool_t::watch_set(). */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-06-12 12:50:31 +03:00
|
|
|
if (buf_pool.page_hash_contains<true>(page_id)) {
|
2020-02-12 14:45:21 +02:00
|
|
|
/* A buffer pool watch has been set or the
|
|
|
|
page has been read into the buffer pool.
|
|
|
|
Do not buffer the request. If a purge operation
|
|
|
|
is being buffered, have this request executed
|
|
|
|
directly on the page in the buffer pool after the
|
|
|
|
buffered entries for this page have been merged. */
|
|
|
|
DBUG_RETURN(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
skip_watch:
|
|
|
|
entry_size = rec_get_converted_size(index, entry, 0);
|
|
|
|
|
|
|
|
if (entry_size
|
|
|
|
>= page_get_free_space_of_empty(dict_table_is_comp(index->table))
|
|
|
|
/ 2) {
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
DBUG_RETURN(false);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
err = ibuf_insert_low(BTR_MODIFY_PREV, op, no_counter,
|
|
|
|
entry, entry_size,
|
2019-02-06 19:50:11 +02:00
|
|
|
index, page_id, zip_size, thr);
|
2014-02-26 19:11:54 +01:00
|
|
|
if (err == DB_FAIL) {
|
2016-08-12 11:17:45 +03:00
|
|
|
err = ibuf_insert_low(BTR_MODIFY_TREE | BTR_LATCH_FOR_INSERT,
|
|
|
|
op, no_counter, entry, entry_size,
|
2019-02-06 19:50:11 +02:00
|
|
|
index, page_id, zip_size, thr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
ut_a(err == DB_SUCCESS || err == DB_STRONG_FAIL
|
|
|
|
|| err == DB_TOO_BIG_RECORD);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
DBUG_RETURN(err == DB_SUCCESS);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
During merge, inserts to an index page a secondary index entry extracted
|
2014-05-06 21:13:16 +02:00
|
|
|
from the insert buffer.
|
2014-02-26 19:11:54 +01:00
|
|
|
@return newly inserted record */
|
2016-06-21 14:21:03 +02:00
|
|
|
static MY_ATTRIBUTE((nonnull))
|
2014-02-26 19:11:54 +01:00
|
|
|
rec_t*
|
|
|
|
ibuf_insert_to_index_page_low(
|
|
|
|
/*==========================*/
|
|
|
|
const dtuple_t* entry, /*!< in: buffered entry to insert */
|
|
|
|
buf_block_t* block, /*!< in/out: index page where the buffered
|
|
|
|
entry should be placed */
|
|
|
|
dict_index_t* index, /*!< in: record descriptor */
|
2020-04-28 10:46:51 +10:00
|
|
|
rec_offs** offsets,/*!< out: offsets on *rec */
|
2014-02-26 19:11:54 +01:00
|
|
|
mem_heap_t* heap, /*!< in/out: memory heap */
|
|
|
|
mtr_t* mtr, /*!< in/out: mtr */
|
|
|
|
page_cur_t* page_cur)/*!< in/out: cursor positioned on the record
|
|
|
|
after which to insert the buffered entry */
|
|
|
|
{
|
|
|
|
rec_t* rec;
|
|
|
|
DBUG_ENTER("ibuf_insert_to_index_page_low");
|
|
|
|
|
|
|
|
rec = page_cur_tuple_insert(page_cur, entry, index,
|
|
|
|
offsets, &heap, 0, mtr);
|
|
|
|
if (rec != NULL) {
|
|
|
|
DBUG_RETURN(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Page reorganization or recompression should already have
|
|
|
|
been attempted by page_cur_tuple_insert(). Besides, per
|
|
|
|
ibuf_index_page_calc_free_zip() the page should not have been
|
|
|
|
recompressed or reorganized. */
|
2019-08-08 22:53:33 +03:00
|
|
|
ut_ad(!is_buf_block_get_page_zip(block));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* If the record did not fit, reorganize */
|
|
|
|
|
|
|
|
btr_page_reorganize(page_cur, index, mtr);
|
|
|
|
|
|
|
|
/* This time the record must fit */
|
|
|
|
|
|
|
|
rec = page_cur_tuple_insert(page_cur, entry, index,
|
|
|
|
offsets, &heap, 0, mtr);
|
|
|
|
if (rec != NULL) {
|
|
|
|
DBUG_RETURN(rec);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::error() << "Insert buffer insert fails; page free "
|
2019-12-03 10:19:45 +02:00
|
|
|
<< page_get_max_insert_size(block->frame, 1)
|
|
|
|
<< ", dtuple size "
|
|
|
|
<< rec_get_converted_size(index, entry, 0);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
fputs("InnoDB: Cannot insert index record ", stderr);
|
|
|
|
dtuple_print(stderr, entry);
|
|
|
|
fputs("\nInnoDB: The table where this index record belongs\n"
|
|
|
|
"InnoDB: is now probably corrupt. Please run CHECK TABLE on\n"
|
|
|
|
"InnoDB: that table.\n", stderr);
|
|
|
|
|
2021-03-01 23:07:12 +05:30
|
|
|
if (buf_block_t *bitmap_page = ibuf_bitmap_get_map_page(
|
|
|
|
block->page.id(), block->zip_size(), mtr)) {
|
|
|
|
|
|
|
|
ib::error() << "page " << block->page.id() << ", size "
|
|
|
|
<< block->physical_size() << ", bitmap bits "
|
|
|
|
<< ibuf_bitmap_page_get_bits(bitmap_page->frame,
|
|
|
|
block->page.id(), block->zip_size(),
|
|
|
|
IBUF_BITMAP_FREE, mtr);
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::error() << BUG_REPORT_MSG;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_ad(0);
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
During merge, inserts to an index page a secondary index entry extracted
|
|
|
|
from the insert buffer. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_insert_to_index_page(
|
|
|
|
/*======================*/
|
|
|
|
const dtuple_t* entry, /*!< in: buffered entry to insert */
|
|
|
|
buf_block_t* block, /*!< in/out: index page where the buffered entry
|
|
|
|
should be placed */
|
|
|
|
dict_index_t* index, /*!< in: record descriptor */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
page_cur_t page_cur;
|
|
|
|
ulint low_match;
|
|
|
|
page_t* page = buf_block_get_frame(block);
|
|
|
|
rec_t* rec;
|
2020-04-28 10:46:51 +10:00
|
|
|
rec_offs* offsets;
|
2014-02-26 19:11:54 +01:00
|
|
|
mem_heap_t* heap;
|
|
|
|
|
|
|
|
DBUG_ENTER("ibuf_insert_to_index_page");
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_PRINT("ibuf", ("page " UINT32PF ":" UINT32PF,
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
block->page.id().space(),
|
|
|
|
block->page.id().page_no()));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(!dict_index_is_online_ddl(index));// this is an ibuf_dummy index
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(dtuple_check_typed(entry));
|
2017-02-23 23:05:12 +02:00
|
|
|
#ifdef BTR_CUR_HASH_ADAPT
|
2016-09-02 17:28:54 +03:00
|
|
|
/* A change buffer merge must occur before users are granted
|
|
|
|
any access to the page. No adaptive hash index entries may
|
|
|
|
point to a freshly read page. */
|
2016-09-06 09:43:16 +03:00
|
|
|
ut_ad(!block->index);
|
2016-09-02 17:28:54 +03:00
|
|
|
assert_block_ahi_empty(block);
|
2017-02-23 23:05:12 +02:00
|
|
|
#endif /* BTR_CUR_HASH_ADAPT */
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(mtr->is_named_space(block->page.id().space()));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(dict_table_is_comp(index->table)
|
|
|
|
!= (ibool)!!page_is_comp(page))) {
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::warn() << "Trying to insert a record from the insert"
|
|
|
|
" buffer to an index page but the 'compact' flag does"
|
|
|
|
" not match!";
|
2014-02-26 19:11:54 +01:00
|
|
|
goto dump;
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = page_rec_get_next(page_get_infimum_rec(page));
|
|
|
|
|
|
|
|
if (page_rec_is_supremum(rec)) {
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::warn() << "Trying to insert a record from the insert"
|
|
|
|
" buffer to an index page but the index page"
|
|
|
|
" is empty!";
|
2014-02-26 19:11:54 +01:00
|
|
|
goto dump;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (!rec_n_fields_is_sane(index, rec, entry)) {
|
|
|
|
ib::warn() << "Trying to insert a record from the insert"
|
|
|
|
" buffer to an index page but the number of fields"
|
|
|
|
" does not match!";
|
|
|
|
rec_print(stderr, rec, index);
|
2014-02-26 19:11:54 +01:00
|
|
|
dump:
|
|
|
|
dtuple_print(stderr, entry);
|
|
|
|
ut_ad(0);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::warn() << "The table where this index record belongs"
|
|
|
|
" is now probably corrupt. Please run CHECK TABLE on"
|
|
|
|
" your tables. " << BUG_REPORT_MSG;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
low_match = page_cur_search(block, index, entry, &page_cur);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
heap = mem_heap_create(
|
|
|
|
sizeof(upd_t)
|
|
|
|
+ REC_OFFS_HEADER_SIZE * sizeof(*offsets)
|
|
|
|
+ dtuple_get_n_fields(entry)
|
|
|
|
* (sizeof(upd_field_t) + sizeof *offsets));
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(low_match == dtuple_get_n_fields(entry))) {
|
|
|
|
upd_t* update;
|
|
|
|
|
|
|
|
rec = page_cur_get_rec(&page_cur);
|
|
|
|
|
|
|
|
/* This is based on
|
|
|
|
row_ins_sec_index_entry_by_modify(BTR_MODIFY_LEAF). */
|
|
|
|
ut_ad(rec_get_deleted_flag(rec, page_is_comp(page)));
|
|
|
|
|
2021-04-13 10:28:13 +03:00
|
|
|
offsets = rec_get_offsets(rec, index, NULL, index->n_fields,
|
2017-09-19 19:20:11 +03:00
|
|
|
ULINT_UNDEFINED, &heap);
|
2014-02-26 19:11:54 +01:00
|
|
|
update = row_upd_build_sec_rec_difference_binary(
|
|
|
|
rec, index, offsets, entry, heap);
|
|
|
|
|
|
|
|
if (update->n_fields == 0) {
|
|
|
|
/* The records only differ in the delete-mark.
|
|
|
|
Clear the delete-mark, like we did before
|
|
|
|
Bug #56680 was fixed. */
|
2020-02-07 13:03:02 +02:00
|
|
|
btr_rec_set_deleted<false>(block, rec, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
goto updated_in_place;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the info bits. Clear the delete-mark. */
|
|
|
|
update->info_bits = rec_get_info_bits(rec, page_is_comp(page));
|
MDEV-21907: InnoDB: Enable -Wconversion on clang and GCC
The -Wconversion in GCC seems to be stricter than in clang.
GCC at least since version 4.4.7 issues truncation warnings for
assignments to bitfields, while clang 10 appears to only issue
warnings when the sizes in bytes rounded to the nearest integer
powers of 2 are different.
Before GCC 10.0.0, -Wconversion required more casts and would not
allow some operations, such as x<<=1 or x+=1 on a data type that
is narrower than int.
GCC 5 (but not GCC 4, GCC 6, or any later version) is complaining
about x|=y even when x and y are compatible types that are narrower
than int. Hence, we must rewrite some x|=y as
x=static_cast<byte>(x|y) or similar, or we must disable -Wconversion.
In GCC 6 and later, the warning for assigning wider to bitfields
that are narrower than 8, 16, or 32 bits can be suppressed by
applying a bitwise & with the exact bitmask of the bitfield.
For older GCC, we must disable -Wconversion for GCC 4 or 5 in such
cases.
The bitwise negation operator appears to promote short integers
to a wider type, and hence we must add explicit truncation casts
around them. Microsoft Visual C does not allow a static_cast to
truncate a constant, such as static_cast<byte>(1) truncating int.
Hence, we will use the constructor-style cast byte(~1) for such cases.
This has been tested at least with GCC 4.8.5, 5.4.0, 7.4.0, 9.2.1, 10.0.0,
clang 9.0.1, 10.0.0, and MSVC 14.22.27905 (Microsoft Visual Studio 2019)
on 64-bit and 32-bit targets (IA-32, AMD64, POWER 8, POWER 9, ARMv8).
2020-03-12 19:46:41 +02:00
|
|
|
update->info_bits &= byte(~REC_INFO_DELETED_FLAG);
|
2020-02-07 13:03:02 +02:00
|
|
|
page_zip_des_t* page_zip = buf_block_get_page_zip(block);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* We cannot invoke btr_cur_optimistic_update() here,
|
|
|
|
because we do not have a btr_cur_t or que_thr_t,
|
|
|
|
as the insert buffer merge occurs at a very low level. */
|
|
|
|
if (!row_upd_changes_field_size_or_external(index, offsets,
|
|
|
|
update)
|
|
|
|
&& (!page_zip || btr_cur_update_alloc_zip(
|
|
|
|
page_zip, &page_cur, index, offsets,
|
|
|
|
rec_offs_size(offsets), false, mtr))) {
|
|
|
|
/* This is the easy case. Do something similar
|
|
|
|
to btr_cur_update_in_place(). */
|
|
|
|
rec = page_cur_get_rec(&page_cur);
|
MDEV-12353: Replace MLOG_REC_INSERT,MLOG_COMP_REC_INSERT
page_mem_alloc_free(), page_dir_set_n_heap(), page_ptr_set_direction():
Merge with the callers.
page_direction_reset(), page_direction_increment(),
page_zip_dir_insert(), page_zip_write_rec_ext(), page_zip_write_rec():
Add the parameter mtr, and write log.
PageBulk::insert(), PageBulk::finish(): Write log for all changes.
page_cur_rec_insert(), page_cur_insert_rec_write_log(),
page_cur_insert_rec_write_log(): Remove.
page_rec_set_next(), page_header_set_field(), page_header_set_ptr():
Remove. Use lower-level operations with or without logging.
page_zip_dir_add_slot(): Move to the same compilation unit with
its only caller, page_cur_insert_rec_zip().
page_cur_insert_rec_zip(): Mark pieces of code that must be skipped
once this task is completed.
btr_defragment_chunk(): Before starting a mini-transaction that
is writing (a lot), invoke log_free_check(). This should allow
the test innodb.innodb_defrag_concurrent to pass with the
mtr default_mysqld.cnf setting of innodb_log_file_size=10M.
MLOG_BUF_MARGIN: Remove.
2020-02-13 12:33:27 +02:00
|
|
|
btr_cur_upd_rec_in_place(rec, index, offsets,
|
|
|
|
update, block, mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:23:04 +01:00
|
|
|
DBUG_EXECUTE_IF(
|
|
|
|
"crash_after_log_ibuf_upd_inplace",
|
|
|
|
log_buffer_flush_to_disk();
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::info() << "Wrote log record for ibuf"
|
|
|
|
" update in place operation";
|
2014-02-26 19:23:04 +01:00
|
|
|
DBUG_SUICIDE();
|
|
|
|
);
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
goto updated_in_place;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* btr_cur_update_alloc_zip() may have changed this */
|
|
|
|
rec = page_cur_get_rec(&page_cur);
|
|
|
|
|
|
|
|
/* A collation may identify values that differ in
|
|
|
|
storage length.
|
|
|
|
Some examples (1 or 2 bytes):
|
|
|
|
utf8_turkish_ci: I = U+0131 LATIN SMALL LETTER DOTLESS I
|
|
|
|
utf8_general_ci: S = U+00DF LATIN SMALL LETTER SHARP S
|
|
|
|
utf8_general_ci: A = U+00E4 LATIN SMALL LETTER A WITH DIAERESIS
|
|
|
|
|
|
|
|
latin1_german2_ci: SS = U+00DF LATIN SMALL LETTER SHARP S
|
|
|
|
|
|
|
|
Examples of a character (3-byte UTF-8 sequence)
|
|
|
|
identified with 2 or 4 characters (1-byte UTF-8 sequences):
|
|
|
|
|
|
|
|
utf8_unicode_ci: 'II' = U+2171 SMALL ROMAN NUMERAL TWO
|
|
|
|
utf8_unicode_ci: '(10)' = U+247D PARENTHESIZED NUMBER TEN
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Delete the different-length record, and insert the
|
|
|
|
buffered one. */
|
|
|
|
|
|
|
|
lock_rec_store_on_page_infimum(block, rec);
|
|
|
|
page_cur_delete_rec(&page_cur, index, offsets, mtr);
|
|
|
|
page_cur_move_to_prev(&page_cur);
|
|
|
|
rec = ibuf_insert_to_index_page_low(entry, block, index,
|
|
|
|
&offsets, heap, mtr,
|
|
|
|
&page_cur);
|
|
|
|
|
|
|
|
ut_ad(!cmp_dtuple_rec(entry, rec, offsets));
|
|
|
|
lock_rec_restore_from_page_infimum(block, rec, block);
|
|
|
|
} else {
|
|
|
|
offsets = NULL;
|
|
|
|
ibuf_insert_to_index_page_low(entry, block, index,
|
|
|
|
&offsets, heap, mtr,
|
|
|
|
&page_cur);
|
|
|
|
}
|
|
|
|
updated_in_place:
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************//**
|
|
|
|
During merge, sets the delete mark on a record for a secondary index
|
|
|
|
entry. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_set_del_mark(
|
|
|
|
/*==============*/
|
|
|
|
const dtuple_t* entry, /*!< in: entry */
|
|
|
|
buf_block_t* block, /*!< in/out: block */
|
|
|
|
const dict_index_t* index, /*!< in: record descriptor */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
page_cur_t page_cur;
|
|
|
|
ulint low_match;
|
|
|
|
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(dtuple_check_typed(entry));
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
low_match = page_cur_search(block, index, entry, &page_cur);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (low_match == dtuple_get_n_fields(entry)) {
|
2020-02-07 13:03:02 +02:00
|
|
|
rec_t* rec = page_cur_get_rec(&page_cur);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Delete mark the old index record. According to a
|
|
|
|
comment in row_upd_sec_index_entry(), it can already
|
|
|
|
have been delete marked if a lock wait occurred in
|
|
|
|
row_ins_sec_index_entry() in a previous invocation of
|
|
|
|
row_upd_sec_index_entry(). */
|
|
|
|
|
|
|
|
if (UNIV_LIKELY
|
|
|
|
(!rec_get_deleted_flag(
|
|
|
|
rec, dict_table_is_comp(index->table)))) {
|
2020-02-07 13:03:02 +02:00
|
|
|
btr_rec_set_deleted<true>(block, rec, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const page_t* page
|
|
|
|
= page_cur_get_page(&page_cur);
|
|
|
|
const buf_block_t* block
|
|
|
|
= page_cur_get_block(&page_cur);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::error() << "Unable to find a record to delete-mark";
|
2014-02-26 19:11:54 +01:00
|
|
|
fputs("InnoDB: tuple ", stderr);
|
|
|
|
dtuple_print(stderr, entry);
|
|
|
|
fputs("\n"
|
|
|
|
"InnoDB: record ", stderr);
|
|
|
|
rec_print(stderr, page_cur_get_rec(&page_cur), index);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ib::error() << "page " << block->page.id() << " ("
|
2016-08-12 11:17:45 +03:00
|
|
|
<< page_get_n_recs(page) << " records, index id "
|
|
|
|
<< btr_page_get_index_id(page) << ").";
|
|
|
|
|
|
|
|
ib::error() << BUG_REPORT_MSG;
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************//**
|
|
|
|
During merge, delete a record for a secondary index entry. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
ibuf_delete(
|
|
|
|
/*========*/
|
|
|
|
const dtuple_t* entry, /*!< in: entry */
|
|
|
|
buf_block_t* block, /*!< in/out: block */
|
|
|
|
dict_index_t* index, /*!< in: record descriptor */
|
|
|
|
mtr_t* mtr) /*!< in/out: mtr; must be committed
|
|
|
|
before latching any further pages */
|
|
|
|
{
|
|
|
|
page_cur_t page_cur;
|
|
|
|
ulint low_match;
|
|
|
|
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(dtuple_check_typed(entry));
|
2021-04-13 10:28:13 +03:00
|
|
|
ut_ad(!index->is_spatial());
|
|
|
|
ut_ad(!index->is_clust());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
low_match = page_cur_search(block, index, entry, &page_cur);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (low_match == dtuple_get_n_fields(entry)) {
|
|
|
|
page_zip_des_t* page_zip= buf_block_get_page_zip(block);
|
|
|
|
page_t* page = buf_block_get_frame(block);
|
|
|
|
rec_t* rec = page_cur_get_rec(&page_cur);
|
|
|
|
|
|
|
|
/* TODO: the below should probably be a separate function,
|
|
|
|
it's a bastardized version of btr_cur_optimistic_delete. */
|
|
|
|
|
2020-04-28 10:46:51 +10:00
|
|
|
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
|
|
|
|
rec_offs* offsets = offsets_;
|
2014-02-26 19:11:54 +01:00
|
|
|
mem_heap_t* heap = NULL;
|
|
|
|
ulint max_ins_size = 0;
|
|
|
|
|
|
|
|
rec_offs_init(offsets_);
|
|
|
|
|
2021-04-13 10:28:13 +03:00
|
|
|
offsets = rec_get_offsets(rec, index, offsets, index->n_fields,
|
|
|
|
ULINT_UNDEFINED, &heap);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_get_n_recs(page) <= 1
|
|
|
|
|| !(REC_INFO_DELETED_FLAG
|
|
|
|
& rec_get_info_bits(rec, page_is_comp(page)))) {
|
|
|
|
/* Refuse to purge the last record or a
|
|
|
|
record that has not been marked for deletion. */
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::error() << "Unable to purge a record";
|
2014-02-26 19:11:54 +01:00
|
|
|
fputs("InnoDB: tuple ", stderr);
|
|
|
|
dtuple_print(stderr, entry);
|
|
|
|
fputs("\n"
|
|
|
|
"InnoDB: record ", stderr);
|
|
|
|
rec_print_new(stderr, rec, offsets);
|
2016-08-12 11:17:45 +03:00
|
|
|
fprintf(stderr, "\nspace " UINT32PF " offset " UINT32PF
|
2014-02-26 19:11:54 +01:00
|
|
|
" (%u records, index id %llu)\n"
|
|
|
|
"InnoDB: Submit a detailed bug report"
|
2017-10-26 14:11:38 +03:00
|
|
|
" to https://jira.mariadb.org/\n",
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
block->page.id().space(),
|
|
|
|
block->page.id().page_no(),
|
2014-02-26 19:11:54 +01:00
|
|
|
(unsigned) page_get_n_recs(page),
|
|
|
|
(ulonglong) btr_page_get_index_id(page));
|
|
|
|
|
|
|
|
ut_ad(0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock_update_delete(block, rec);
|
|
|
|
|
|
|
|
if (!page_zip) {
|
|
|
|
max_ins_size
|
|
|
|
= page_get_max_insert_size_after_reorganize(
|
|
|
|
page, 1);
|
|
|
|
}
|
|
|
|
#ifdef UNIV_ZIP_DEBUG
|
|
|
|
ut_a(!page_zip || page_zip_validate(page_zip, page, index));
|
|
|
|
#endif /* UNIV_ZIP_DEBUG */
|
|
|
|
page_cur_delete_rec(&page_cur, index, offsets, mtr);
|
|
|
|
#ifdef UNIV_ZIP_DEBUG
|
|
|
|
ut_a(!page_zip || page_zip_validate(page_zip, page, index));
|
|
|
|
#endif /* UNIV_ZIP_DEBUG */
|
|
|
|
|
|
|
|
if (page_zip) {
|
|
|
|
ibuf_update_free_bits_zip(block, mtr);
|
|
|
|
} else {
|
|
|
|
ibuf_update_free_bits_low(block, max_ins_size, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UNIV_LIKELY_NULL(heap)) {
|
|
|
|
mem_heap_free(heap);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* The record must have been purged already. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Restores insert buffer tree cursor position
|
2020-10-15 16:28:19 +03:00
|
|
|
@return whether the position was restored */
|
2016-06-21 14:21:03 +02:00
|
|
|
static MY_ATTRIBUTE((nonnull))
|
2020-10-15 16:28:19 +03:00
|
|
|
bool
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_restore_pos(
|
|
|
|
/*=============*/
|
2020-10-15 16:28:19 +03:00
|
|
|
const page_id_t page_id,/*!< in: page identifier */
|
2014-02-26 19:11:54 +01:00
|
|
|
const dtuple_t* search_tuple,
|
|
|
|
/*!< in: search tuple for entries of page_no */
|
|
|
|
ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
|
|
|
|
btr_pcur_t* pcur, /*!< in/out: persistent cursor whose
|
|
|
|
position is to be restored */
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mode == BTR_MODIFY_LEAF
|
|
|
|
|| BTR_LATCH_MODE_WITHOUT_INTENTION(mode) == BTR_MODIFY_TREE);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
MDEV-20605 Awaken transaction can miss inserted by other transaction records due to wrong persistent cursor restoration
sel_restore_position_for_mysql() moves forward persistent cursor
position after btr_pcur_restore_position() call if cursor relative position
is BTR_PCUR_ON and the cursor points to the record with NOT the same field
values as in a stored record(and some other not important for this case
conditions).
It was done because btr_pcur_restore_position() sets
page_cur_mode_t mode to PAGE_CUR_LE for cursor->rel_pos == BTR_PCUR_ON
before opening cursor. So we are searching for the record less or equal
to stored one. And if the found record is not equal to stored one, then
it is less and we need to move cursor forward.
But there can be a situation when the stored record was purged, but the
new one with the same key but different value was inserted while
row_search_mvcc() was suspended. In this case, when the thread is
awaken, it will invoke sel_restore_position_for_mysql(), which, in turns,
invoke btr_pcur_restore_position(), which will return false because found
record don't match stored record, and
sel_restore_position_for_mysql() will move forward cursor position.
The above can lead to the case when awaken row_search_mvcc() do not see
records inserted by other transactions while it slept. The mtr test case
shows the example how it can be.
The fix is to return special value from persistent cursor restoring
function which would notify its caller that uniq fields of restored
record and stored record are the same, and in this case
sel_restore_position_for_mysql() don't move cursor forward.
Delete-marked records are correctly processed in row_search_mvcc().
Non-unique secondary indexes are "uniquified" by adding the PK, the
index->n_uniq should then be index->n_fields. So there is no need in
additional checks in the fix.
If transaction's readview can't see the changes made in secondary index
record, it requests clustered index record in row_search_mvcc() to check
its transaction id and get the correspondent record version. After this
row_search_mvcc() commits mtr to preserve clustered index latching
order, and starts mtr. Between those mtr commit and start secondary
index pages are unlatched, and purge has the ability to remove stored in
the cursor record, what causes rows duplication in result set for
non-locking reads, as cursor position is restored to the previously
visited record.
To solve this the changes are just switched off for non-locking reads,
it's quite simple solution, besides the changes don't make sense for
non-locking reads.
The more complex and effective from performance perspective solution is
to create mtr savepoint before clustered record requesting and rolling
back to that savepoint after that. See MDEV-27557.
One more solution is to have per-record transaction id for secondary
indexes. See MDEV-17598.
If any of those is implemented, just remove select_lock_type argument in
sel_restore_position_for_mysql().
2021-11-30 18:22:39 +03:00
|
|
|
if (UNIV_LIKELY(btr_pcur_restore_position(mode, pcur, mtr) ==
|
|
|
|
btr_pcur_t::SAME_ALL)) {
|
2020-10-15 16:28:19 +03:00
|
|
|
return true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2020-10-26 16:04:12 +02:00
|
|
|
if (fil_space_t* s = fil_space_t::get(page_id.space())) {
|
2018-05-09 12:07:55 +03:00
|
|
|
ib::error() << "ibuf cursor restoration fails!"
|
2016-08-12 11:17:45 +03:00
|
|
|
" ibuf record inserted to page "
|
2020-10-15 16:28:19 +03:00
|
|
|
<< page_id
|
2018-05-09 12:07:55 +03:00
|
|
|
<< " in file " << s->chain.start->name;
|
2018-05-09 16:52:45 +03:00
|
|
|
s->release();
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
ib::error() << BUG_REPORT_MSG;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
rec_print_old(stderr, btr_pcur_get_rec(pcur));
|
|
|
|
rec_print_old(stderr, pcur->old_rec);
|
|
|
|
dtuple_print(stderr, search_tuple);
|
|
|
|
|
|
|
|
rec_print_old(stderr,
|
|
|
|
page_rec_get_next(btr_pcur_get_rec(pcur)));
|
|
|
|
}
|
|
|
|
|
2018-05-09 12:07:55 +03:00
|
|
|
ibuf_btr_pcur_commit_specify_mtr(pcur, mtr);
|
2020-10-15 16:28:19 +03:00
|
|
|
return false;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2019-11-07 10:34:33 +02:00
|
|
|
/**
|
|
|
|
Delete a change buffer record.
|
2020-10-15 16:28:19 +03:00
|
|
|
@param[in] page_id page identifier
|
2019-11-07 10:34:33 +02:00
|
|
|
@param[in,out] pcur persistent cursor positioned on the record
|
|
|
|
@param[in] search_tuple search key for (space,page_no)
|
|
|
|
@param[in,out] mtr mini-transaction
|
|
|
|
@return whether mtr was committed (due to pessimistic operation) */
|
|
|
|
static MY_ATTRIBUTE((warn_unused_result, nonnull))
|
2020-10-15 16:28:19 +03:00
|
|
|
bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur,
|
2019-11-07 10:34:33 +02:00
|
|
|
const dtuple_t* search_tuple, mtr_t* mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ibool success;
|
|
|
|
page_t* root;
|
|
|
|
dberr_t err;
|
|
|
|
|
|
|
|
ut_ad(ibuf_inside(mtr));
|
|
|
|
ut_ad(page_rec_is_user_rec(btr_pcur_get_rec(pcur)));
|
2020-10-15 16:28:19 +03:00
|
|
|
ut_ad(ibuf_rec_get_page_no(mtr, btr_pcur_get_rec(pcur))
|
|
|
|
== page_id.page_no());
|
|
|
|
ut_ad(ibuf_rec_get_space(mtr, btr_pcur_get_rec(pcur))
|
|
|
|
== page_id.space());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur),
|
|
|
|
0, mtr);
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
if (page_is_empty(btr_pcur_get_page(pcur))) {
|
|
|
|
/* If a B-tree page is empty, it must be the root page
|
|
|
|
and the whole B-tree must be empty. InnoDB does not
|
|
|
|
allow empty B-tree pages other than the root. */
|
|
|
|
root = btr_pcur_get_page(pcur);
|
|
|
|
|
|
|
|
ut_ad(page_get_space_id(root) == IBUF_SPACE_ID);
|
|
|
|
ut_ad(page_get_page_no(root)
|
|
|
|
== FSP_IBUF_TREE_ROOT_PAGE_NO);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
/* ibuf.empty is protected by the root page latch.
|
2014-02-26 19:11:54 +01:00
|
|
|
Before the deletion, it had to be FALSE. */
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(!ibuf.empty);
|
|
|
|
ibuf.empty = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We have to resort to a pessimistic delete from ibuf.
|
|
|
|
Delete-mark the record so that it will not be applied again,
|
|
|
|
in case the server crashes before the pessimistic delete is
|
|
|
|
made persistent. */
|
2020-02-07 13:03:02 +02:00
|
|
|
btr_rec_set_deleted<true>(btr_pcur_get_block(pcur),
|
|
|
|
btr_pcur_get_rec(pcur), mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
btr_pcur_store_position(pcur, mtr);
|
|
|
|
ibuf_btr_pcur_commit_specify_mtr(pcur, mtr);
|
|
|
|
|
|
|
|
ibuf_mtr_start(mtr);
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
if (!ibuf_restore_pos(page_id, search_tuple,
|
2016-08-12 11:17:45 +03:00
|
|
|
BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE,
|
|
|
|
pcur, mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr->has_committed());
|
2014-02-26 19:11:54 +01:00
|
|
|
goto func_exit;
|
|
|
|
}
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
root = ibuf_tree_root_get(mtr)->frame;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
btr_cur_pessimistic_delete(&err, TRUE, btr_pcur_get_btr_cur(pcur), 0,
|
2016-08-12 11:17:45 +03:00
|
|
|
false, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_a(err == DB_SUCCESS);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ibuf_size_update(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.empty = page_is_empty(root);
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_btr_pcur_commit_specify_mtr(pcur, mtr);
|
|
|
|
|
|
|
|
func_exit:
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr->has_committed());
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_close(pcur);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
/** Check whether buffered changes exist for a page.
|
2020-01-13 17:37:37 +02:00
|
|
|
@param[in] id page identifier
|
|
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
@return whether buffered changes exist */
|
2020-01-13 17:37:37 +02:00
|
|
|
bool ibuf_page_exists(const page_id_t id, ulint zip_size)
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
{
|
2020-01-13 17:37:37 +02:00
|
|
|
ut_ad(!fsp_is_system_temporary(id.space()));
|
|
|
|
|
|
|
|
const ulint physical_size = zip_size ? zip_size : srv_page_size;
|
|
|
|
|
|
|
|
if (ibuf_fixed_addr_page(id, physical_size)
|
|
|
|
|| fsp_descr_page(id, physical_size)) {
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mtr_t mtr;
|
|
|
|
bool bitmap_bits = false;
|
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
2019-12-03 10:19:45 +02:00
|
|
|
if (const buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(
|
2020-01-13 17:37:37 +02:00
|
|
|
id, zip_size, &mtr)) {
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
bitmap_bits = ibuf_bitmap_page_get_bits(
|
2020-01-13 17:37:37 +02:00
|
|
|
bitmap_page->frame, id, zip_size,
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
IBUF_BITMAP_BUFFERED, &mtr) != 0;
|
|
|
|
}
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
return bitmap_bits;
|
|
|
|
}
|
|
|
|
|
2021-01-14 14:26:24 +02:00
|
|
|
/** Reset the bits in the bitmap page for the given block and page id.
|
|
|
|
@param b X-latched secondary index page (nullptr to discard changes)
|
|
|
|
@param page_id page identifier
|
|
|
|
@param zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
|
|
|
@param mtr mini-transaction */
|
|
|
|
static void ibuf_reset_bitmap(buf_block_t *b, page_id_t page_id,
|
|
|
|
ulint zip_size, mtr_t *mtr)
|
|
|
|
{
|
|
|
|
buf_block_t *bitmap= ibuf_bitmap_get_map_page(page_id, zip_size, mtr);
|
|
|
|
if (!bitmap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const ulint physical_size = zip_size ? zip_size : srv_page_size;
|
|
|
|
/* FIXME: update the bitmap byte only once! */
|
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_BUFFERED>(bitmap, page_id,
|
|
|
|
physical_size, false, mtr);
|
|
|
|
|
|
|
|
if (b)
|
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_FREE>(bitmap, page_id, physical_size,
|
|
|
|
ibuf_index_page_calc_free(b),
|
|
|
|
mtr);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** When an index page is read from a disk to the buffer pool, this function
|
2014-02-26 19:11:54 +01:00
|
|
|
applies any buffered operations to the page and deletes the entries from the
|
|
|
|
insert buffer. If the page is not read, but created in the buffer pool, this
|
|
|
|
function deletes its buffered entries from the insert buffer; there can
|
|
|
|
exist entries for such a page if the page belonged to an index which
|
2016-08-12 11:17:45 +03:00
|
|
|
subsequently was dropped.
|
2020-11-12 11:18:04 +02:00
|
|
|
@param block X-latched page to try to apply changes to, or NULL to discard
|
|
|
|
@param page_id page identifier
|
|
|
|
@param zip_size ROW_FORMAT=COMPRESSED page size, or 0 */
|
|
|
|
void ibuf_merge_or_delete_for_page(buf_block_t *block, const page_id_t page_id,
|
|
|
|
ulint zip_size)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2021-05-27 16:46:11 +03:00
|
|
|
if (trx_sys_hdr_page(page_id)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_t pcur;
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
ulint volume = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
#endif /* UNIV_IBUF_DEBUG */
|
|
|
|
bool corruption_noticed = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_t mtr;
|
|
|
|
|
|
|
|
/* Counts for merged & discarded operations. */
|
|
|
|
ulint mops[IBUF_OP_COUNT];
|
|
|
|
ulint dops[IBUF_OP_COUNT];
|
|
|
|
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
ut_ad(!block || page_id == block->page.id());
|
|
|
|
ut_ad(!block || block->page.state() == BUF_BLOCK_FILE_PAGE);
|
2020-05-07 17:57:03 +03:00
|
|
|
ut_ad(!block || block->page.status == buf_page_t::NORMAL);
|
2021-05-27 16:46:11 +03:00
|
|
|
ut_ad(!trx_sys_hdr_page(page_id));
|
|
|
|
ut_ad(page_id < page_id_t(SRV_SPACE_ID_UPPER_BOUND, 0));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-02-06 19:50:11 +02:00
|
|
|
const ulint physical_size = zip_size ? zip_size : srv_page_size;
|
|
|
|
|
|
|
|
if (ibuf_fixed_addr_page(page_id, physical_size)
|
|
|
|
|| fsp_descr_page(page_id, physical_size)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-13 21:54:21 +02:00
|
|
|
fil_space_t* space = fil_space_t::get(page_id.space());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-11-10 09:47:29 +02:00
|
|
|
if (UNIV_UNLIKELY(!space)) {
|
|
|
|
block = NULL;
|
|
|
|
} else {
|
|
|
|
ulint bitmap_bits = 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-11-10 09:47:29 +02:00
|
|
|
ibuf_mtr_start(&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-11-13 21:54:21 +02:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(
|
2020-11-12 11:18:04 +02:00
|
|
|
page_id, zip_size, &mtr);
|
2015-09-24 14:02:18 +03:00
|
|
|
|
2020-11-13 21:54:21 +02:00
|
|
|
if (bitmap_page
|
|
|
|
&& fil_page_get_type(bitmap_page->frame)
|
|
|
|
!= FIL_PAGE_TYPE_ALLOCATED) {
|
2020-11-10 09:47:29 +02:00
|
|
|
bitmap_bits = ibuf_bitmap_page_get_bits(
|
2020-11-13 21:54:21 +02:00
|
|
|
bitmap_page->frame, page_id, zip_size,
|
2020-11-10 09:47:29 +02:00
|
|
|
IBUF_BITMAP_BUFFERED, &mtr);
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-11-10 09:47:29 +02:00
|
|
|
ibuf_mtr_commit(&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2021-01-14 14:26:24 +02:00
|
|
|
if (bitmap_bits && fseg_page_is_free(
|
|
|
|
space, page_id.page_no())) {
|
|
|
|
ibuf_mtr_start(&mtr);
|
2021-02-26 19:02:26 +05:30
|
|
|
mtr.set_named_space(space);
|
2021-01-14 14:26:24 +02:00
|
|
|
ibuf_reset_bitmap(block, page_id, zip_size, &mtr);
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
bitmap_bits = 0;
|
|
|
|
}
|
|
|
|
|
2020-11-10 09:47:29 +02:00
|
|
|
if (!bitmap_bits) {
|
|
|
|
/* No changes are buffered for this page. */
|
2020-11-12 10:37:21 +02:00
|
|
|
space->release();
|
2020-11-10 09:47:29 +02:00
|
|
|
return;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-06 08:24:48 +02:00
|
|
|
mem_heap_t* heap = mem_heap_create(512);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-11-06 08:24:48 +02:00
|
|
|
const dtuple_t* search_tuple = ibuf_search_tuple_build(
|
2016-08-12 11:17:45 +03:00
|
|
|
page_id.space(), page_id.page_no(), heap);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (block != NULL) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Move the ownership of the x-latch on the page to this OS
|
|
|
|
thread, so that we can acquire a second x-latch on it. This
|
|
|
|
is needed for the insert operations to the index page to pass
|
|
|
|
the debug checks. */
|
|
|
|
|
|
|
|
rw_lock_x_lock_move_ownership(&(block->lock));
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (!fil_page_index_page_check(block->frame)
|
|
|
|
|| !page_is_leaf(block->frame)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
corruption_noticed = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::error() << "Corruption in the tablespace. Bitmap"
|
|
|
|
" shows insert buffer records to page "
|
|
|
|
<< page_id << " though the page type is "
|
|
|
|
<< fil_page_get_type(block->frame)
|
|
|
|
<< ", which is not an index leaf page. We try"
|
|
|
|
" to resolve the problem by skipping the"
|
|
|
|
" insert buffer merge for this page. Please"
|
|
|
|
" run CHECK TABLE on your tables to determine"
|
|
|
|
" if they are corrupt after this.";
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(mops, 0, sizeof(mops));
|
|
|
|
memset(dops, 0, sizeof(dops));
|
|
|
|
|
|
|
|
loop:
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
|
|
|
/* Position pcur in the insert buffer at the first entry for this
|
|
|
|
index page */
|
|
|
|
btr_pcur_open_on_user_rec(
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
|
2014-02-26 19:11:54 +01:00
|
|
|
&pcur, &mtr);
|
|
|
|
|
2020-11-12 10:37:21 +02:00
|
|
|
if (block) {
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ut_ad(rw_lock_own(&block->lock, RW_LOCK_X));
|
|
|
|
buf_block_buf_fix_inc(block, __FILE__, __LINE__);
|
|
|
|
rw_lock_x_lock(&block->lock);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
2014-02-26 19:11:54 +01:00
|
|
|
/* This is a user page (secondary index leaf page),
|
|
|
|
but we pretend that it is a change buffer page in
|
|
|
|
order to obey the latching order. This should be OK,
|
|
|
|
because buffered changes are applied immediately while
|
|
|
|
the block is io-fixed. Other threads must not try to
|
|
|
|
latch an io-fixed block. */
|
|
|
|
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
|
2020-11-12 10:37:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (space) {
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
mtr.set_named_space(space);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!btr_pcur_is_on_user_rec(&pcur)) {
|
2019-11-26 16:32:51 +02:00
|
|
|
ut_ad(btr_pcur_is_after_last_on_page(&pcur));
|
2014-02-26 19:11:54 +01:00
|
|
|
goto reset_bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
rec_t* rec;
|
|
|
|
|
|
|
|
ut_ad(btr_pcur_is_on_user_rec(&pcur));
|
|
|
|
|
|
|
|
rec = btr_pcur_get_rec(&pcur);
|
|
|
|
|
|
|
|
/* Check if the entry is for this index page */
|
2016-08-12 11:17:45 +03:00
|
|
|
if (ibuf_rec_get_page_no(&mtr, rec) != page_id.page_no()
|
|
|
|
|| ibuf_rec_get_space(&mtr, rec) != page_id.space()) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (block != NULL) {
|
2019-12-03 10:19:45 +02:00
|
|
|
page_header_reset_last_insert(block, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
goto reset_bit;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (corruption_noticed) {
|
2014-02-26 19:11:54 +01:00
|
|
|
fputs("InnoDB: Discarding record\n ", stderr);
|
|
|
|
rec_print_old(stderr, rec);
|
|
|
|
fputs("\nInnoDB: from the insert buffer!\n\n", stderr);
|
2016-08-12 11:17:45 +03:00
|
|
|
} else if (block != NULL && !rec_get_deleted_flag(rec, 0)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Now we have at pcur a record which should be
|
|
|
|
applied on the index page; NOTE that the call below
|
|
|
|
copies pointers to fields in rec, and we must
|
|
|
|
keep the latch to the rec page until the
|
|
|
|
insertion is finished! */
|
|
|
|
dtuple_t* entry;
|
|
|
|
trx_id_t max_trx_id;
|
|
|
|
dict_index_t* dummy_index;
|
|
|
|
ibuf_op_t op = ibuf_rec_get_op_type(&mtr, rec);
|
|
|
|
|
|
|
|
max_trx_id = page_get_max_trx_id(page_align(rec));
|
2019-12-03 10:19:45 +02:00
|
|
|
page_update_max_trx_id(block,
|
|
|
|
buf_block_get_page_zip(block),
|
|
|
|
max_trx_id, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_ad(page_validate(page_align(rec), ibuf.index));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
entry = ibuf_build_entry_from_ibuf_rec(
|
|
|
|
&mtr, rec, heap, &dummy_index);
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
ut_ad(!dummy_index->table->space);
|
|
|
|
dummy_index->table->space = space;
|
|
|
|
dummy_index->table->space_id = space->id;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_ad(page_validate(block->frame, dummy_index));
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case IBUF_OP_INSERT:
|
|
|
|
#ifdef UNIV_IBUF_DEBUG
|
|
|
|
volume += rec_get_converted_size(
|
|
|
|
dummy_index, entry, 0);
|
|
|
|
|
|
|
|
volume += page_dir_calc_reserved_space(1);
|
|
|
|
|
2018-04-27 14:26:43 +03:00
|
|
|
ut_a(volume <= (4U << srv_page_size_shift)
|
|
|
|
/ IBUF_PAGE_SIZE_PER_FREE_SPACE);
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif
|
|
|
|
ibuf_insert_to_index_page(
|
|
|
|
entry, block, dummy_index, &mtr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IBUF_OP_DELETE_MARK:
|
|
|
|
ibuf_set_del_mark(
|
|
|
|
entry, block, dummy_index, &mtr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IBUF_OP_DELETE:
|
|
|
|
ibuf_delete(entry, block, dummy_index, &mtr);
|
|
|
|
/* Because ibuf_delete() will latch an
|
|
|
|
insert buffer bitmap page, commit mtr
|
|
|
|
before latching any further pages.
|
|
|
|
Store and restore the cursor position. */
|
|
|
|
ut_ad(rec == btr_pcur_get_rec(&pcur));
|
|
|
|
ut_ad(page_rec_is_user_rec(rec));
|
|
|
|
ut_ad(ibuf_rec_get_page_no(&mtr, rec)
|
2016-08-12 11:17:45 +03:00
|
|
|
== page_id.page_no());
|
|
|
|
ut_ad(ibuf_rec_get_space(&mtr, rec)
|
|
|
|
== page_id.space());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Mark the change buffer record processed,
|
|
|
|
so that it will not be merged again in case
|
|
|
|
the server crashes between the following
|
|
|
|
mtr_commit() and the subsequent mtr_commit()
|
|
|
|
of deleting the change buffer record. */
|
2020-02-07 13:03:02 +02:00
|
|
|
btr_rec_set_deleted<true>(
|
|
|
|
btr_pcur_get_block(&pcur),
|
|
|
|
btr_pcur_get_rec(&pcur), &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
btr_pcur_store_position(&pcur, &mtr);
|
|
|
|
ibuf_btr_pcur_commit_specify_mtr(&pcur, &mtr);
|
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
mtr.set_named_space(space);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
MDEV-19514 Defer change buffer merge until pages are requested
We will remove the InnoDB background operation of merging buffered
changes to secondary index leaf pages. Changes will only be merged as a
result of an operation that accesses a secondary index leaf page,
such as a SQL statement that performs a lookup via that index,
or is modifying the index. Also ROLLBACK and some background operations,
such as purging the history of committed transactions, or computing
index cardinality statistics, can cause change buffer merge.
Encryption key rotation will not perform change buffer merge.
The motivation of this change is to simplify the I/O logic and to
allow crash recovery to happen in the background (MDEV-14481).
We also hope that this will reduce the number of "mystery" crashes
due to corrupted data. Because change buffer merge will typically
take place as a result of executing SQL statements, there should be
a clearer connection between the crash and the SQL statements that
were executed when the server crashed.
In many cases, a slight performance improvement was observed.
This is joint work with Thirunarayanan Balathandayuthapani
and was tested by Axel Schwenke and Matthias Leich.
The InnoDB monitor counter innodb_ibuf_merge_usec will be removed.
On slow shutdown (innodb_fast_shutdown=0), we will continue to
merge all buffered changes (and purge all undo log history).
Two InnoDB configuration parameters will be changed as follows:
innodb_disable_background_merge: Removed.
This parameter existed only in debug builds.
All change buffer merges will use synchronous reads.
innodb_force_recovery will be changed as follows:
* innodb_force_recovery=4 will be the same as innodb_force_recovery=3
(the change buffer merge cannot be disabled; it can only happen as
a result of an operation that accesses a secondary index leaf page).
The option used to be capable of corrupting secondary index leaf pages.
Now that capability is removed, and innodb_force_recovery=4 becomes 'safe'.
* innodb_force_recovery=5 (which essentially hard-wires
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
becomes safe to use. Bogus data can be returned to SQL, but
persistent InnoDB data files will not be corrupted further.
* innodb_force_recovery=6 (ignore the redo log files)
will be the only option that can potentially cause
persistent corruption of InnoDB data files.
Code changes:
buf_page_t::ibuf_exist: New flag, to indicate whether buffered
changes exist for a buffer pool page. Pages with pending changes
can be returned by buf_page_get_gen(). Previously, the changes
were always merged inside buf_page_get_gen() if needed.
ibuf_page_exists(const buf_page_t&): Check if a buffered changes
exist for an X-latched or read-fixed page.
buf_page_get_gen(): Add the parameter allow_ibuf_merge=false.
All callers that know that they may be accessing a secondary index
leaf page must pass this parameter as allow_ibuf_merge=true,
unless it does not matter for that caller whether all buffered
changes have been applied. Assert that whenever allow_ibuf_merge
holds, the page actually is a leaf page. Attempt change buffer
merge only to secondary B-tree index leaf pages.
btr_block_get(): Add parameter 'bool merge'.
All callers of btr_block_get() should know whether the page could be
a secondary index leaf page. If it is not, we should avoid consulting
the change buffer bitmap to even consider a merge. This is the main
interface to requesting index pages from the buffer pool.
ibuf_merge_or_delete_for_page(), recv_recover_page(): Replace
buf_page_get_known_nowait() with much simpler logic, because
it is now guaranteed that that the block is x-latched or read-fixed.
mlog_init_t::mark_ibuf_exist(): Renamed from mlog_init_t::ibuf_merge().
On crash recovery, we will no longer merge any buffered changes
for the pages that we read into the buffer pool during the last batch
of applying log records.
buf_page_get_gen_known_nowait(), BUF_MAKE_YOUNG, BUF_KEEP_OLD: Remove.
btr_search_guess_on_hash(): Merge buf_page_get_gen_known_nowait()
to its only remaining caller.
buf_page_make_young_if_needed(): Define as an inline function.
Add the parameter buf_pool.
buf_page_peek_if_young(), buf_page_peek_if_too_old(): Add the
parameter buf_pool.
fil_space_validate_for_mtr_commit(): Remove a bogus comment
about background merge of the change buffer.
btr_cur_open_at_rnd_pos_func(), btr_cur_search_to_nth_level_func(),
btr_cur_open_at_index_side_func(): Use narrower data types and scopes.
ibuf_read_merge_pages(): Replaces buf_read_ibuf_merge_pages().
Merge the change buffer by invoking buf_page_get_gen().
2019-10-11 17:28:15 +03:00
|
|
|
ut_ad(rw_lock_own(&block->lock, RW_LOCK_X));
|
|
|
|
buf_block_buf_fix_inc(block,
|
|
|
|
__FILE__, __LINE__);
|
|
|
|
rw_lock_x_lock(&block->lock);
|
|
|
|
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* This is a user page (secondary
|
|
|
|
index leaf page), but it should be OK
|
|
|
|
to use too low latching order for it,
|
|
|
|
as the block is io-fixed. */
|
|
|
|
buf_block_dbg_add_level(
|
|
|
|
block, SYNC_IBUF_TREE_NODE);
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
if (!ibuf_restore_pos(page_id, search_tuple,
|
2014-02-26 19:11:54 +01:00
|
|
|
BTR_MODIFY_LEAF,
|
|
|
|
&pcur, &mtr)) {
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr.has_committed());
|
2014-02-26 19:11:54 +01:00
|
|
|
mops[op]++;
|
|
|
|
ibuf_dummy_index_free(dummy_index);
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
mops[op]++;
|
|
|
|
|
|
|
|
ibuf_dummy_index_free(dummy_index);
|
|
|
|
} else {
|
|
|
|
dops[ibuf_rec_get_op_type(&mtr, rec)]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete the record from ibuf */
|
2020-10-15 16:28:19 +03:00
|
|
|
if (ibuf_delete_rec(page_id, &pcur, search_tuple, &mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Deletion was pessimistic and mtr was committed:
|
|
|
|
we start from the beginning again */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr.has_committed());
|
2014-02-26 19:11:54 +01:00
|
|
|
goto loop;
|
|
|
|
} else if (btr_pcur_is_after_last_on_page(&pcur)) {
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reset_bit:
|
2021-01-14 14:26:24 +02:00
|
|
|
if (space) {
|
|
|
|
ibuf_reset_bitmap(block, page_id, zip_size, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_mtr_commit(&mtr);
|
2017-03-30 12:48:42 +02:00
|
|
|
|
|
|
|
if (space) {
|
2018-04-23 13:15:54 +03:00
|
|
|
space->release();
|
2017-03-30 12:48:42 +02:00
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.n_merges++;
|
|
|
|
ibuf_add_ops(ibuf.n_merged_ops, mops);
|
|
|
|
ibuf_add_ops(ibuf.n_discarded_ops, dops);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2019-09-26 10:25:34 +03:00
|
|
|
/** Delete all change buffer entries for a tablespace,
|
2022-11-08 10:55:22 +02:00
|
|
|
in DISCARD TABLESPACE, IMPORT TABLESPACE, or read-ahead.
|
2019-09-26 10:25:34 +03:00
|
|
|
@param[in] space missing or to-be-discarded tablespace */
|
|
|
|
void ibuf_delete_for_discarded_space(ulint space)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
mem_heap_t* heap;
|
|
|
|
btr_pcur_t pcur;
|
|
|
|
dtuple_t* search_tuple;
|
|
|
|
const rec_t* ibuf_rec;
|
|
|
|
mtr_t mtr;
|
|
|
|
|
|
|
|
/* Counts for discarded operations. */
|
|
|
|
ulint dops[IBUF_OP_COUNT];
|
|
|
|
|
|
|
|
heap = mem_heap_create(512);
|
|
|
|
|
|
|
|
/* Use page number 0 to build the search tuple so that we get the
|
|
|
|
cursor positioned at the first entry for this space id */
|
|
|
|
|
|
|
|
search_tuple = ibuf_search_tuple_build(space, 0, heap);
|
|
|
|
|
|
|
|
memset(dops, 0, sizeof(dops));
|
|
|
|
loop:
|
2022-11-08 11:37:43 +02:00
|
|
|
log_free_check();
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
|
|
|
/* Position pcur in the insert buffer at the first entry for the
|
|
|
|
space */
|
|
|
|
btr_pcur_open_on_user_rec(
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
|
2014-02-26 19:11:54 +01:00
|
|
|
&pcur, &mtr);
|
|
|
|
|
|
|
|
if (!btr_pcur_is_on_user_rec(&pcur)) {
|
2019-11-26 16:32:51 +02:00
|
|
|
ut_ad(btr_pcur_is_after_last_on_page(&pcur));
|
2014-02-26 19:11:54 +01:00
|
|
|
goto leave_loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ut_ad(btr_pcur_is_on_user_rec(&pcur));
|
|
|
|
|
|
|
|
ibuf_rec = btr_pcur_get_rec(&pcur);
|
|
|
|
|
|
|
|
/* Check if the entry is for this space */
|
|
|
|
if (ibuf_rec_get_space(&mtr, ibuf_rec) != space) {
|
|
|
|
|
|
|
|
goto leave_loop;
|
|
|
|
}
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
uint32_t page_no = ibuf_rec_get_page_no(&mtr, ibuf_rec);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
dops[ibuf_rec_get_op_type(&mtr, ibuf_rec)]++;
|
|
|
|
|
|
|
|
/* Delete the record from ibuf */
|
2020-10-15 16:28:19 +03:00
|
|
|
if (ibuf_delete_rec(page_id_t(space, page_no),
|
|
|
|
&pcur, search_tuple, &mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Deletion was pessimistic and mtr was committed:
|
|
|
|
we start from the beginning again */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr.has_committed());
|
2014-02-26 19:11:54 +01:00
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (btr_pcur_is_after_last_on_page(&pcur)) {
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
leave_loop:
|
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf_add_ops(ibuf.n_discarded_ops, dops);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Looks if the insert buffer is empty.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return true if empty */
|
2014-02-26 19:11:54 +01:00
|
|
|
bool
|
|
|
|
ibuf_is_empty(void)
|
|
|
|
/*===============*/
|
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
|
|
|
|
ibuf_mtr_start(&mtr);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ut_d(mutex_enter(&ibuf_mutex));
|
|
|
|
const buf_block_t* root = ibuf_tree_root_get(&mtr);
|
|
|
|
bool is_empty = page_is_empty(root->frame);
|
2019-07-03 16:05:34 +03:00
|
|
|
ut_a(is_empty == ibuf.empty);
|
2019-12-03 10:19:45 +02:00
|
|
|
ut_d(mutex_exit(&ibuf_mutex));
|
2014-02-26 19:11:54 +01:00
|
|
|
ibuf_mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return(is_empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
|
|
Prints info of ibuf. */
|
|
|
|
void
|
|
|
|
ibuf_print(
|
|
|
|
/*=======*/
|
|
|
|
FILE* file) /*!< in: file where to print */
|
|
|
|
{
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
|
|
|
fprintf(file,
|
2017-04-21 05:51:27 +03:00
|
|
|
"Ibuf: size " ULINTPF ", free list len " ULINTPF ","
|
|
|
|
" seg size " ULINTPF ", " ULINTPF " merges\n",
|
2022-11-14 15:40:28 +02:00
|
|
|
ulint{ibuf.size},
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf.free_list_len,
|
|
|
|
ibuf.seg_size,
|
|
|
|
ulint{ibuf.n_merges});
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
fputs("merged operations:\n ", file);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf_print_ops(ibuf.n_merged_ops, file);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
fputs("discarded operations:\n ", file);
|
2019-07-03 16:05:34 +03:00
|
|
|
ibuf_print_ops(ibuf.n_discarded_ops, file);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
}
|
|
|
|
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
/** Check the insert buffer bitmaps on IMPORT TABLESPACE.
|
|
|
|
@param[in] trx transaction
|
|
|
|
@param[in,out] space tablespace being imported
|
2014-02-26 19:11:54 +01:00
|
|
|
@return DB_SUCCESS or error code */
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
dberr_t ibuf_check_bitmap_on_import(const trx_t* trx, fil_space_t* space)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ut_ad(trx->mysql_thd);
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
ut_ad(space->purpose == FIL_TYPE_IMPORT);
|
2019-02-06 19:50:11 +02:00
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
const unsigned zip_size = space->zip_size();
|
|
|
|
const unsigned physical_size = space->physical_size();
|
2017-11-07 23:02:39 +02:00
|
|
|
|
2020-10-26 15:59:30 +02:00
|
|
|
uint32_t size= std::min(space->free_limit, space->size);
|
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
return(DB_TABLE_NOT_FOUND);
|
2017-11-06 14:55:34 +02:00
|
|
|
}
|
2020-10-26 15:59:30 +02:00
|
|
|
|
|
|
|
mtr_t mtr;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_enter(&ibuf_mutex);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/* The two bitmap pages (allocation bitmap and ibuf bitmap) repeat
|
|
|
|
every page_size pages. For example if page_size is 16 KiB, then the
|
|
|
|
two bitmap pages repeat every 16 KiB * 16384 = 256 MiB. In the loop
|
|
|
|
below page_no is measured in number of pages since the beginning of
|
|
|
|
the space, as usual. */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
for (uint32_t page_no = 0; page_no < size; page_no += physical_size) {
|
2014-02-26 19:11:54 +01:00
|
|
|
if (trx_is_interrupted(trx)) {
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
return(DB_INTERRUPTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
ibuf_enter(&mtr);
|
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(
|
2019-02-06 19:50:11 +02:00
|
|
|
page_id_t(space->id, page_no), zip_size, &mtr);
|
2019-12-03 10:19:45 +02:00
|
|
|
if (!bitmap_page) {
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
mtr.commit();
|
|
|
|
return DB_CORRUPTION;
|
|
|
|
}
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2020-03-30 18:52:17 +03:00
|
|
|
if (buf_is_zeroes(span<const byte>(bitmap_page->frame,
|
2020-03-30 14:50:23 +03:00
|
|
|
physical_size))) {
|
2016-08-12 11:17:45 +03:00
|
|
|
/* This means we got all-zero page instead of
|
|
|
|
ibuf bitmap page. The subsequent page should be
|
|
|
|
all-zero pages. */
|
|
|
|
#ifdef UNIV_DEBUG
|
2020-10-15 16:28:19 +03:00
|
|
|
for (uint32_t curr_page = page_no + 1;
|
2019-02-06 19:50:11 +02:00
|
|
|
curr_page < physical_size; curr_page++) {
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
buf_block_t* block = buf_page_get(
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
page_id_t(space->id, curr_page),
|
2019-02-06 19:50:11 +02:00
|
|
|
zip_size, RW_S_LATCH, &mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
page_t* page = buf_block_get_frame(block);
|
2020-03-18 14:57:01 +03:00
|
|
|
ut_ad(buf_is_zeroes(span<const byte>(
|
2020-03-30 14:50:23 +03:00
|
|
|
page,
|
|
|
|
physical_size)));
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
ibuf_exit(&mtr);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 16:28:19 +03:00
|
|
|
for (uint32_t i = FSP_IBUF_BITMAP_OFFSET + 1; i < physical_size;
|
2019-12-03 10:19:45 +02:00
|
|
|
i++) {
|
2020-10-15 16:28:19 +03:00
|
|
|
const uint32_t offset = page_no + i;
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
const page_id_t cur_page_id(space->id, offset);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
if (ibuf_bitmap_page_get_bits(
|
2019-12-03 10:19:45 +02:00
|
|
|
bitmap_page->frame, cur_page_id, zip_size,
|
2019-02-06 19:50:11 +02:00
|
|
|
IBUF_BITMAP_IBUF, &mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
ibuf_exit(&mtr);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
ib_errf(trx->mysql_thd,
|
|
|
|
IB_LOG_LEVEL_ERROR,
|
|
|
|
ER_INNODB_INDEX_CORRUPT,
|
2020-10-15 16:28:19 +03:00
|
|
|
"File %s page %u"
|
2014-02-26 19:11:54 +01:00
|
|
|
" is wrongly flagged to belong to the"
|
|
|
|
" insert buffer",
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
space->chain.start->name, offset);
|
2014-02-26 19:11:54 +01:00
|
|
|
return(DB_CORRUPTION);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ibuf_bitmap_page_get_bits(
|
2019-12-03 10:19:45 +02:00
|
|
|
bitmap_page->frame, cur_page_id, zip_size,
|
2014-02-26 19:11:54 +01:00
|
|
|
IBUF_BITMAP_BUFFERED, &mtr)) {
|
|
|
|
|
|
|
|
ib_errf(trx->mysql_thd,
|
|
|
|
IB_LOG_LEVEL_WARN,
|
|
|
|
ER_INNODB_INDEX_CORRUPT,
|
|
|
|
"Buffered changes"
|
2020-10-15 16:28:19 +03:00
|
|
|
" for file %s page %u are lost",
|
MDEV-12266: Change dict_table_t::space to fil_space_t*
InnoDB always keeps all tablespaces in the fil_system cache.
The fil_system.LRU is only for closing file handles; the
fil_space_t and fil_node_t for all data files will remain
in main memory. Between startup to shutdown, they can only be
created and removed by DDL statements. Therefore, we can
let dict_table_t::space point directly to the fil_space_t.
dict_table_t::space_id: A numeric tablespace ID for the corner cases
where we do not have a tablespace. The most prominent examples are
ALTER TABLE...DISCARD TABLESPACE or a missing or corrupted file.
There are a few functional differences; most notably:
(1) DROP TABLE will delete matching .ibd and .cfg files,
even if they were not attached to the data dictionary.
(2) Some error messages will report file names instead of numeric IDs.
There still are many functions that use numeric tablespace IDs instead
of fil_space_t*, and many functions could be converted to fil_space_t
member functions. Also, Tablespace and Datafile should be merged with
fil_space_t and fil_node_t. page_id_t and buf_page_get_gen() could use
fil_space_t& instead of a numeric ID, and after moving to a single
buffer pool (MDEV-15058), buf_pool_t::page_hash could be moved to
fil_space_t::page_hash.
FilSpace: Remove. Only few calls to fil_space_acquire() will remain,
and gradually they should be removed.
mtr_t::set_named_space_id(ulint): Renamed from set_named_space(),
to prevent accidental calls to this slower function. Very few
callers remain.
fseg_create(), fsp_reserve_free_extents(): Take fil_space_t*
as a parameter instead of a space_id.
fil_space_t::rename(): Wrapper for fil_rename_tablespace_check(),
fil_name_write_rename(), fil_rename_tablespace(). Mariabackup
passes the parameter log=false; InnoDB passes log=true.
dict_mem_table_create(): Take fil_space_t* instead of space_id
as parameter.
dict_process_sys_tables_rec_and_mtr_commit(): Replace the parameter
'status' with 'bool cached'.
dict_get_and_save_data_dir_path(): Avoid copying the fil_node_t::name.
fil_ibd_open(): Return the tablespace.
fil_space_t::set_imported(): Replaces fil_space_set_imported().
truncate_t: Change many member function parameters to fil_space_t*,
and remove page_size parameters.
row_truncate_prepare(): Merge to its only caller.
row_drop_table_from_cache(): Assert that the table is persistent.
dict_create_sys_indexes_tuple(): Write SYS_INDEXES.SPACE=FIL_NULL
if the tablespace has been discarded.
row_import_update_discarded_flag(): Remove a constant parameter.
2018-03-27 16:31:10 +03:00
|
|
|
space->chain.start->name, offset);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Tolerate this error, so that
|
|
|
|
slightly corrupted tables can be
|
|
|
|
imported and dumped. Clear the bit. */
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_BUFFERED>(
|
2019-02-06 19:50:11 +02:00
|
|
|
bitmap_page, cur_page_id,
|
2019-12-03 10:19:45 +02:00
|
|
|
physical_size, false, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf_exit(&mtr);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&ibuf_mutex);
|
|
|
|
return(DB_SUCCESS);
|
|
|
|
}
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2022-11-08 11:37:43 +02:00
|
|
|
void ibuf_set_bitmap_for_bulk_load(buf_block_t *block, mtr_t *mtr, bool reset)
|
2016-08-12 11:17:45 +03:00
|
|
|
{
|
|
|
|
ulint free_val;
|
|
|
|
|
|
|
|
ut_a(page_is_leaf(buf_block_get_frame(block)));
|
|
|
|
|
|
|
|
free_val = ibuf_index_page_calc_free(block);
|
|
|
|
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
buf_block_t* bitmap_page = ibuf_bitmap_get_map_page(block->page.id(),
|
2022-11-08 17:01:28 +02:00
|
|
|
block->zip_size(),
|
|
|
|
mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
free_val = reset ? 0 : ibuf_index_page_calc_free(block);
|
2019-12-03 10:19:45 +02:00
|
|
|
/* FIXME: update the bitmap byte only once! */
|
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_FREE>(
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
bitmap_page, block->page.id(), block->physical_size(),
|
2022-11-08 17:01:28 +02:00
|
|
|
free_val, mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2019-12-03 10:19:45 +02:00
|
|
|
ibuf_bitmap_page_set_bits<IBUF_BITMAP_BUFFERED>(
|
MDEV-15053 Reduce buf_pool_t::mutex contention
User-visible changes: The INFORMATION_SCHEMA views INNODB_BUFFER_PAGE
and INNODB_BUFFER_PAGE_LRU will report a dummy value FLUSH_TYPE=0
and will no longer report the PAGE_STATE value READY_FOR_USE.
We will remove some fields from buf_page_t and move much code to
member functions of buf_pool_t and buf_page_t, so that the access
rules of data members can be enforced consistently.
Evicting or adding pages in buf_pool.LRU will remain covered by
buf_pool.mutex.
Evicting or adding pages in buf_pool.page_hash will remain
covered by both buf_pool.mutex and the buf_pool.page_hash X-latch.
After this fix, buf_pool.page_hash lookups can entirely
avoid acquiring buf_pool.mutex, only relying on
buf_pool.hash_lock_get() S-latch.
Similarly, buf_flush_check_neighbors() can will rely solely on
buf_pool.mutex, no buf_pool.page_hash latch at all.
The buf_pool.mutex is rather contended in I/O heavy benchmarks,
especially when the workload does not fit in the buffer pool.
The first attempt to alleviate the contention was the
buf_pool_t::mutex split in
commit 4ed7082eefe56b3e97e0edefb3df76dd7ef5e858
which introduced buf_block_t::mutex, which we are now removing.
Later, multiple instances of buf_pool_t were introduced
in commit c18084f71b02ea707c6461353e6cfc15d7553bc6
and recently removed by us in
commit 1a6f708ec594ac0ae2dd30db926ab07b100fa24b (MDEV-15058).
UNIV_BUF_DEBUG: Remove. This option to enable some buffer pool
related debugging in otherwise non-debug builds has not been used
for years. Instead, we have been using UNIV_DEBUG, which is enabled
in CMAKE_BUILD_TYPE=Debug.
buf_block_t::mutex, buf_pool_t::zip_mutex: Remove. We can mainly rely on
std::atomic and the buf_pool.page_hash latches, and in some cases
depend on buf_pool.mutex or buf_pool.flush_list_mutex just like before.
We must always release buf_block_t::lock before invoking
unfix() or io_unfix(), to prevent a glitch where a block that was
added to the buf_pool.free list would apper X-latched. See
commit c5883debd6ef440a037011c11873b396923e93c5 how this glitch
was finally caught in a debug environment.
We move some buf_pool_t::page_hash specific code from the
ha and hash modules to buf_pool, for improved readability.
buf_pool_t::close(): Assert that all blocks are clean, except
on aborted startup or crash-like shutdown.
buf_pool_t::validate(): No longer attempt to validate
n_flush[] against the number of BUF_IO_WRITE fixed blocks,
because buf_page_t::flush_type no longer exists.
buf_pool_t::watch_set(): Replaces buf_pool_watch_set().
Reduce mutex contention by separating the buf_pool.watch[]
allocation and the insert into buf_pool.page_hash.
buf_pool_t::page_hash_lock<bool exclusive>(): Acquire a
buf_pool.page_hash latch.
Replaces and extends buf_page_hash_lock_s_confirm()
and buf_page_hash_lock_x_confirm().
buf_pool_t::READ_AHEAD_PAGES: Renamed from BUF_READ_AHEAD_PAGES.
buf_pool_t::curr_size, old_size, read_ahead_area, n_pend_reads:
Use Atomic_counter.
buf_pool_t::running_out(): Replaces buf_LRU_buf_pool_running_out().
buf_pool_t::LRU_remove(): Remove a block from the LRU list
and return its predecessor. Incorporates buf_LRU_adjust_hp(),
which was removed.
buf_page_get_gen(): Remove a redundant call of fsp_is_system_temporary(),
for mode == BUF_GET_IF_IN_POOL_OR_WATCH, which is only used by
BTR_DELETE_OP (purge), which is never invoked on temporary tables.
buf_free_from_unzip_LRU_list_batch(): Avoid redundant assignments.
buf_LRU_free_from_unzip_LRU_list(): Simplify the loop condition.
buf_LRU_free_page(): Clarify the function comment.
buf_flush_check_neighbor(), buf_flush_check_neighbors():
Rewrite the construction of the page hash range. We will hold
the buf_pool.mutex for up to buf_pool.read_ahead_area (at most 64)
consecutive lookups of buf_pool.page_hash.
buf_flush_page_and_try_neighbors(): Remove.
Merge to its only callers, and remove redundant operations in
buf_flush_LRU_list_batch().
buf_read_ahead_random(), buf_read_ahead_linear(): Rewrite.
Do not acquire buf_pool.mutex, and iterate directly with page_id_t.
ut_2_power_up(): Remove. my_round_up_to_next_power() is inlined
and avoids any loops.
fil_page_get_prev(), fil_page_get_next(), fil_addr_is_null(): Remove.
buf_flush_page(): Add a fil_space_t* parameter. Minimize the
buf_pool.mutex hold time. buf_pool.n_flush[] is no longer updated
atomically with the io_fix, and we will protect most buf_block_t
fields with buf_block_t::lock. The function
buf_flush_write_block_low() is removed and merged here.
buf_page_init_for_read(): Use static linkage. Initialize the newly
allocated block and acquire the exclusive buf_block_t::lock while not
holding any mutex.
IORequest::IORequest(): Remove the body. We only need to invoke
set_punch_hole() in buf_flush_page() and nowhere else.
buf_page_t::flush_type: Remove. Replaced by IORequest::flush_type.
This field is only used during a fil_io() call.
That function already takes IORequest as a parameter, so we had
better introduce for the rarely changing field.
buf_block_t::init(): Replaces buf_page_init().
buf_page_t::init(): Replaces buf_page_init_low().
buf_block_t::initialise(): Initialise many fields, but
keep the buf_page_t::state(). Both buf_pool_t::validate() and
buf_page_optimistic_get() requires that buf_page_t::in_file()
be protected atomically with buf_page_t::in_page_hash
and buf_page_t::in_LRU_list.
buf_page_optimistic_get(): Now that buf_block_t::mutex
no longer exists, we must check buf_page_t::io_fix()
after acquiring the buf_pool.page_hash lock, to detect
whether buf_page_init_for_read() has been initiated.
We will also check the io_fix() before acquiring hash_lock
in order to avoid unnecessary computation.
The field buf_block_t::modify_clock (protected by buf_block_t::lock)
allows buf_page_optimistic_get() to validate the block.
buf_page_t::real_size: Remove. It was only used while flushing
pages of page_compressed tables.
buf_page_encrypt(): Add an output parameter that allows us ot eliminate
buf_page_t::real_size. Replace a condition with debug assertion.
buf_page_should_punch_hole(): Remove.
buf_dblwr_t::add_to_batch(): Replaces buf_dblwr_add_to_batch().
Add the parameter size (to replace buf_page_t::real_size).
buf_dblwr_t::write_single_page(): Replaces buf_dblwr_write_single_page().
Add the parameter size (to replace buf_page_t::real_size).
fil_system_t::detach(): Replaces fil_space_detach().
Ensure that fil_validate() will not be violated even if
fil_system.mutex is released and reacquired.
fil_node_t::complete_io(): Renamed from fil_node_complete_io().
fil_node_t::close_to_free(): Replaces fil_node_close_to_free().
Avoid invoking fil_node_t::close() because fil_system.n_open
has already been decremented in fil_space_t::detach().
BUF_BLOCK_READY_FOR_USE: Remove. Directly use BUF_BLOCK_MEMORY.
BUF_BLOCK_ZIP_DIRTY: Remove. Directly use BUF_BLOCK_ZIP_PAGE,
and distinguish dirty pages by buf_page_t::oldest_modification().
BUF_BLOCK_POOL_WATCH: Remove. Use BUF_BLOCK_NOT_USED instead.
This state was only being used for buf_page_t that are in
buf_pool.watch.
buf_pool_t::watch[]: Remove pointer indirection.
buf_page_t::in_flush_list: Remove. It was set if and only if
buf_page_t::oldest_modification() is nonzero.
buf_page_decrypt_after_read(), buf_corrupt_page_release(),
buf_page_check_corrupt(): Change the const fil_space_t* parameter
to const fil_node_t& so that we can report the correct file name.
buf_page_monitor(): Declare as an ATTRIBUTE_COLD global function.
buf_page_io_complete(): Split to buf_page_read_complete() and
buf_page_write_complete().
buf_dblwr_t::in_use: Remove.
buf_dblwr_t::buf_block_array: Add IORequest::flush_t.
buf_dblwr_sync_datafiles(): Remove. It was a useless wrapper of
os_aio_wait_until_no_pending_writes().
buf_flush_write_complete(): Declare static, not global.
Add the parameter IORequest::flush_t.
buf_flush_freed_page(): Simplify the code.
recv_sys_t::flush_lru: Renamed from flush_type and changed to bool.
fil_read(), fil_write(): Replaced with direct use of fil_io().
fil_buffering_disabled(): Remove. Check srv_file_flush_method directly.
fil_mutex_enter_and_prepare_for_io(): Return the resolved
fil_space_t* to avoid a duplicated lookup in the caller.
fil_report_invalid_page_access(): Clean up the parameters.
fil_io(): Return fil_io_t, which comprises fil_node_t and error code.
Always invoke fil_space_t::acquire_for_io() and let either the
sync=true caller or fil_aio_callback() invoke
fil_space_t::release_for_io().
fil_aio_callback(): Rewrite to replace buf_page_io_complete().
fil_check_pending_operations(): Remove a parameter, and remove some
redundant lookups.
fil_node_close_to_free(): Wait for n_pending==0. Because we no longer
do an extra lookup of the tablespace between fil_io() and the
completion of the operation, we must give fil_node_t::complete_io() a
chance to decrement the counter.
fil_close_tablespace(): Remove unused parameter trx, and document
that this is only invoked during the error handling of IMPORT TABLESPACE.
row_import_discard_changes(): Merged with the only caller,
row_import_cleanup(). Do not lock up the data dictionary while
invoking fil_close_tablespace().
logs_empty_and_mark_files_at_shutdown(): Do not invoke
fil_close_all_files(), to avoid a !needs_flush assertion failure
on fil_node_t::close().
innodb_shutdown(): Invoke os_aio_free() before fil_close_all_files().
fil_close_all_files(): Invoke fil_flush_file_spaces()
to ensure proper durability.
thread_pool::unbind(): Fix a crash that would occur on Windows
after srv_thread_pool->disable_aio() and os_file_close().
This fix was submitted by Vladislav Vaintroub.
Thanks to Matthias Leich and Axel Schwenke for extensive testing,
Vladislav Vaintroub for helpful comments, and Eugene Kosov for a review.
2020-06-05 12:35:46 +03:00
|
|
|
bitmap_page, block->page.id(), block->physical_size(),
|
2022-11-08 17:01:28 +02:00
|
|
|
false, mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|