/* How should the old versions in the history list be managed? ---------------------------------------------------------- Ifeachtransactionisgivenawholepageforitsupdateundolog,file spaceconsumptioncanbe10timeshigherthannecessary.Therefore, partlyfilledupdateundologpagesshouldbereusable.Butthenthere isnowayindividualpagescanbeorderedsothattheorderingagrees withtheserializationnumbersofthetransactionsonthepages.Thus, thehistorylistmustbeformedofundologs,nottheirheaderpagesas itwasintheoldimplementation. However,onasingleheaderpagethetransactionsareplacedin theorderoftheirserializationnumbers.Asoldversionsarepurged,we mayfreethepagewhenthelasttransactiononthepagehasbeenpurged. Aproblemisthatthepurgehastogothroughthetransactions intheserializationorder.Thismeansthatwehavetolookthroughall rollbacksegmentsfortheonethathasthesmallesttransactionnumber initshistorylist. Whenshouldwedoapurge?Apurgeisnecessarywhenspaceis runningoutinanyoftherollbacksegments.Thenwemayhavetopurge alsooldversionwhichmightbeneededbysomeconsistentread.Howdo wetriggerthestartofapurge?Whenatransactionwritestoanundolog, itmaynoticethatthespaceisrunningout.Whenareadviewisclosed, itmaymakesomehistorysuperfluous.Theservercanhaveanutilitywhich periodicallychecksifitcanpurgesomehistory. Inaparallelizedpurgewehavetheproblemthataquerythread canremoveadeletemarkedclusteredindexrecordbeforeanotherquery threadhasprocessedanearlierversionoftherecord,whichcannotthen bedonebecausetherowcannotbeconstructedfromtheclusteredindex record.Toavoidthisproblem,wewillstoreintheupdateanddeletemark undorecordalsothecolumnsnecessarytoconstructthesecondaryindex entrieswhicharemodified. Wecanlatchthestackofversionsofasingleclusteredindexrecord bytakingalatchontheclusteredindexpage.Aslongasthelatchisheld, nonewversionscanbeaddedandnoversionsremovedbyundo.But,apurge
can still remove old versions from the bottom of the stack. */
/* How to protect rollback segments, undo logs, and history lists with ------------------------------------------------------------------- latches? ------- Whenatransactiondoesitsfirstinsertormodifyintheclusteredindex,an undologisassignedforit.Thenwemusthaveanx-latchtotherollback segmentheader. Whenthetransactionperformsmodificationsorrollsback,its undologisprotectedbyundopagelatches. Onlythethreadthatisassociatedwiththetransactionmayholdmultiple undopagelatchesatatime.Undopagesarealwaysprivatetoasingle transaction.OtherthreadsthatareperformingMVCCreads orcheckingforimplicitlockswilllockatmostoneundopageatatime intrx_undo_get_undo_rec_low(). Whenthetransactioncommits,itspersistentundologisadded tothehistorylist.Ifitisnotsuitableforreuse,itsslotisreset. Inbothcases,anx-latchmustbeacquiredontherollbacksegmentheaderpage. Thepurgeoperationstepsthroughthehistorylistwithoutmodifying ituntilatruncateoperationoccurs,whichcanremoveundologsfromtheend ofthelistandreleaseundologsegments.Insteppingthroughthelist, s-latchesontheundologpagesareenough,butinatruncate,x-latchesmust
be obtained on the rollback segment and individual pages. */
/********************************************************************//**
Creates and initializes an undo log memory object.
@return own: the undo log memory object */ static
trx_undo_t*
trx_undo_mem_create( /*================*/
trx_rseg_t* rseg, /*!< in: rollback segment memory object */
ulint id, /*!< in: slot index within rseg */
trx_id_t trx_id, /*!< in: id of the trx for which the undo log
is created */ const XID* xid, /*!< in: X/Open XA transaction identification*/
uint32_t page_no,/*!< in: undo log header page number */
uint16_t offset);/*!< in: undo log header byte offset on page */
/** Determine the start offset of undo log records of an undo log page. @param[in]blockundologpage @param[in]page_noundologheaderpagenumber @param[in]offsetundologheaderoffset
@return start offset */ static
uint16_t trx_undo_page_get_start(const buf_block_t *block, uint32_t page_no,
uint16_t offset)
{ return page_no == block->page.id().page_no()
? mach_read_from_2(offset + TRX_UNDO_LOG_START + block->page.frame)
: TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE;
}
/** Get the first undo log record on a page. @param[in]blockundologpage @param[in]page_noundologheaderpagenumber @param[in]offsetundologheaderpageoffset @returnpointertofirstrecord
@retval nullptr if none exists */
trx_undo_rec_t*
trx_undo_page_get_first_rec(const buf_block_t *block, uint32_t page_no,
uint16_t offset)
{
uint16_t start= trx_undo_page_get_start(block, page_no, offset);
uint16_t end= trx_undo_page_get_end(block, page_no, offset);
ut_ad(start <= end); return start >= end ? nullptr : block->page.frame + start;
}
/** Get the last undo log record on a page. @param[in]pageundologpage @param[in]page_noundologheaderpagenumber @param[in]offsetundologheaderpageoffset @returnpointertolastrecord
@retval NULL if none exists */ static
trx_undo_rec_t*
trx_undo_page_get_last_rec(const buf_block_t *block, uint32_t page_no,
uint16_t offset)
{
uint16_t start= trx_undo_page_get_start(block, page_no, offset);
uint16_t end= trx_undo_page_get_end(block, page_no, offset);
ut_ad(start <= end); return start >= end
? nullptr
: block->page.frame + mach_read_from_2(block->page.frame + end - 2);
}
/** Get the previous record in an undo log from the previous page. @param[in,out]blockundologpage @param[in]recundorecordoffsetinthepage @param[in]page_noundologheaderpagenumber @param[in]offsetundologheaderoffsetonpage @param[in]sharedlatchingmode:true=RW_S_LATCH,false=RW_X_LATCH @param[in,out]mtrmini-transaction
@return undo log record, the page latched, NULL if none */ static trx_undo_rec_t*
trx_undo_get_prev_rec_from_prev_page(buf_block_t *&block, uint16_t rec,
uint32_t page_no, uint16_t offset, bool shared, mtr_t *mtr)
{
uint32_t prev_page_no= mach_read_from_4(TRX_UNDO_PAGE_HDR +
TRX_UNDO_PAGE_NODE +
FLST_PREV + FIL_ADDR_PAGE +
block->page.frame);
/** Get the next record in an undo log from the next page. @param[in,out]blockundologpage @param[in]page_noundologheaderpagenumber @param[in]offsetundologheaderoffsetonpage @param[in]modelatchingmode:RW_S_LATCHorRW_X_LATCH @param[in,out]mtrmini-transaction
@return undo log record, the page latched, NULL if none */ static trx_undo_rec_t*
trx_undo_get_next_rec_from_next_page(const buf_block_t *&block,
uint32_t page_no, uint16_t offset,
rw_lock_type_t mode, mtr_t *mtr)
{ if (page_no == block->page.id().page_no() &&
mach_read_from_2(block->page.frame + offset + TRX_UNDO_NEXT_LOG)) return nullptr;
/** Apply any changes to tables for which online DDL is in progress. */
ATTRIBUTE_COLD void trx_t::apply_log()
{ const trx_undo_t *undo= rsegs.m_redo.undo; if (!undo || !undo_no) return; const page_id_t page_id{rsegs.m_redo.rseg->space->id, undo->hdr_page_no};
page_id_t next_page_id(page_id);
buf_block_t *block=
buf_pool.page_fix(page_id, nullptr, this, buf_pool_t::FIX_WAIT_READ); if (UNIV_UNLIKELY(!block)) return;
UndorecApplier log_applier(page_id, *this);
for (;;)
{
trx_undo_rec_t *rec= trx_undo_page_get_first_rec(block, page_id.page_no(),
undo->hdr_offset); while (rec)
{ const uint16_t offset= uint16_t(rec - block->page.frame); /* Since we are the only thread who could write to this undo page,
it is safe to dereference rec while only holding a buffer-fix. */
log_applier.apply_undo_rec(rec, offset);
rec= trx_undo_page_get_next_rec(block, offset,
page_id.page_no(), undo->hdr_offset);
}
/** Look for a free slot for an undo log segment. @paramrseg_headerrollbacksegmentheader @returnslotindex
@retval ULINT_UNDEFINED if not found */ static ulint trx_rsegf_undo_find_free(const buf_block_t *rseg_header)
{
ulint max_slots= TRX_RSEG_N_SLOTS;
for (ulint i= 0; i < max_slots; i++) if (trx_rsegf_get_nth_undo(rseg_header, i) == FIL_NULL) return i;
if (slot_no == ULINT_UNDEFINED) {
ib::warn() << "Cannot find a free slot for an undo log. Do" " you have too many active transactions running" " concurrently?";
/* When we add a page to an undo log, this is analogous to apessimisticinsertinaB-tree,andwemustreservethe
counterpart of the tree latch, which is the rseg mutex. */
/********************************************************************//**
Frees an undo log page that is not the header page.
@return last page number in remaining log */ static
uint32_t
trx_undo_free_page( /*===============*/
trx_rseg_t* rseg, /*!< in: rollback segment */ bool in_history, /*!< in: TRUE if the undo log is in the history
list */
uint32_t hdr_page_no, /*!< in: header page number */
uint32_t page_no, /*!< in: page number to free: must not be the
header page */
mtr_t* mtr, /*!< in: mtr which does not have a latch to any undologpage;thecallermusthavereserved
the rollback segment mutex */
dberr_t* err) /*!< out: error code */
{
ut_a(hdr_page_no != page_no);
for (trx_undo_rec_t *rec=
trx_undo_page_get_last_rec(undo_block,
undo.hdr_page_no, undo.hdr_offset);
rec; )
{ if (trx_undo_rec_get_undo_no(rec) < limit) goto func_exit; /* Truncate at least this record off, maybe more */
trunc_here= rec;
rec= trx_undo_page_get_prev_rec(undo_block, rec,
undo.hdr_page_no, undo.hdr_offset);
}
if (undo.last_page_no != undo.hdr_page_no)
{
err= trx_undo_free_last_page(&undo, &mtr); if (UNIV_UNLIKELY(err != DB_SUCCESS)) goto func_exit;
undo.rseg->latch.wr_unlock();
mtr.commit(); continue;
}
const trx_id_t trx_id= mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); if (trx_id >> 48) {
sql_print_error("InnoDB: corrupted TRX_ID %" PRIx64, trx_id); goto corrupted;
} /* We will increment rseg->needs_purge, like trx_undo_reuse_cached()
would do it, to avoid trouble on rollback or XA COMMIT. */
trx_id_t trx_no = trx_id + 1;
switch (state) { case TRX_UNDO_ACTIVE: case TRX_UNDO_PREPARED: if (UNIV_LIKELY(type != 1)) { /* The undo log of a previously committed transactionthatwasloggedinthispagemay havetrx_nogreaterthanthetrx_idofthe currentundolog,becausethetrx_idofthe currenttransactioncanbeassignedbeforethe previoustransactioniscommitted.
Anundopagecancontainseveralsmall transactionswhenaTRX_UNDO_CACHEDpageis beingreused.Onlythelast(uncommitted) transactionmaylackatrx_no.Bydesign,any precedingtransactionsmustbecommittedand haveatrx_no,andthelastcommitted transactioninthepagemusthavethelargest
trx_no. */ const uint16 prev_offset = mach_read_from_2(
undo_header + TRX_UNDO_PREV_LOG); if (!prev_offset) { break;
} const trx_ulogf_t* const prev_undo_header=
block->page.frame + prev_offset; const trx_id_t trx_no_prev = mach_read_from_8(
prev_undo_header + TRX_UNDO_TRX_NO); if (trx_no_prev > trx_id) {
trx_no = trx_no_prev + 1;
} break;
}
sql_print_error("InnoDB: upgrade from older version than" " MariaDB 10.3 requires clean shutdown"); goto corrupted; default:
sql_print_error("InnoDB: unsupported undo header state %u",
state); goto corrupted; case TRX_UNDO_CACHED: if (UNIV_UNLIKELY(type != 0)) { /* This undo page was not updated by MariaDB 10.3orlater.TheTRX_UNDO_TRX_NOfieldmay
contain garbage. */ break;
} goto read_trx_no; case TRX_UNDO_TO_PURGE: if (UNIV_UNLIKELY(type == 1)) { goto corrupted_type;
}
read_trx_no:
trx_no = mach_read_from_8(TRX_UNDO_TRX_NO + undo_header); if (trx_no >> 48) {
sql_print_error("InnoDB: corrupted TRX_NO %" PRIx64,
trx_no); goto corrupted;
} if (trx_no < trx_id) {
trx_no = trx_id;
}
}
/* Read X/Open XA transaction identification if it exists, or
set it to NULL. */
if (undo_header[TRX_UNDO_XID_EXISTS]) {
trx_undo_read_xid(undo_header, &xid);
} else {
xid.null();
}
if (trx_no > rseg->needs_purge) {
rseg->needs_purge = trx_no;
}
/** Set the state of the undo log segment at a XA PREPARE or XA ROLLBACK. @param[in,out]undoundolog @param[in]rollbackfalse=XAPREPARE,true=XAROLLBACK @param[in,out]mtrmini-transaction
@return undo log segment header page, x-latched */ void trx_undo_set_state_at_prepare(trx_undo_t *undo, bool rollback, mtr_t *mtr)
noexcept
{
ut_a(undo->id < TRX_RSEG_N_SLOTS);
buf_block_t* block = buf_page_get(
page_id_t(undo->rseg->space->id, undo->hdr_page_no), 0,
RW_X_LATCH, mtr); if (UNIV_UNLIKELY(!block)) { /* In case of !rollback the undo header page corruptionwouldleavethetransactionobjectinan
unexpected (active) state. */
ut_a(rollback); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.