Skip to content

Commit f5794e1

Browse files
committed
MDEV-26445 innodb_undo_log_truncate is unnecessarily slow
trx_purge_truncate_history(): Do not force a write of the undo tablespace that is being truncated. Instead, prevent page writes by acquiring an exclusive latch on all dirty pages of the tablespace. fseg_create(): Relax an assertion that could fail if a dirty undo page is being initialized during undo tablespace truncation (and trx_purge_truncate_history() already acquired an exclusive latch on it). fsp_page_create(): If we are truncating a tablespace, try to reuse a page that we may have already latched exclusively (because it was in buf_pool.flush_list). To some extent, this helps the test innodb.undo_truncate,16k to avoid running out of buffer pool. mtr_t::commit_shrink(): Mark as clean all pages that are outside the new bounds of the tablespace, and only add the newly reinitialized pages to the buf_pool.flush_list. buf_page_create(): Do not unnecessarily invoke change buffer merge on undo tablespaces. buf_page_t::clear_oldest_modification(bool temporary): Move some assertions to the caller buf_page_write_complete(). innodb.undo_truncate: Use a bigger innodb_buffer_pool_size=24M. On my system, it would otherwise hang 1 out of 1547 attempts (on the 40th repeat of innodb.undo_truncate,16k). Other page sizes were not affected.
1 parent f5fddae commit f5794e1

File tree

8 files changed

+330
-279
lines changed

8 files changed

+330
-279
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--innodb-buffer-pool-size=24M

mysql-test/suite/innodb/t/undo_truncate.test

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
--source include/have_undo_tablespaces.inc
44
--source include/have_sequence.inc
55

6+
--disable_query_log
7+
call mtr.add_suppression("InnoDB: Difficult to find free blocks in the buffer pool");
8+
--enable_query_log
9+
610
SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
711
SET @save_truncate = @@GLOBAL.innodb_undo_log_truncate;
812
SET GLOBAL innodb_undo_log_truncate = 0;
@@ -46,17 +50,5 @@ drop table t1, t2;
4650

4751
--source include/wait_all_purged.inc
4852

49-
# Truncation will normally not occur with innodb_page_size=64k,
50-
# and occasionally not with innodb_page_size=32k,
51-
# because the undo log will not grow enough.
52-
# TODO: For some reason this does not occur on 4k either!
53-
if (`select @@innodb_page_size IN (8192,16384)`)
54-
{
55-
let $wait_condition = (SELECT variable_value!=@trunc_start
56-
FROM information_schema.global_status
57-
WHERE variable_name = 'innodb_undo_truncations');
58-
source include/wait_condition.inc;
59-
}
60-
6153
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
6254
SET GLOBAL innodb_undo_log_truncate = @save_truncate;

storage/innobase/buf/buf0buf.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3796,6 +3796,7 @@ buf_page_create(fil_space_t *space, uint32_t offset,
37963796
/* Delete possible entries for the page from the insert buffer:
37973797
such can exist if the page belonged to an index which was dropped */
37983798
if (page_id < page_id_t{SRV_SPACE_ID_UPPER_BOUND, 0} &&
3799+
!srv_is_undo_tablespace(page_id.space()) &&
37993800
!recv_recovery_is_on())
38003801
ibuf_merge_or_delete_for_page(nullptr, page_id, zip_size);
38013802

storage/innobase/buf/buf0flu.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,12 @@ void buf_page_write_complete(const IORequest &request)
363363
const bool temp= fsp_is_system_temporary(bpage->id().space());
364364

365365
mysql_mutex_lock(&buf_pool.mutex);
366+
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
366367
buf_pool.stat.n_pages_written++;
367368
/* While we do not need any mutex for clearing oldest_modification
368369
here, we hope that it will be in the same cache line with io_fix,
369370
whose changes must be protected by buf_pool.mutex. */
371+
ut_ad(temp || bpage->oldest_modification() > 2);
370372
bpage->clear_oldest_modification(temp);
371373
ut_ad(bpage->io_fix() == BUF_IO_WRITE);
372374
bpage->set_io_fix(BUF_IO_NONE);

storage/innobase/fsp/fsp0fsp.cc

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,11 +1054,36 @@ static
10541054
buf_block_t*
10551055
fsp_page_create(fil_space_t *space, page_no_t offset, mtr_t *mtr)
10561056
{
1057-
buf_block_t *free_block= buf_LRU_get_free_block(false);
1058-
buf_block_t *block= buf_page_create(space, static_cast<uint32_t>(offset),
1059-
space->zip_size(), mtr, free_block);
1057+
buf_block_t *block, *free_block;
1058+
1059+
if (UNIV_UNLIKELY(space->is_being_truncated))
1060+
{
1061+
const page_id_t page_id{space->id, offset};
1062+
const ulint fold= page_id.fold();
1063+
mysql_mutex_lock(&buf_pool.mutex);
1064+
block= reinterpret_cast<buf_block_t*>
1065+
(buf_pool.page_hash_get_low(page_id, fold));
1066+
if (block && block->page.oldest_modification() <= 1)
1067+
block= nullptr;
1068+
mysql_mutex_unlock(&buf_pool.mutex);
1069+
1070+
if (block)
1071+
{
1072+
ut_ad(block->page.buf_fix_count() >= 1);
1073+
ut_ad(rw_lock_get_x_lock_count(&block->lock) == 1);
1074+
ut_ad(mtr->have_x_latch(*block));
1075+
free_block= block;
1076+
goto got_free_block;
1077+
}
1078+
}
1079+
1080+
free_block= buf_LRU_get_free_block(false);
1081+
got_free_block:
1082+
block= buf_page_create(space, static_cast<uint32_t>(offset),
1083+
space->zip_size(), mtr, free_block);
10601084
if (UNIV_UNLIKELY(block != free_block))
10611085
buf_pool.free_block(free_block);
1086+
10621087
fsp_init_file_page(space, block, mtr);
10631088
return block;
10641089
}
@@ -1728,7 +1753,10 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr,
17281753
goto funct_exit;
17291754
}
17301755

