Skip to content

Commit

Permalink
MDEV-8923: port innodb_buffer_pool_dump_pct from MySQL
Browse files Browse the repository at this point in the history
Backport pull request #125 from grooverdan/MDEV-8923_innodb_buffer_pool_dump_pct to 10.0

WL#6504 InnoDB buffer pool dump/load enchantments

This patch consists of two parts:

    1. Dump only the hottest N% of the buffer pool(s)
    2. Prevent hogging the server duing BP load

From MySQL - commit b409342c43ce2edb68807100a77001367c7e6b8e

Add testcases for innodb_buffer_pool_dump_pct_basic.

Part of the code authored by Daniel Black
  • Loading branch information
Jan Lindström committed Dec 15, 2015
1 parent af3c670 commit b63bf73
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 6 deletions.
@@ -0,0 +1,20 @@
SET @orig = @@global.innodb_buffer_pool_dump_pct;
SELECT @orig;
@orig
100
SET GLOBAL innodb_buffer_pool_dump_pct=3, GLOBAL innodb_buffer_pool_dump_now = ON;
SET GLOBAL innodb_buffer_pool_dump_pct=0;
SELECT @@global.innodb_buffer_pool_dump_pct;
@@global.innodb_buffer_pool_dump_pct
1
SHOW WARNINGS;
Level Code Message
Warning 1292 Truncated incorrect innodb_buffer_pool_dump_pct value: '0'
SET GLOBAL innodb_buffer_pool_dump_pct=101;
SELECT @@global.innodb_buffer_pool_dump_pct;
@@global.innodb_buffer_pool_dump_pct
100
SHOW WARNINGS;
Level Code Message
Warning 1292 Truncated incorrect innodb_buffer_pool_dump_pct value: '101'
SET GLOBAL innodb_buffer_pool_dump_pct=@orig;
34 changes: 34 additions & 0 deletions mysql-test/suite/sys_vars/t/innodb_buffer_pool_dump_pct_basic.test
@@ -0,0 +1,34 @@
#
# Basic test for innodb_buffer_pool_dump_pct
#

-- source include/have_innodb.inc

# Check the default value
SET @orig = @@global.innodb_buffer_pool_dump_pct;
SELECT @orig;

# Do the dump
SET GLOBAL innodb_buffer_pool_dump_pct=3, GLOBAL innodb_buffer_pool_dump_now = ON;

# Wait for the dump to complete
let $wait_condition =
SELECT SUBSTR(variable_value, 1, 33) = 'Buffer pool(s) dump completed at '
FROM information_schema.global_status
WHERE LOWER(variable_name) = 'innodb_buffer_pool_dump_status';
-- source include/wait_condition.inc

# Confirm that the dump file has been created
-- let $file = `SELECT CONCAT(@@datadir, @@global.innodb_buffer_pool_filename)`
-- file_exists $file

--disable_warnings
SET GLOBAL innodb_buffer_pool_dump_pct=0;
SELECT @@global.innodb_buffer_pool_dump_pct;
SHOW WARNINGS;
SET GLOBAL innodb_buffer_pool_dump_pct=101;
SELECT @@global.innodb_buffer_pool_dump_pct;
SHOW WARNINGS;
--enable_warnings

SET GLOBAL innodb_buffer_pool_dump_pct=@orig;
88 changes: 85 additions & 3 deletions storage/innobase/buf/buf0dump.cc
Expand Up @@ -230,6 +230,16 @@ buf_dump(
continue;
}

if (srv_buf_pool_dump_pct != 100) {
ut_ad(srv_buf_pool_dump_pct < 100);

n_pages = n_pages * srv_buf_pool_dump_pct / 100;

if (n_pages == 0) {
n_pages = 1;
}
}

dump = static_cast<buf_dump_t*>(
ut_malloc(n_pages * sizeof(*dump))) ;

Expand All @@ -244,9 +254,9 @@ buf_dump(
return;
}

