WL#4976: Introduce timer-based InnoDB Thread Concurrency
Affects: Server-5.4
—
Status: Complete
Introduce timer-based InnoDB Thread Concurrency. In MySQL 5.4.0
In MySQL 5.4.0, add a new system variable: innodb_thread_concurrency_timer_based innodb_thread_concurrency_timer_based: If enabled, use a lock-free timer-based method of handling thread concurrency. If disabled, the original mutex-based method is used. For the lock-free concurrency method to be used, two requirements must be satisfied: 1) The innodb_thread_concurrency system variable must be set to a number greater than 0. The default value in MySQL 5.4 is 0, so it must be changed to use the lock-free concurrency method. 2) Atomic instructions must be available; that is, the Innodb_have_sync_atomic status variable must be ON. If innodb_thread_concurrency is 0 or Innodb_have_sync_atomic is OFF, enabling innodb_thread_concurrency_timer_based has no effect. Details: 1) innodb_thread_concurrency_timer_based is a global system variable and does not have a session value. 2) The variable is not dynamic (cannot be changed at runtime). To assign values different from the defaults, users must set them at Server startup.
These patches introduced a new timer based innodb thread concurrency. A new parameter innodb_thread_concurrency_timer_based is used to get this new feature (it is set by default). The new feature is only available on platforms where atomic instructions are available. This patch introduces the timer based InnoDB Thread Concurrency. === modified file 'storage/innobase/handler/ha_innodb.cc' --- storage/innobase/handler/ha_innodb.cc 2008-11-17 21:54:32 +0000 +++ storage/innobase/handler/ha_innodb.cc 2008-12-12 20:17:15 +0000 @@ -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_loop "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 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), === modified file 'storage/innobase/include/srv0srv.h' --- storage/innobase/include/srv0srv.h 2008-11-17 21:54:32 +0000 +++ storage/innobase/include/srv0srv.h 2008-12-12 20:17:15 +0000 @@ -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. */ === modified file 'storage/innobase/srv/srv0srv.c' --- storage/innobase/srv/srv0srv.c 2008-12-10 17:54:23 +0000 +++ storage/innobase/srv/srv0srv.c 2008-12-12 20:17:15 +0000 @@ -171,6 +171,7 @@ ulint srv_awe_window_size = 0; /* size 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 les 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--;
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.