Commit dcb2968f authored by Vlad Lesin's avatar Vlad Lesin

MDEV-27557 InnoDB unnecessarily commits mtr during secondary index search to...

MDEV-27557 InnoDB unnecessarily commits mtr during secondary index search to preserve clustered index latching order

New function to release latches till savepoint was added in mtr_t. As
there is no longer need to limit MDEV-20605 fix usage for locking reads
only, the limitation is removed.
parent 157a838b
...@@ -97,6 +97,15 @@ struct mtr_t { ...@@ -97,6 +97,15 @@ struct mtr_t {
/** Commit the mini-transaction. */ /** Commit the mini-transaction. */
void commit(); void commit();
/** Release latches till savepoint. To simplify the code only
MTR_MEMO_S_LOCK and MTR_MEMO_PAGE_S_FIX slot types are allowed to be
released, otherwise it would be neccesary to add one more argument in the
function to point out what slot types are allowed for rollback, and this
would be overengineering as currently the function is used only in one place
in the code.
@param savepoint savepoint, can be obtained with get_savepoint */
void rollback_to_savepoint(ulint savepoint);
/** Commit a mini-transaction that is shrinking a tablespace. /** Commit a mini-transaction that is shrinking a tablespace.
@param space tablespace that is being shrunk */ @param space tablespace that is being shrunk */
ATTRIBUTE_COLD void commit_shrink(fil_space_t &space); ATTRIBUTE_COLD void commit_shrink(fil_space_t &space);
......
...@@ -300,6 +300,50 @@ struct ReleaseAll { ...@@ -300,6 +300,50 @@ struct ReleaseAll {
} }
}; };
/** Stops iteration is savepoint is reached */
template <typename Functor> struct TillSavepoint
{
/** Constructor
@param[in] functor functor which is called if savepoint is not reached
@param[in] savepoint savepoint value to rollback
@param[in] used current position in slots container */
TillSavepoint(const Functor &functor, ulint savepoint, ulint used)
: functor(functor),
m_slots_count((used - savepoint) / sizeof(mtr_memo_slot_t))
{
ut_ad(savepoint);
ut_ad(used >= savepoint);
}
/** @return true if savepoint is not reached, false otherwise */
bool operator()(mtr_memo_slot_t *slot)
{
#ifdef UNIV_DEBUG
/** This check is added because the code is invoked only from
row_search_mvcc() to release latches acquired during clustered index search
for secondary index record. To make it more universal we could add one more
member in this functor for debug build to pass only certain slot types,
but this is currently not necessary. */
switch (slot->type)
{
case MTR_MEMO_S_LOCK:
case MTR_MEMO_PAGE_S_FIX:
break;
default:
ut_a(false);
}
#endif
return m_slots_count-- && functor(slot);
}
private:
/** functor to invoke */
const Functor &functor;
/** slots count left till savepoint */
ulint m_slots_count;
};
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/** Check that all slots have been handled. */ /** Check that all slots have been handled. */
struct DebugCheck { struct DebugCheck {
...@@ -468,6 +512,21 @@ void mtr_t::commit() ...@@ -468,6 +512,21 @@ void mtr_t::commit()
release_resources(); release_resources();
} }
/** Release latches till savepoint. To simplify the code only
MTR_MEMO_S_LOCK and MTR_MEMO_PAGE_S_FIX slot types are allowed to be
released, otherwise it would be neccesary to add one more argument in the
function to point out what slot types are allowed for rollback, and this
would be overengineering as corrently the function is used only in one place
in the code.
@param savepoint savepoint, can be obtained with get_savepoint */
void mtr_t::rollback_to_savepoint(ulint savepoint)
{
Iterate<TillSavepoint<ReleaseLatches>> iteration(
TillSavepoint<ReleaseLatches>(ReleaseLatches(), savepoint,
get_savepoint()));
m_memo.for_each_block_in_reverse(iteration);
}
/** Shrink a tablespace. */ /** Shrink a tablespace. */
struct Shrink struct Shrink
{ {
......
...@@ -3588,14 +3588,12 @@ record with the same ordering prefix in in the B-tree index ...@@ -3588,14 +3588,12 @@ record with the same ordering prefix in in the B-tree index
@param[in] latch_mode latch mode wished in restoration @param[in] latch_mode latch mode wished in restoration
@param[in] pcur cursor whose position has been stored @param[in] pcur cursor whose position has been stored
@param[in] moves_up true if the cursor moves up in the index @param[in] moves_up true if the cursor moves up in the index
@param[in] mtr mtr; CAUTION: may commit mtr temporarily! @param[in,out] mtr mtr; CAUTION: may commit mtr temporarily!
@param[in] select_lock_type select lock type
@return true if we may need to process the record the cursor is now @return true if we may need to process the record the cursor is now
positioned on (i.e. we should not go to the next record yet) */ positioned on (i.e. we should not go to the next record yet) */
static bool sel_restore_position_for_mysql(bool *same_user_rec, static bool sel_restore_position_for_mysql(bool *same_user_rec,
ulint latch_mode, btr_pcur_t *pcur, ulint latch_mode, btr_pcur_t *pcur,
bool moves_up, mtr_t *mtr, bool moves_up, mtr_t *mtr)
lock_mode select_lock_type)
{ {
auto status = btr_pcur_restore_position(latch_mode, pcur, mtr); auto status = btr_pcur_restore_position(latch_mode, pcur, mtr);
...@@ -3618,8 +3616,7 @@ static bool sel_restore_position_for_mysql(bool *same_user_rec, ...@@ -3618,8 +3616,7 @@ static bool sel_restore_position_for_mysql(bool *same_user_rec,
switch (pcur->rel_pos) { switch (pcur->rel_pos) {
case BTR_PCUR_ON: case BTR_PCUR_ON:
if (!*same_user_rec && moves_up) { if (!*same_user_rec && moves_up) {
if (status == btr_pcur_t::SAME_UNIQ if (status == btr_pcur_t::SAME_UNIQ)
&& select_lock_type != LOCK_NONE)
return true; return true;
next: next:
if (btr_pcur_move_to_next(pcur, mtr) if (btr_pcur_move_to_next(pcur, mtr)
...@@ -4303,7 +4300,7 @@ row_search_mvcc( ...@@ -4303,7 +4300,7 @@ row_search_mvcc(
const rec_t* clust_rec; const rec_t* clust_rec;
Row_sel_get_clust_rec_for_mysql row_sel_get_clust_rec_for_mysql; Row_sel_get_clust_rec_for_mysql row_sel_get_clust_rec_for_mysql;
ibool unique_search = FALSE; ibool unique_search = FALSE;
ibool mtr_has_extra_clust_latch = FALSE; ulint mtr_extra_clust_savepoint = 0;
bool moves_up = false; bool moves_up = false;
/* if the returned record was locked and we did a semi-consistent /* if the returned record was locked and we did a semi-consistent
read (fetch the newest committed version), then this is set to read (fetch the newest committed version), then this is set to
...@@ -4673,7 +4670,7 @@ row_search_mvcc( ...@@ -4673,7 +4670,7 @@ row_search_mvcc(
bool need_to_process = sel_restore_position_for_mysql( bool need_to_process = sel_restore_position_for_mysql(
&same_user_rec, BTR_SEARCH_LEAF, &same_user_rec, BTR_SEARCH_LEAF,
pcur, moves_up, &mtr, prebuilt->select_lock_type); pcur, moves_up, &mtr);
if (UNIV_UNLIKELY(need_to_process)) { if (UNIV_UNLIKELY(need_to_process)) {
if (UNIV_UNLIKELY(prebuilt->row_read_type if (UNIV_UNLIKELY(prebuilt->row_read_type
...@@ -5355,7 +5352,7 @@ row_search_mvcc( ...@@ -5355,7 +5352,7 @@ row_search_mvcc(
/* It was a non-clustered index and we must fetch also the /* It was a non-clustered index and we must fetch also the
clustered index record */ clustered index record */
mtr_has_extra_clust_latch = TRUE; mtr_extra_clust_savepoint = mtr.get_savepoint();
ut_ad(!vrow); ut_ad(!vrow);
/* The following call returns 'offsets' associated with /* The following call returns 'offsets' associated with
...@@ -5643,27 +5640,15 @@ row_search_mvcc( ...@@ -5643,27 +5640,15 @@ row_search_mvcc(
/* No need to do store restore for R-tree */ /* No need to do store restore for R-tree */
mtr.commit(); mtr.commit();
mtr.start(); mtr.start();
mtr_has_extra_clust_latch = FALSE; mtr_extra_clust_savepoint = 0;
} else if (mtr_has_extra_clust_latch) { } else if (mtr_extra_clust_savepoint) {
/* If we have extra cluster latch, we must commit /* We must release any clustered index latches
mtr if we are moving to the next non-clustered if we are moving to the next non-clustered
index record, because we could break the latching index record, because we could break the latching
order if we would access a different clustered order if we would access a different clustered
index page right away without releasing the previous. */ index page right away without releasing the previous. */
mtr.rollback_to_savepoint(mtr_extra_clust_savepoint);
btr_pcur_store_position(pcur, &mtr); mtr_extra_clust_savepoint = 0;
mtr.commit();
mtr_has_extra_clust_latch = FALSE;
mtr.start();
if (sel_restore_position_for_mysql(&same_user_rec,
BTR_SEARCH_LEAF,
pcur, moves_up, &mtr,
prebuilt->select_lock_type)
) {
goto rec_loop;
}
} }
if (moves_up) { if (moves_up) {
...@@ -5723,7 +5708,7 @@ row_search_mvcc( ...@@ -5723,7 +5708,7 @@ row_search_mvcc(
lock_table_wait: lock_table_wait:
mtr.commit(); mtr.commit();
mtr_has_extra_clust_latch = FALSE; mtr_extra_clust_savepoint = 0;
trx->error_state = err; trx->error_state = err;
...@@ -5752,7 +5737,7 @@ row_search_mvcc( ...@@ -5752,7 +5737,7 @@ row_search_mvcc(
if (!dict_index_is_spatial(index)) { if (!dict_index_is_spatial(index)) {
sel_restore_position_for_mysql( sel_restore_position_for_mysql(
&same_user_rec, BTR_SEARCH_LEAF, pcur, &same_user_rec, BTR_SEARCH_LEAF, pcur,
moves_up, &mtr, prebuilt->select_lock_type); moves_up, &mtr);
} }
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment