diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d86afee51ca..c212855ea85 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -148,6 +148,9 @@ long innobase_max_merged_io = 64; /* Number of background IO threads for read and write. */ long innobase_read_io_threads, innobase_write_io_threads; +/* Use timer based InnoDB concurrency throttling flag */ +static my_bool innobase_thread_concurrency_timer_based; + /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call srv_active_wake_master_thread after each fetch or search, we only do @@ -1602,6 +1605,9 @@ innobase_init( srv_n_log_files = (ulint) innobase_log_files_in_group; srv_log_file_size = (ulint) innobase_log_file_size; + srv_thread_concurrency_timer_based = + (ibool) innobase_thread_concurrency_timer_based; + #ifdef UNIV_LOG_ARCHIVE srv_log_archive_on = (ulint) innobase_log_archive; #endif /* UNIV_LOG_ARCHIVE */ @@ -8236,6 +8242,12 @@ static MYSQL_SYSVAR_ULONG(sync_spin_loops, srv_n_spin_wait_rounds, "Count of spin-loop rounds in InnoDB mutexes", NULL, NULL, 20L, 0L, ~0L, 0); +static MYSQL_SYSVAR_BOOL(thread_concurrency_timer_based, + innobase_thread_concurrency_timer_based, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Use InnoDB timer based concurrency throttling. ", + NULL, NULL, TRUE); + static MYSQL_SYSVAR_ULONG(thread_concurrency, srv_thread_concurrency, PLUGIN_VAR_RQCMDARG, "Helps in performance tuning in heavily concurrent environments. Sets the maximum number of threads allowed inside InnoDB. Value 0 will disable the thread throttling.", @@ -8278,6 +8290,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(read_io_threads), MYSQL_SYSVAR(write_io_threads), MYSQL_SYSVAR(max_merged_io), + MYSQL_SYSVAR(thread_concurrency_timer_based), MYSQL_SYSVAR(file_per_table), MYSQL_SYSVAR(flush_log_at_trx_commit), MYSQL_SYSVAR(flush_method), diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index fa206f16415..87b42c89ac0 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -89,6 +89,8 @@ extern ulint srv_awe_window_size; extern ulint srv_mem_pool_size; extern ulint srv_lock_table_size; +extern ibool srv_thread_concurrency_timer_based; + extern ulint srv_n_file_io_threads; /* Number of background IO threads for read and write. Replaces * srv_n_file_io_threads. */ diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 5eee43092da..23a20393939 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -171,6 +171,7 @@ ulint srv_awe_window_size = 0; /* size in pages; MySQL inits ulint srv_mem_pool_size = ULINT_MAX; /* size in bytes */ ulint srv_lock_table_size = ULINT_MAX; + ulint srv_io_capacity = ULINT_MAX; /* Number of IO operations per second the server can do */ @@ -288,19 +289,20 @@ Value 10 should be good if there are less than 4 processors + 4 disks in the computer. Bigger computers need bigger values. Value 0 will disable the concurrency check. */ +ibool srv_thread_concurrency_timer_based = TRUE; ulong srv_thread_concurrency = 0; ulong srv_commit_concurrency = 0; os_fast_mutex_t srv_conc_mutex; /* this mutex protects srv_conc data structures */ -lint srv_conc_n_threads = 0; /* number of OS threads currently +lint srv_conc_n_threads = 0; /* number of OS threads currently inside InnoDB; it is not an error if this drops temporarily below zero because we do not demand that every thread increments this, but a thread waiting for a lock decrements this temporarily */ -ulint srv_conc_n_waiting_threads = 0; /* number of OS threads waiting in the +ulint srv_conc_n_waiting_threads = 0; /* number of OS threads waiting in the FIFO for a permission to enter InnoDB */ @@ -1061,6 +1063,91 @@ ulong srv_max_purge_lag = 0; Puts an OS thread to wait if there are too many concurrent threads (>= srv_thread_concurrency) inside InnoDB. The threads wait in a FIFO queue. */ +static void +inc_srv_conc_n_threads(lint *n_threads) +{ + *n_threads = os_atomic_increment(&srv_conc_n_threads, 1); +} + +static void +dec_srv_conc_n_threads() +{ + os_atomic_increment(&srv_conc_n_threads, -1); +} + +static void +print_already_in_error(trx_t* trx) +{ + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: trying to declare trx" + " to enter InnoDB, but\n" + "InnoDB: it already is declared.\n", stderr); + trx_print(stderr, trx, 0); + putc('\n', stderr); + return; +} + +static void +enter_innodb_with_tickets(trx_t* trx) +{ + trx->declared_to_be_inside_innodb = TRUE; + trx->n_tickets_to_enter_innodb = SRV_FREE_TICKETS_TO_ENTER; + return; +} + +static void +srv_conc_enter_innodb_timer_based(trx_t* trx) +{ + lint conc_n_threads; + ibool has_yielded = FALSE; + ulint has_slept = 0; + + if (trx->declared_to_be_inside_innodb) { + print_already_in_error(trx); + } +retry: + if (srv_conc_n_threads < (lint) srv_thread_concurrency) { + inc_srv_conc_n_threads(&conc_n_threads); + if (conc_n_threads <= srv_thread_concurrency) { + enter_innodb_with_tickets(trx); + return; + } + dec_srv_conc_n_threads(&conc_n_threads); + } + if (!has_yielded) + { + has_yielded = TRUE; + os_thread_yield(); + goto retry; + } + if (trx->has_search_latch + || NULL != UT_LIST_GET_FIRST(trx->trx_locks)) { + + inc_srv_conc_n_threads(&conc_n_threads); + enter_innodb_with_tickets(trx); + return; + } + if (has_slept < 2) + { + trx->op_info = "sleeping before entering InnoDB"; + os_thread_sleep(10000); + trx->op_info = ""; + has_slept++; + } + inc_srv_conc_n_threads(&conc_n_threads); + enter_innodb_with_tickets(trx); + return; +} + +static void +srv_conc_exit_innodb_timer_based(trx_t* trx) +{ + dec_srv_conc_n_threads(); + trx->declared_to_be_inside_innodb = FALSE; + trx->n_tickets_to_enter_innodb = 0; + return; +} + void srv_conc_enter_innodb( /*==================*/ @@ -1091,15 +1178,17 @@ srv_conc_enter_innodb( return; } +#ifdef UNIV_SYNC_ATOMIC + if (srv_thread_concurrency_timer_based) { + srv_conc_enter_innodb_timer_based(trx); + return; + } +#endif + os_fast_mutex_lock(&srv_conc_mutex); retry: if (trx->declared_to_be_inside_innodb) { - ut_print_timestamp(stderr); - fputs(" InnoDB: Error: trying to declare trx" - " to enter InnoDB, but\n" - "InnoDB: it already is declared.\n", stderr); - trx_print(stderr, trx, 0); - putc('\n', stderr); + print_already_in_error(trx); os_fast_mutex_unlock(&srv_conc_mutex); return; @@ -1226,17 +1315,25 @@ srv_conc_force_enter_innodb( trx_t* trx) /* in: transaction object associated with the thread */ { + lint conc_n_threads; + if (UNIV_LIKELY(!srv_thread_concurrency)) { return; } +#ifdef UNIV_SYNC_ATOMIC + if (srv_thread_concurrency_timer_based) { + inc_srv_conc_n_threads(&conc_n_threads); + trx->declared_to_be_inside_innodb = TRUE; + trx->n_tickets_to_enter_innodb = 1; + return; + } +#endif os_fast_mutex_lock(&srv_conc_mutex); - srv_conc_n_threads++; trx->declared_to_be_inside_innodb = TRUE; trx->n_tickets_to_enter_innodb = 1; - os_fast_mutex_unlock(&srv_conc_mutex); } @@ -1268,6 +1365,14 @@ srv_conc_force_exit_innodb( return; } +#ifdef UNIV_SYNC_ATOMIC + if (srv_thread_concurrency_timer_based) + { + srv_conc_exit_innodb_timer_based(trx); + return; + } +#endif + os_fast_mutex_lock(&srv_conc_mutex); srv_conc_n_threads--;