Skip to content

Commit 5da664b

Browse files
PS-11120: Reduce contention on BUF_BLOCK_MUTEX by reading ahead the access_time
This patch reduces contention inside buf_page_optimistic_get. We acquire there the BUF_BLOCK_MUTEX twice. But the second time we acquire only to update the access time. Given that we would update the access time only if it was zero, we could avoid acquiring the mutex when we knew that the access time was already non-zero. This we could learn the first time we acquired the mutex by reading the access time field of the page. Note: the second time we acquired the mutex, we re-check if it still was zero anyway, so the patch is safe and allows to save significant number of times we needed to acquire the mutex. This is a contribution from: Anna Glasgall (with a minor fix).
1 parent 29c0faa commit 5da664b

1 file changed

Lines changed: 31 additions & 6 deletions

File tree

storage/innobase/buf/buf0buf.cc

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4589,6 +4589,10 @@ bool buf_page_optimistic_get(ulint rw_latch, buf_block_t *block,
45894589

45904590
buf_block_buf_fix_inc(block, ut::Location{file, line});
45914591

4592+
/* Grab the access time while we have the mutex to potentially
4593+
avoid the need to acquire the mutex the second time (below). */
4594+
auto access_time = buf_page_is_accessed(&block->page);
4595+
45924596
buf_page_mutex_exit(block);
45934597

45944598
ut_ad(!ibuf_inside(mtr) ||
@@ -4635,15 +4639,35 @@ bool buf_page_optimistic_get(ulint rw_latch, buf_block_t *block,
46354639
return (false);
46364640
}
46374641

4638-
buf_page_mutex_enter(block);
4642+
/* Only grab the mutex to update access time if it was zero when we
4643+
checked earlier. This check is to reduce contention on page mutex
4644+
for hot pages (access time would be set only if it was zero anyway). */
4645+
if (access_time == std::chrono::steady_clock::time_point{}) {
4646+
buf_page_mutex_enter(block);
46394647

4640-
const auto access_time = buf_page_is_accessed(&block->page);
4648+
/* Refresh the access_time. Because of race condition we might see
4649+
that it's been set by other thread since the last time we checked.
46414650
4642-
buf_page_set_accessed(&block->page);
4651+
Note: it's important to update access_time variable because we use it
4652+
later to determine if it was the first page access and we should:
4653+
- try reading ahead next consecutive pages on the disk,
4654+
- update thd->access_distinct_page() statistics (trx != nullptr).
46434655
4644-
ut_ad(!block->page.file_page_was_freed);
4656+
Without this:
4657+
- we could be calling too many times the buf_read_ahead_linear
4658+
if the set of hot pages was changing over time,
4659+
- the sum of innodb_pages_distinct across many queries
4660+
could have the same page counted twice (so no longer would be
4661+
lower bound for the total number of unique page accesses). */
4662+
access_time = buf_page_is_accessed(&block->page);
46454663

4646-
buf_page_mutex_exit(block);
4664+
/* This is no-op if access time was non-zero. */
4665+
buf_page_set_accessed(&block->page);
4666+
4667+
ut_ad(!block->page.file_page_was_freed);
4668+
4669+
buf_page_mutex_exit(block);
4670+
}
46474671

46484672
if (fetch_mode != Page_fetch::SCAN) {
46494673
buf_page_make_young_if_needed(&block->page);
@@ -4661,10 +4685,11 @@ bool buf_page_optimistic_get(ulint rw_latch, buf_block_t *block,
46614685
trx_t *trx;
46624686
if (access_time == std::chrono::steady_clock::time_point{}) {
46634687
trx = innobase_get_trx_for_slow_log();
4664-
/* In the case of a first access, try to apply linear read-ahead */
4688+
/* In the case of a first access, try to apply linear read-ahead. */
46654689
buf_read_ahead_linear(block->page.id, block->page.size, ibuf_inside(mtr),
46664690
trx);
46674691
} else {
4692+
/* It's not the first page access (don't bump access_distinct_page). */
46684693
trx = nullptr;
46694694
}
46704695

0 commit comments

Comments
 (0)