for (bpage = UT_LIST_GET_LAST(buf_pool->LRU), j = 0;
bpage != NULL;
bpage = UT_LIST_GET_PREV(LRU, bpage), j++) {
for (bpage = UT_LIST_GET_FIRST(buf_pool->LRU), j = 0;
bpage != NULL && j < n_pages;
bpage = UT_LIST_GET_NEXT(LRU, bpage), j++) {

ut_a(buf_page_in_file(bpage));

Expand Down Expand Up @@ -360,6 +370,72 @@ buf_dump_sort(
buf_dump_cmp);
}

/*****************************************************************//**
Artificially delay the buffer pool loading if necessary. The idea of
this function is to prevent hogging the server with IO and slowing down
too much normal client queries. */
UNIV_INLINE
void
buf_load_throttle_if_needed(
/*========================*/
ulint* last_check_time, /*!< in/out: miliseconds since epoch
of the last time we did check if
throttling is needed, we do the check
every srv_io_capacity IO ops. */
ulint* last_activity_count,
ulint n_io) /*!< in: number of IO ops done since
buffer pool load has started */
{
if (n_io % srv_io_capacity < srv_io_capacity - 1) {
return;
}

if (*last_check_time == 0 || *last_activity_count == 0) {
*last_check_time = ut_time_ms();
*last_activity_count = srv_get_activity_count();
return;
}

/* srv_io_capacity IO operations have been performed by buffer pool
load since the last time we were here. */

/* If no other activity, then keep going without any delay. */
if (srv_get_activity_count() == *last_activity_count) {
return;
}

/* There has been other activity, throttle. */

ulint now = ut_time_ms();
ulint elapsed_time = now - *last_check_time;

/* Notice that elapsed_time is not the time for the last
srv_io_capacity IO operations performed by BP load. It is the
time elapsed since the last time we detected that there has been
other activity. This has a small and acceptable deficiency, e.g.:
1. BP load runs and there is no other activity.
2. Other activity occurs, we run N IO operations after that and
enter here (where 0 <= N < srv_io_capacity).
3. last_check_time is very old and we do not sleep at this time, but
only update last_check_time and last_activity_count.
4. We run srv_io_capacity more IO operations and call this function
again.
5. There has been more other activity and thus we enter here.
6. Now last_check_time is recent and we sleep if necessary to prevent
more than srv_io_capacity IO operations per second.
The deficiency is that we could have slept at 3., but for this we
would have to update last_check_time before the
"cur_activity_count == *last_activity_count" check and calling
ut_time_ms() that often may turn out to be too expensive. */

if (elapsed_time < 1000 /* 1 sec (1000 mili secs) */) {
os_thread_sleep((1000 - elapsed_time) * 1000 /* micro secs */);
}

*last_check_time = ut_time_ms();
*last_activity_count = srv_get_activity_count();
}

/*****************************************************************//**
Perform a buffer pool load from the file specified by
innodb_buffer_pool_filename. If any errors occur then the value of
Expand Down Expand Up @@ -521,6 +597,9 @@ buf_load()

ut_free(dump_tmp);

ulint last_check_time = 0;
ulint last_activity_cnt = 0;

for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {

buf_read_page_async(BUF_DUMP_SPACE(dump[i]),
Expand All @@ -544,6 +623,9 @@ buf_load()
"Buffer pool(s) load aborted on request");
return;
}

buf_load_throttle_if_needed(
&last_check_time, &last_activity_cnt, i);
}

ut_free(dump);
Expand Down
6 changes: 6 additions & 0 deletions storage/innobase/handler/ha_innodb.cc
Expand Up @@ -16487,6 +16487,11 @@ static MYSQL_SYSVAR_BOOL(buffer_pool_dump_at_shutdown, srv_buffer_pool_dump_at_s
"Dump the buffer pool into a file named @@innodb_buffer_pool_filename",
NULL, NULL, FALSE);

static MYSQL_SYSVAR_ULONG(buffer_pool_dump_pct, srv_buf_pool_dump_pct,
PLUGIN_VAR_RQCMDARG,
"Dump only the hottest N% of each buffer pool, defaults to 100",
NULL, NULL, 100, 1, 100, 0);

#ifdef UNIV_DEBUG
static MYSQL_SYSVAR_STR(buffer_pool_evict, srv_buffer_pool_evict,
PLUGIN_VAR_RQCMDARG,
Expand Down Expand Up @@ -16960,6 +16965,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(buffer_pool_filename),
MYSQL_SYSVAR(buffer_pool_dump_now),
MYSQL_SYSVAR(buffer_pool_dump_at_shutdown),
MYSQL_SYSVAR(buffer_pool_dump_pct),
#ifdef UNIV_DEBUG
MYSQL_SYSVAR(buffer_pool_evict),
#endif /* UNIV_DEBUG */
Expand Down
2 changes: 2 additions & 0 deletions storage/innobase/include/srv0srv.h
Expand Up @@ -306,6 +306,8 @@ extern ulong srv_flush_neighbors; /*!< whether or not to flush
neighbors of a block */
extern ulint srv_buf_pool_old_size; /*!< previously requested size */
extern ulint srv_buf_pool_curr_size; /*!< current size in bytes */
extern ulong srv_buf_pool_dump_pct; /*!< dump that may % of each buffer
pool during BP dump */
extern ulint srv_mem_pool_size;
extern ulint srv_lock_table_size;

Expand Down
2 changes: 2 additions & 0 deletions storage/innobase/srv/srv0srv.cc
Expand Up @@ -232,6 +232,8 @@ UNIV_INTERN ulong srv_flush_neighbors = 1;
UNIV_INTERN ulint srv_buf_pool_old_size;
/* current size in kilobytes */
UNIV_INTERN ulint srv_buf_pool_curr_size = 0;
/* dump that may % of each buffer pool during BP dump */
UNIV_INTERN ulong srv_buf_pool_dump_pct;
/* size in bytes */
UNIV_INTERN ulint srv_mem_pool_size = ULINT_MAX;
UNIV_INTERN ulint srv_lock_table_size = ULINT_MAX;
Expand Down
88 changes: 85 additions & 3 deletions storage/xtradb/buf/buf0dump.cc
Expand Up @@ -230,6 +230,16 @@ buf_dump(
continue;
}

if (srv_buf_pool_dump_pct != 100) {
ut_ad(srv_buf_pool_dump_pct < 100);

n_pages = n_pages * srv_buf_pool_dump_pct / 100;

if (n_pages == 0) {
n_pages = 1;
}
}

dump = static_cast<buf_dump_t*>(
ut_malloc(n_pages * sizeof(*dump))) ;

Expand All @@ -244,9 +254,9 @@ buf_dump(
return;
}

for (bpage = UT_LIST_GET_LAST(buf_pool->LRU), j = 0;
bpage != NULL;
bpage = UT_LIST_GET_PREV(LRU, bpage), j++) {
for (bpage = UT_LIST_GET_FIRST(buf_pool->LRU), j = 0;
bpage != NULL && j < n_pages;
bpage = UT_LIST_GET_NEXT(LRU, bpage), j++) {

ut_a(buf_page_in_file(bpage));

Expand Down Expand Up @@ -360,6 +370,72 @@ buf_dump_sort(
buf_dump_cmp);
}

/*****************************************************************//**
Artificially delay the buffer pool loading if necessary. The idea of
this function is to prevent hogging the server with IO and slowing down
too much normal client queries. */
UNIV_INLINE
void
buf_load_throttle_if_needed(
/*========================*/
ulint* last_check_time, /*!< in/out: miliseconds since epoch
of the last time we did check if
throttling is needed, we do the check
every srv_io_capacity IO ops. */
ulint* last_activity_count,
ulint n_io) /*!< in: number of IO ops done since
buffer pool load has started */
{
if (n_io % srv_io_capacity < srv_io_capacity - 1) {
return;
}

if (*last_check_time == 0 || *last_activity_count == 0) {
*last_check_time = ut_time_ms();
*last_activity_count = srv_get_activity_count();
return;
}

/* srv_io_capacity IO operations have been performed by buffer pool
load since the last time we were here. */

/* If no other activity, then keep going without any delay. */
if (srv_get_activity_count() == *last_activity_count) {
return;
}

/* There has been other activity, throttle. */

ulint now = ut_time_ms();
ulint elapsed_time = now - *last_check_time;

/* Notice that elapsed_time is not the time for the last
srv_io_capacity IO operations performed by BP load. It is the
time elapsed since the last time we detected that there has been
other activity. This has a small and acceptable deficiency, e.g.:
1. BP load runs and there is no other activity.
2. Other activity occurs, we run N IO operations after that and
enter here (where 0 <= N < srv_io_capacity).
3. last_check_time is very old and we do not sleep at this time, but
only update last_check_time and last_activity_count.
4. We run srv_io_capacity more IO operations and call this function
again.
5. There has been more other activity and thus we enter here.
6. Now last_check_time is recent and we sleep if necessary to prevent
more than srv_io_capacity IO operations per second.
The deficiency is that we could have slept at 3., but for this we
would have to update last_check_time before the
"cur_activity_count == *last_activity_count" check and calling
ut_time_ms() that often may turn out to be too expensive. */

if (elapsed_time < 1000 /* 1 sec (1000 mili secs) */) {
os_thread_sleep((1000 - elapsed_time) * 1000 /* micro secs */);
}

*last_check_time = ut_time_ms();
*last_activity_count = srv_get_activity_count();
}

/*****************************************************************//**
Perform a buffer pool load from the file specified by
innodb_buffer_pool_filename. If any errors occur then the value of
Expand Down Expand Up @@ -521,6 +597,9 @@ buf_load()

ut_free(dump_tmp);

ulint last_check_time = 0;
ulint last_activity_cnt = 0;

for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {

buf_read_page_async(BUF_DUMP_SPACE(dump[i]),
Expand All @@ -544,6 +623,9 @@ buf_load()
"Buffer pool(s) load aborted on request");
return;
}

buf_load_throttle_if_needed(
&last_check_time, &last_activity_cnt, i);
}

ut_free(dump);
Expand Down
6 changes: 6 additions & 0 deletions storage/xtradb/handler/ha_innodb.cc
Expand Up @@ -17840,6 +17840,11 @@ static MYSQL_SYSVAR_BOOL(buffer_pool_dump_at_shutdown, srv_buffer_pool_dump_at_s
"Dump the buffer pool into a file named @@innodb_buffer_pool_filename",
NULL, NULL, FALSE);

static MYSQL_SYSVAR_ULONG(buffer_pool_dump_pct, srv_buf_pool_dump_pct,
PLUGIN_VAR_RQCMDARG,
"Dump only the hottest N% of each buffer pool, defaults to 100",
NULL, NULL, 100, 1, 100, 0);

#ifdef UNIV_DEBUG
static MYSQL_SYSVAR_STR(buffer_pool_evict, srv_buffer_pool_evict,
PLUGIN_VAR_RQCMDARG,
Expand Down Expand Up @@ -18382,6 +18387,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(buffer_pool_filename),
MYSQL_SYSVAR(buffer_pool_dump_now),
MYSQL_SYSVAR(buffer_pool_dump_at_shutdown),
MYSQL_SYSVAR(buffer_pool_dump_pct),
#ifdef UNIV_DEBUG
MYSQL_SYSVAR(buffer_pool_evict),
#endif /* UNIV_DEBUG */
Expand Down
2 changes: 2 additions & 0 deletions storage/xtradb/include/srv0srv.h
Expand Up @@ -335,6 +335,8 @@ extern ulong srv_flush_neighbors; /*!< whether or not to flush
neighbors of a block */
extern ulint srv_buf_pool_old_size; /*!< previously requested size */
extern ulint srv_buf_pool_curr_size; /*!< current size in bytes */
extern ulong srv_buf_pool_dump_pct; /*!< dump that may % of each buffer
pool during BP dump */
extern ulint srv_mem_pool_size;
extern ulint srv_lock_table_size;

Expand Down
2 changes: 2 additions & 0 deletions storage/xtradb/srv/srv0srv.cc
Expand Up @@ -260,6 +260,8 @@ UNIV_INTERN ulong srv_flush_neighbors = 1;
UNIV_INTERN ulint srv_buf_pool_old_size;
/* current size in kilobytes */
UNIV_INTERN ulint srv_buf_pool_curr_size = 0;
/* dump that may % of each buffer pool during BP dump */
UNIV_INTERN ulong srv_buf_pool_dump_pct;
/* size in bytes */
UNIV_INTERN ulint srv_mem_pool_size = ULINT_MAX;
UNIV_INTERN ulint srv_lock_table_size = ULINT_MAX;
Expand Down

0 comments on commit b63bf73

Please sign in to comment.