1731-
ut_ad(rw_lock_get_x_lock_count(&block->lock) == 1);
1756+
ut_d(const auto x = rw_lock_get_x_lock_count(&block->lock));
1757+
ut_ad(x > 0);
1758+
ut_ad(x == 1 || space->is_being_truncated);
1759+
ut_ad(x <= 2);
17321760
ut_ad(!fil_page_get_type(block->frame));
17331761
mtr->write<1>(*block, FIL_PAGE_TYPE + 1 + block->frame,
17341762
FIL_PAGE_TYPE_SYS);

storage/innobase/include/buf0buf.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,9 +2232,7 @@ inline void buf_page_t::clear_oldest_modification()
22322232
it from buf_pool.flush_list */
22332233
inline void buf_page_t::clear_oldest_modification(bool temporary)
22342234
{
2235-
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
22362235
ut_ad(temporary == fsp_is_system_temporary(id().space()));
2237-
ut_ad(io_fix_ == BUF_IO_WRITE);
22382236
if (temporary)
22392237
{
22402238
ut_ad(oldest_modification() == 2);

storage/innobase/mtr/mtr0mtr.cc

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -459,15 +459,15 @@ void mtr_t::commit()
459459
release_resources();
460460
}
461461

462-
#ifdef UNIV_DEBUG
463-
/** Check that all pages belong to a shrunk tablespace. */
462+
/** Shrink a tablespace. */
464463
struct Shrink
465464
{
466-
const page_id_t low, high;
467-
Shrink(const fil_space_t &space) :
468-
low({space.id, 0}), high({space.id, space.size}) {}
465+
/** the first non-existing page in the tablespace */
466+
const page_id_t high;
469467

470-
bool operator()(const mtr_memo_slot_t *slot) const
468+
Shrink(const fil_space_t &space) : high({space.id, space.size}) {}
469+
470+
bool operator()(mtr_memo_slot_t *slot) const
471471
{
472472
if (!slot->object)
473473
return true;
@@ -476,29 +476,31 @@ struct Shrink
476476
ut_ad("invalid type" == 0);
477477
return false;
478478
case MTR_MEMO_SPACE_X_LOCK:
479-
ut_ad(low.space() == static_cast<fil_space_t*>(slot->object)->id);
479+
ut_ad(high.space() == static_cast<fil_space_t*>(slot->object)->id);
480480
return true;
481481
case MTR_MEMO_PAGE_X_MODIFY:
482482
case MTR_MEMO_PAGE_SX_MODIFY:
483483
case MTR_MEMO_PAGE_X_FIX:
484484
case MTR_MEMO_PAGE_SX_FIX:
485-
const auto &bpage= static_cast<buf_block_t*>(slot->object)->page;
485+
auto &bpage= static_cast<buf_block_t*>(slot->object)->page;
486+
ut_ad(bpage.io_fix() == BUF_IO_NONE);
486487
const auto id= bpage.id();
487-
if (id == page_id_t{0, TRX_SYS_PAGE_NO})
488+
if (id < high)
488489
{
489-
ut_ad(srv_is_undo_tablespace(low.space()));
490+
ut_ad(id.space() == high.space() ||
491+
(id == page_id_t{0, TRX_SYS_PAGE_NO} &&
492+
srv_is_undo_tablespace(high.space())));
490493
break;
491494
}
492-
ut_ad(id >= low);
493-
ut_ad(id < high);
495+
ut_ad(id.space() == high.space());
494496
ut_ad(bpage.state() == BUF_BLOCK_FILE_PAGE);
495-
ut_ad(bpage.oldest_modification() <= 1);
496-
break;
497+
if (bpage.oldest_modification() > 1)
498+
bpage.clear_oldest_modification(false);
499+
slot->type= static_cast<mtr_memo_type_t>(slot->type & ~MTR_MEMO_MODIFY);
497500
}
498501
return true;
499502
}
500503
};
501-
#endif
502504

503505
/** Commit a mini-transaction that is shrinking a tablespace.
504506
@param space tablespace that is being shrunk */
@@ -542,7 +544,7 @@ void mtr_t::commit_shrink(fil_space_t &space)
542544
else
543545
ut_ad(!m_freed_space);
544546

545-
ut_d(m_memo.for_each_block_in_reverse(CIterate<Shrink>{space}));
547+
m_memo.for_each_block_in_reverse(CIterate<Shrink>{space});
546548

547549
m_memo.for_each_block_in_reverse(CIterate<const ReleaseBlocks>
548550
(ReleaseBlocks(start_lsn, m_commit_lsn,

0 commit comments

Comments
 (0)