diff options
Diffstat (limited to 'storage/innobase/buf/buf0dump.cc')
-rw-r--r-- | storage/innobase/buf/buf0dump.cc | 348 |
1 files changed, 224 insertions, 124 deletions
diff --git a/storage/innobase/buf/buf0dump.cc b/storage/innobase/buf/buf0dump.cc index 0abf7118b4f..682be386f2b 100644 --- a/storage/innobase/buf/buf0dump.cc +++ b/storage/innobase/buf/buf0dump.cc @@ -23,32 +23,33 @@ Implements a buffer pool dump/load. Created April 08, 2011 Vasil Dimov *******************************************************/ -#include "univ.i" +#include "my_global.h" +#include "my_sys.h" + +#include "mysql/psi/mysql_stage.h" +#include "mysql/psi/psi.h" -#include <stdarg.h> /* va_* */ -#include <string.h> /* strerror() */ +#include "univ.i" -#include "buf0buf.h" /* buf_pool_mutex_enter(), srv_buf_pool_instances */ +#include "buf0buf.h" #include "buf0dump.h" -#include "db0err.h" -#include "dict0dict.h" /* dict_operation_lock */ -#include "os0file.h" /* OS_FILE_MAX_PATH */ -#include "os0sync.h" /* os_event* */ -#include "os0thread.h" /* os_thread_* */ -#include "srv0srv.h" /* srv_fast_shutdown, srv_buf_dump* */ -#include "srv0start.h" /* srv_shutdown_state */ -#include "sync0rw.h" /* rw_lock_s_lock() */ -#include "ut0byte.h" /* ut_ull_create() */ -#include "ut0sort.h" /* UT_SORT_FUNCTION_BODY */ +#include "dict0dict.h" +#include "os0file.h" +#include "os0thread.h" +#include "srv0srv.h" +#include "srv0start.h" +#include "sync0rw.h" +#include "ut0byte.h" + +#include <algorithm> enum status_severity { + STATUS_VERBOSE, STATUS_INFO, - STATUS_NOTICE, STATUS_ERR }; -#define SHUTTING_DOWN() (UNIV_UNLIKELY(srv_shutdown_state \ - != SRV_SHUTDOWN_NONE)) +#define SHUTTING_DOWN() (srv_shutdown_state != SRV_SHUTDOWN_NONE) /* Flags that tell the buffer pool dump/load thread which action should it take after being waked up. */ @@ -73,7 +74,6 @@ Wakes up the buffer pool dump/load thread and instructs it to start a dump. This function is called by MySQL code via buffer_pool_dump_now() and it should return immediately because the whole MySQL is frozen during its execution. */ -UNIV_INTERN void buf_dump_start() /*============*/ @@ -87,7 +87,6 @@ Wakes up the buffer pool dump/load thread and instructs it to start a load. This function is called by MySQL code via buffer_pool_load_now() and it should return immediately because the whole MySQL is frozen during its execution. */ -UNIV_INTERN void buf_load_start() /*============*/ @@ -123,7 +122,18 @@ buf_dump_status( sizeof(export_vars.innodb_buffer_pool_dump_status), fmt, ap); - ib_logf((ib_log_level_t) severity, "%s", export_vars.innodb_buffer_pool_dump_status); + switch (severity) { + case STATUS_INFO: + ib::info() << export_vars.innodb_buffer_pool_dump_status; + break; + + case STATUS_ERR: + ib::error() << export_vars.innodb_buffer_pool_dump_status; + break; + + case STATUS_VERBOSE: + break; + } va_end(ap); } @@ -154,10 +164,17 @@ buf_load_status( sizeof(export_vars.innodb_buffer_pool_load_status), fmt, ap); - if (severity == STATUS_NOTICE || severity == STATUS_ERR) { - ut_print_timestamp(stderr); - fprintf(stderr, " InnoDB: %s\n", - export_vars.innodb_buffer_pool_load_status); + switch (severity) { + case STATUS_INFO: + ib::info() << export_vars.innodb_buffer_pool_load_status; + break; + + case STATUS_ERR: + ib::error() << export_vars.innodb_buffer_pool_load_status; + break; + + case STATUS_VERBOSE: + break; } va_end(ap); @@ -182,6 +199,56 @@ get_buf_dump_dir() return(dump_dir); } +/** Generate the path to the buffer pool dump/load file. +@param[out] path generated path +@param[in] path_size size of 'path', used as in snprintf(3). */ +static +void +buf_dump_generate_path( + char* path, + size_t path_size) +{ + char buf[FN_REFLEN]; + + ut_snprintf(buf, sizeof(buf), "%s%c%s", get_buf_dump_dir(), + OS_PATH_SEPARATOR, srv_buf_dump_filename); + + os_file_type_t type; + bool exists = false; + bool ret; + + ret = os_file_status(buf, &exists, &type); + + /* For realpath() to succeed the file must exist. */ + + if (ret && exists) { + /* my_realpath() assumes the destination buffer is big enough + to hold FN_REFLEN bytes. */ + ut_a(path_size >= FN_REFLEN); + + my_realpath(path, buf, 0); + } else { + /* If it does not exist, then resolve only srv_data_home + and append srv_buf_dump_filename to it. */ + char srv_data_home_full[FN_REFLEN]; + + my_realpath(srv_data_home_full, get_buf_dump_dir(), 0); + + if (srv_data_home_full[strlen(srv_data_home_full) - 1] + == OS_PATH_SEPARATOR) { + + ut_snprintf(path, path_size, "%s%s", + srv_data_home_full, + srv_buf_dump_filename); + } else { + ut_snprintf(path, path_size, "%s%c%s", + srv_data_home_full, + OS_PATH_SEPARATOR, + srv_buf_dump_filename); + } + } +} + /*****************************************************************//** Perform a buffer pool dump into the file specified by innodb_buffer_pool_filename. If any errors occur then the value of @@ -204,14 +271,12 @@ buf_dump( ulint i; int ret; - ut_snprintf(full_filename, sizeof(full_filename), - "%s%c%s", get_buf_dump_dir(), SRV_PATH_SEPARATOR, - srv_buf_dump_filename); + buf_dump_generate_path(full_filename, sizeof(full_filename)); ut_snprintf(tmp_filename, sizeof(tmp_filename), "%s.incomplete", full_filename); - buf_dump_status(STATUS_NOTICE, "Dumping buffer pool(s) to %s", + buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) to %s", full_filename); f = fopen(tmp_filename, "w"); @@ -257,8 +322,8 @@ buf_dump( } } - dump = static_cast<buf_dump_t*>( - ut_malloc(n_pages * sizeof(*dump))) ; + dump = static_cast<buf_dump_t*>(ut_malloc_nokey( + n_pages * sizeof(*dump))); if (dump == NULL) { buf_pool_mutex_exit(buf_pool); @@ -277,8 +342,8 @@ buf_dump( ut_a(buf_page_in_file(bpage)); - dump[j] = BUF_DUMP_CREATE(buf_page_get_space(bpage), - buf_page_get_page_no(bpage)); + dump[j] = BUF_DUMP_CREATE(bpage->id.space(), + bpage->id.page_no()); } ut_a(j == n_pages); @@ -311,10 +376,10 @@ buf_dump( counter == limit) { counter = 0; buf_dump_status( - STATUS_INFO, - "Dumping buffer pool " - ULINTPF "/" ULINTPF ", " - "page " ULINTPF "/" ULINTPF, + STATUS_VERBOSE, + "Dumping buffer pool" + " " ULINTPF "/" ULINTPF "," + " page " ULINTPF "/" ULINTPF, i + 1, srv_buf_pool_instances, j + 1, n_pages); } @@ -357,47 +422,11 @@ buf_dump( ut_sprintf_timestamp(now); - buf_dump_status(STATUS_NOTICE, + buf_dump_status(STATUS_INFO, "Buffer pool(s) dump completed at %s", now); } /*****************************************************************//** -Compare two buffer pool dump entries, used to sort the dump on -space_no,page_no before loading in order to increase the chance for -sequential IO. -@return -1/0/1 if entry 1 is smaller/equal/bigger than entry 2 */ -static -lint -buf_dump_cmp( -/*=========*/ - const buf_dump_t d1, /*!< in: buffer pool dump entry 1 */ - const buf_dump_t d2) /*!< in: buffer pool dump entry 2 */ -{ - if (d1 < d2) { - return(-1); - } else if (d1 == d2) { - return(0); - } else { - return(1); - } -} - -/*****************************************************************//** -Sort a buffer pool dump on space_no, page_no. */ -static -void -buf_dump_sort( -/*==========*/ - buf_dump_t* dump, /*!< in/out: buffer pool dump to sort */ - buf_dump_t* tmp, /*!< in/out: temp storage */ - ulint low, /*!< in: lowest index (inclusive) */ - ulint high) /*!< in: highest index (non-inclusive) */ -{ - UT_SORT_FUNCTION_BODY(buf_dump_sort, dump, tmp, low, high, - 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. */ @@ -405,7 +434,7 @@ UNIV_INLINE void buf_load_throttle_if_needed( /*========================*/ - ulint* last_check_time, /*!< in/out: miliseconds since epoch + ulint* last_check_time, /*!< in/out: milliseconds since epoch of the last time we did check if throttling is needed, we do the check every srv_io_capacity IO ops. */ @@ -455,7 +484,7 @@ buf_load_throttle_if_needed( "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) */) { + if (elapsed_time < 1000 /* 1 sec (1000 milli secs) */) { os_thread_sleep((1000 - elapsed_time) * 1000 /* micro secs */); } @@ -478,7 +507,6 @@ buf_load() char now[32]; FILE* f; buf_dump_t* dump; - buf_dump_t* dump_tmp; ulint dump_n; ulint total_buffer_pools_pages; ulint i; @@ -489,11 +517,9 @@ buf_load() /* Ignore any leftovers from before */ buf_load_abort_flag = FALSE; - ut_snprintf(full_filename, sizeof(full_filename), - "%s%c%s", get_buf_dump_dir(), SRV_PATH_SEPARATOR, - srv_buf_dump_filename); + buf_dump_generate_path(full_filename, sizeof(full_filename)); - buf_load_status(STATUS_NOTICE, + buf_load_status(STATUS_INFO, "Loading buffer pool(s) from %s", full_filename); f = fopen(full_filename, "r"); @@ -523,41 +549,38 @@ buf_load() what = "parsing"; } fclose(f); - buf_load_status(STATUS_ERR, "Error %s '%s', " - "unable to load buffer pool (stage 1)", + buf_load_status(STATUS_ERR, "Error %s '%s'," + " unable to load buffer pool (stage 1)", what, full_filename); return; } /* If dump is larger than the buffer pool(s), then we ignore the extra trailing. This could happen if a dump is made, then buffer - pool is shrunk and then load it attempted. */ + pool is shrunk and then load is attempted. */ total_buffer_pools_pages = buf_pool_get_n_pages() * srv_buf_pool_instances; if (dump_n > total_buffer_pools_pages) { dump_n = total_buffer_pools_pages; } - dump = static_cast<buf_dump_t*>(ut_malloc(dump_n * sizeof(*dump))); - - if (dump == NULL) { + if(dump_n != 0) { + dump = static_cast<buf_dump_t*>(ut_malloc_nokey( + dump_n * sizeof(*dump))); + } else { fclose(f); - buf_load_status(STATUS_ERR, - "Cannot allocate " ULINTPF " bytes: %s", - (ulint) (dump_n * sizeof(*dump)), - strerror(errno)); + ut_sprintf_timestamp(now); + buf_load_status(STATUS_INFO, + "Buffer pool(s) load completed at %s" + " (%s was empty)", now, full_filename); return; } - dump_tmp = static_cast<buf_dump_t*>( - ut_malloc(dump_n * sizeof(*dump_tmp))); - - if (dump_tmp == NULL) { - ut_free(dump); + if (dump == NULL) { fclose(f); buf_load_status(STATUS_ERR, - "Cannot allocate " ULINTPF " bytes: %s", - (ulint) (dump_n * sizeof(*dump_tmp)), + "Cannot allocate %lu bytes: %s", + (ulint) (dump_n * sizeof(*dump)), strerror(errno)); return; } @@ -575,24 +598,22 @@ buf_load() /* else */ ut_free(dump); - ut_free(dump_tmp); fclose(f); buf_load_status(STATUS_ERR, - "Error parsing '%s', unable " - "to load buffer pool (stage 2)", + "Error parsing '%s', unable" + " to load buffer pool (stage 2)", full_filename); return; } if (space_id > ULINT32_MASK || page_no > ULINT32_MASK) { ut_free(dump); - ut_free(dump_tmp); fclose(f); buf_load_status(STATUS_ERR, - "Error parsing '%s': bogus " - "space,page " ULINTPF "," ULINTPF - " at line " ULINTPF ", " - "unable to load buffer pool", + "Error parsing '%s': bogus" + " space,page " ULINTPF "," ULINTPF + " at line " ULINTPF "," + " unable to load buffer pool", full_filename, space_id, page_no, i); @@ -612,42 +633,107 @@ buf_load() if (dump_n == 0) { ut_free(dump); ut_sprintf_timestamp(now); - buf_load_status(STATUS_NOTICE, - "Buffer pool(s) load completed at %s " - "(%s was empty)", now, full_filename); + buf_load_status(STATUS_INFO, + "Buffer pool(s) load completed at %s" + " (%s was empty)", now, full_filename); return; } if (!SHUTTING_DOWN()) { - buf_dump_sort(dump, dump_tmp, 0, dump_n); + std::sort(dump, dump + dump_n); } - ut_free(dump_tmp); - - ulint last_check_time = 0; - ulint last_activity_cnt = 0; + ulint last_check_time = 0; + ulint last_activity_cnt = 0; + + /* Avoid calling the expensive fil_space_acquire_silent() for each + page within the same tablespace. dump[] is sorted by (space, page), + so all pages from a given tablespace are consecutive. */ + ulint cur_space_id = BUF_DUMP_SPACE(dump[0]); + fil_space_t* space = fil_space_acquire_silent(cur_space_id); + page_size_t page_size(space ? space->flags : 0); + + /* JAN: TODO: MySQL 5.7 PSI +#ifdef HAVE_PSI_STAGE_INTERFACE + PSI_stage_progress* pfs_stage_progress + = mysql_set_stage(srv_stage_buffer_pool_load.m_key); + #endif*/ /* HAVE_PSI_STAGE_INTERFACE */ + /* + mysql_stage_set_work_estimated(pfs_stage_progress, dump_n); + mysql_stage_set_work_completed(pfs_stage_progress, 0); + */ for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) { - buf_read_page_async(BUF_DUMP_SPACE(dump[i]), - BUF_DUMP_PAGE(dump[i])); + /* space_id for this iteration of the loop */ + const ulint this_space_id = BUF_DUMP_SPACE(dump[i]); + + if (this_space_id != cur_space_id) { + if (space != NULL) { + fil_space_release(space); + } + + cur_space_id = this_space_id; + space = fil_space_acquire_silent(cur_space_id); + + if (space != NULL) { + const page_size_t cur_page_size( + space->flags); + page_size.copy_from(cur_page_size); + } + } + + /* JAN: TODO: As we use background page read below, + if tablespace is encrypted we cant use it. */ + if (space == NULL || + (space && space->crypt_data && + space->crypt_data->encryption != FIL_SPACE_ENCRYPTION_OFF && + space->crypt_data->type != CRYPT_SCHEME_UNENCRYPTED)) { + continue; + } + + buf_read_page_background( + page_id_t(this_space_id, BUF_DUMP_PAGE(dump[i])), + page_size, true); if (i % 64 == 63) { os_aio_simulated_wake_handler_threads(); } - if (i % 128 == 0) { - buf_load_status(STATUS_INFO, + /* Update the progress every 32 MiB, which is every Nth page, + where N = 32*1024^2 / page_size. */ + static const ulint update_status_every_n_mb = 32; + static const ulint update_status_every_n_pages + = update_status_every_n_mb * 1024 * 1024 + / page_size.physical(); + + if (i % update_status_every_n_pages == 0) { + buf_load_status(STATUS_VERBOSE, "Loaded " ULINTPF "/" ULINTPF " pages", i + 1, dump_n); + /* mysql_stage_set_work_completed(pfs_stage_progress, + i); */ } if (buf_load_abort_flag) { + if (space != NULL) { + fil_space_release(space); + } buf_load_abort_flag = FALSE; ut_free(dump); buf_load_status( - STATUS_NOTICE, + STATUS_INFO, "Buffer pool(s) load aborted on request"); + /* Premature end, set estimated = completed = i and + end the current stage event. */ + /* + mysql_stage_set_work_estimated(pfs_stage_progress, i); + mysql_stage_set_work_completed(pfs_stage_progress, + i); + */ +#ifdef HAVE_PSI_STAGE_INTERFACE + /* mysql_end_stage(); */ +#endif /* HAVE_PSI_STAGE_INTERFACE */ return; } @@ -655,19 +741,29 @@ buf_load() &last_check_time, &last_activity_cnt, i); } + if (space != NULL) { + fil_space_release(space); + } + ut_free(dump); ut_sprintf_timestamp(now); - buf_load_status(STATUS_NOTICE, + buf_load_status(STATUS_INFO, "Buffer pool(s) load completed at %s", now); + + /* Make sure that estimated = completed when we end. */ + /* mysql_stage_set_work_completed(pfs_stage_progress, dump_n); */ + /* End the stage progress event. */ +#ifdef HAVE_PSI_STAGE_INTERFACE + /* mysql_end_stage(); */ +#endif /* HAVE_PSI_STAGE_INTERFACE */ } /*****************************************************************//** Aborts a currently running buffer pool load. This function is called by MySQL code via buffer_pool_load_abort() and it should return immediately because the whole MySQL is frozen during its execution. */ -UNIV_INTERN void buf_load_abort() /*============*/ @@ -680,7 +776,7 @@ This is the main thread for buffer pool dump/load. It waits for an event and when waked up either performs a dump or load and sleeps again. @return this function does not return, it calls os_thread_exit() */ -extern "C" UNIV_INTERN +extern "C" os_thread_ret_t DECLARE_THREAD(buf_dump_thread)( /*============================*/ @@ -688,11 +784,15 @@ DECLARE_THREAD(buf_dump_thread)( required by os_thread_create */ { ut_ad(!srv_read_only_mode); + /* JAN: TODO: MySQL 5.7 PSI +#ifdef UNIV_PFS_THREAD + pfs_register_thread(buf_dump_thread_key); + #endif */ /* UNIV_PFS_THREAD */ srv_buf_dump_thread_active = TRUE; - buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) not yet started"); - buf_load_status(STATUS_INFO, "Loading buffer pool(s) not yet started"); + buf_dump_status(STATUS_VERBOSE, "Dumping of buffer pool not started"); + buf_load_status(STATUS_VERBOSE, "Loading of buffer pool not started"); if (srv_buffer_pool_load_at_startup) { buf_load(); @@ -724,7 +824,7 @@ DECLARE_THREAD(buf_dump_thread)( /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ - os_thread_exit(NULL); + os_thread_exit(); OS_THREAD_DUMMY_RETURN; } |