summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--innobase/include/srv0srv.h2
-rw-r--r--innobase/include/trx0sys.h4
-rw-r--r--innobase/row/row0mysql.c17
-rw-r--r--innobase/srv/srv0srv.c6
-rw-r--r--innobase/trx/trx0purge.c38
-rw-r--r--innobase/trx/trx0rseg.c7
-rw-r--r--sql/ha_innodb.h1
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/set_var.cc4
9 files changed, 84 insertions, 1 deletions
diff --git a/innobase/include/srv0srv.h b/innobase/include/srv0srv.h
index 57ca1f84f26..c76a1917615 100644
--- a/innobase/include/srv0srv.h
+++ b/innobase/include/srv0srv.h
@@ -99,6 +99,7 @@ extern ibool srv_use_doublewrite_buf;
extern ibool srv_set_thread_priorities;
extern int srv_query_thread_priority;
+extern ulint srv_max_purge_lag;
/*-------------------------------------------*/
extern ulint srv_n_rows_inserted;
@@ -152,6 +153,7 @@ extern ulint srv_test_array_size;
extern ulint srv_activity_count;
extern ulint srv_fatal_semaphore_wait_threshold;
+extern ulint srv_dml_needed_delay;
extern mutex_t* kernel_mutex_temp;/* mutex protecting the server, trx structs,
query threads, and lock table: we allocate
diff --git a/innobase/include/trx0sys.h b/innobase/include/trx0sys.h
index c7ef4d1929d..4d83c5eeaae 100644
--- a/innobase/include/trx0sys.h
+++ b/innobase/include/trx0sys.h
@@ -419,6 +419,10 @@ struct trx_sys_struct{
trx_rseg_t* rseg_array[TRX_SYS_N_RSEGS];
/* Pointer array to rollback segments;
NULL if slot not in use */
+ ulint rseg_history_len;/* Length of the TRX_RSEG_HISTORY
+ list (update undo logs for committed
+ transactions), protected by
+ rseg->mutex */
UT_LIST_BASE_NODE_T(read_view_t) view_list;
/* List of read views sorted on trx no,
biggest first */
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 3eb4f0de60e..78b2aa8e28f 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -89,6 +89,19 @@ row_mysql_is_system_table(
|| 0 == strcmp(name + 6, "user")
|| 0 == strcmp(name + 6, "db"));
}
+
+/***********************************************************************
+Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */
+static
+void
+row_mysql_delay_if_needed(void)
+/*===========================*/
+{
+ if (srv_dml_needed_delay) {
+ os_thread_sleep(srv_dml_needed_delay);
+ }
+}
+
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
returns pointer to the field data. */
@@ -856,6 +869,8 @@ row_insert_for_mysql(
trx->op_info = (char *) "inserting";
+ row_mysql_delay_if_needed();
+
trx_start_if_not_started(trx);
if (node == NULL) {
@@ -1071,6 +1086,8 @@ row_update_for_mysql(
trx->op_info = (char *) "updating or deleting";
+ row_mysql_delay_if_needed();
+
trx_start_if_not_started(trx);
node = prebuilt->upd_node;
diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c
index 0643ab96c1d..0a814268a36 100644
--- a/innobase/srv/srv0srv.c
+++ b/innobase/srv/srv0srv.c
@@ -58,6 +58,10 @@ ulint srv_activity_count = 0;
/* The following is the maximum allowed duration of a lock wait. */
ulint srv_fatal_semaphore_wait_threshold = 600;
+/* How much data manipulation language (DML) statements need to be delayed,
+in microseconds, in order to reduce the lagging of the purge thread. */
+ulint srv_dml_needed_delay = 0;
+
ibool srv_lock_timeout_and_monitor_active = FALSE;
ibool srv_error_monitor_active = FALSE;
@@ -841,6 +845,8 @@ srv_general_init(void)
/*======================= InnoDB Server FIFO queue =======================*/
+/* Maximum allowable purge history length. <=0 means 'infinite'. */
+ulint srv_max_purge_lag = 0;
/*************************************************************************
Puts an OS thread to wait if there are too many concurrent threads
diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c
index a8b6b9fcc21..d772af47b29 100644
--- a/innobase/trx/trx0purge.c
+++ b/innobase/trx/trx0purge.c
@@ -295,6 +295,9 @@ trx_purge_add_update_undo_to_history(
/* Add the log as the first in the history list */
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
+ mutex_enter(&kernel_mutex);
+ trx_sys->rseg_history_len++;
+ mutex_exit(&kernel_mutex);
/* Write the trx number to the undo log header */
mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
@@ -386,6 +389,12 @@ loop:
flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
+
+ mutex_enter(&kernel_mutex);
+ ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
+ trx_sys->rseg_history_len -= n_removed_logs;
+ mutex_exit(&kernel_mutex);
+
freed = FALSE;
while (!freed) {
@@ -470,6 +479,11 @@ loop:
}
if (cmp >= 0) {
+ mutex_enter(&kernel_mutex);
+ ut_a(trx_sys->rseg_history_len >= n_removed_logs);
+ trx_sys->rseg_history_len -= n_removed_logs;
+ mutex_exit(&kernel_mutex);
+
flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
log_hdr + TRX_UNDO_HISTORY_NODE,
n_removed_logs, &mtr);
@@ -1031,6 +1045,30 @@ trx_purge(void)
purge_sys->view = NULL;
mem_heap_empty(purge_sys->heap);
+ /* Determine how much data manipulation language (DML) statements
+ need to be delayed in order to reduce the lagging of the purge
+ thread. */
+ srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
+
+ /* If we cannot advance the 'purge view' because of an old
+ 'consistent read view', then the DML statements cannot be delayed.
+ Also, srv_max_purge_lag <= 0 means 'infinity'. */
+ if (srv_max_purge_lag > 0
+ && !UT_LIST_GET_LAST(trx_sys->view_list)) {
+ float ratio = (float) trx_sys->rseg_history_len
+ / srv_max_purge_lag;
+ if (ratio > ULINT_MAX / 10000) {
+ /* Avoid overflow: maximum delay is 4295 seconds */
+ srv_dml_needed_delay = ULINT_MAX;
+ } else if (ratio > 1) {
+ /* If the history list length exceeds the
+ innodb_max_purge_lag, the
+ data manipulation statements are delayed
+ by at least 5000 microseconds. */
+ srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
+ }
+ }
+
purge_sys->view = read_view_oldest_copy_or_open_new(NULL,
purge_sys->heap);
mutex_exit(&kernel_mutex);
diff --git a/innobase/trx/trx0rseg.c b/innobase/trx/trx0rseg.c
index e3885c86def..a01d4bb835d 100644
--- a/innobase/trx/trx0rseg.c
+++ b/innobase/trx/trx0rseg.c
@@ -135,6 +135,7 @@ trx_rseg_mem_create(
trx_ulogf_t* undo_log_hdr;
fil_addr_t node_addr;
ulint sum_of_undo_sizes;
+ ulint len;
#ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&kernel_mutex));
@@ -166,7 +167,9 @@ trx_rseg_mem_create(
MLOG_4BYTES, mtr)
+ 1 + sum_of_undo_sizes;
- if (flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr) > 0) {
+ len = flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr);
+ if (len > 0) {
+ trx_sys->rseg_history_len += len;
node_addr = trx_purge_get_log_from_hist(
flst_get_last(rseg_header + TRX_RSEG_HISTORY,
@@ -206,6 +209,8 @@ trx_rseg_list_and_array_init(
UT_LIST_INIT(trx_sys->rseg_list);
+ trx_sys->rseg_history_len = 0;
+
for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 5736f70c65c..74acc0640c9 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -207,6 +207,7 @@ extern my_bool innobase_log_archive,
innobase_create_status_file;
extern "C" {
extern ulong srv_max_buf_pool_modified_pct;
+extern ulong srv_max_purge_lag;
}
extern TYPELIB innobase_lock_typelib;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5838bd909dd..d36b441ac9a 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3520,6 +3520,7 @@ enum options_mysqld {
OPT_INNODB_LOG_BUFFER_SIZE,
OPT_INNODB_BUFFER_POOL_SIZE,
OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
+ OPT_INNODB_MAX_PURGE_LAG,
OPT_INNODB_FILE_IO_THREADS,
OPT_INNODB_LOCK_WAIT_TIMEOUT,
OPT_INNODB_THREAD_CONCURRENCY,
@@ -3700,6 +3701,11 @@ struct my_option my_long_options[] =
{"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT,
"Percentage of dirty pages allowed in bufferpool", (gptr*) &srv_max_buf_pool_modified_pct,
(gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0},
+ {"innodb_max_purge_lag", OPT_INNODB_MAX_PURGE_LAG,
+ "",
+ (gptr*) &srv_max_purge_lag,
+ (gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L,
+ 0, 1L, 0},
{"innodb_table_locks", OPT_INNODB_TABLE_LOCKS,
"If Innodb should enforce LOCK TABLE",
(gptr*) &global_system_variables.innodb_table_locks,
diff --git a/sql/set_var.cc b/sql/set_var.cc
index a6a5ea72c71..d5aadbfbdab 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -263,6 +263,8 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout",
#ifdef HAVE_INNOBASE_DB
sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct",
&srv_max_buf_pool_modified_pct);
+sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag",
+ &srv_max_purge_lag);
sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks",
&SV::innodb_table_locks);
#endif
@@ -451,6 +453,7 @@ sys_var *sys_variables[]=
&sys_os,
#ifdef HAVE_INNOBASE_DB
&sys_innodb_max_dirty_pages_pct,
+ &sys_innodb_max_purge_lag,
&sys_innodb_table_locks,
#endif
&sys_unique_checks
@@ -523,6 +526,7 @@ struct show_var_st init_vars[]= {
{"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR},
{"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG},
{sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS},
+ {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS},
{sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
#endif
{sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS},