From c02b424af9b8cf30e3e13cbc555736f1626e3da3 Mon Sep 17 00:00:00 2001
From: AliSQL <AliSQL@alibaba-inc.com>
Date: Sat, 8 Oct 2016 11:03:30 +0800
Subject: [PATCH] Issue#9: PROVIDE ADAPTIVE ALGORITHM FOR INNODB CONCURRENCY
 TICKETS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Description:
------------
To avoide too many concurrency threads，InnoDB use
innodb_thread_concurrency to control the number of operating system
threads concurrently inside InnoDB. When a thread is permitted to
enter InnoDB, it is given a number of "free tickets" equal to the
value of innodb_concurrency_tickets, and the thread can enter and
leave InnoDB freely until it has used up its tickets.

innodb_concurrency_tickets is a fixed number, and it is hard to
choose a proper value fit for both small transactions and large
transactons. With a small innodb_concurrency_tickets value, large
transactons will acquire tickets many times, which extends the length
of time required to complete their task. With a large
innodb_concurrency_tickets value, small transactons may get starved
by too many concurrent large transactions.

This patch provides a method to adaptively adjust tickets assigned to
readonly SELECT SQL statement. For large SELECT query, it may acquire
tickets many times if the previous assigned tickets are exhausted.
To avoid starving smally quries, the tickets assiged to large query are
decreased exponentially according to the number of times it acquire
tickets, i.e. the more times large query acquire tickets, the smaller
tickets it will be assigned. To avoid it getting a too small value,
innodb_rds_min_concurrency_tickets is used as the lower bound of the
tickets number assigned.

Two global InnoDB variables are added,
1. innodb_rds_adaptive_tickets_algo: control whether adaptive algorithm
   is enabled.
2. innodb_rds_min_concurrency_tickets: the lower bound of tickets can
   be assiged under adaptive adjusting.
---
 mysql-test/suite/rds/my.cnf                   |  8 ++
 ...adaptive_concurrency_tickets_issue9.result | 87 +++++++++++++++++++
 ...b_adaptive_concurrency_tickets_issue9.test | 49 +++++++++++
 .../sys_vars/r/sysvars_innodb,32bit.rdiff     | 53 ++++++-----
 .../suite/sys_vars/r/sysvars_innodb.result    | 28 ++++++
 storage/innobase/handler/ha_innodb.cc         | 16 ++++
 storage/innobase/include/srv0srv.h            |  4 +
 storage/innobase/include/trx0trx.h            |  8 ++
 storage/innobase/srv/srv0conc.cc              | 35 +++++++-
 storage/innobase/trx/trx0trx.cc               |  2 +
 10 files changed, 269 insertions(+), 21 deletions(-)
 create mode 100644 mysql-test/suite/rds/my.cnf
 create mode 100644 mysql-test/suite/rds/r/feature_innodb_adaptive_concurrency_tickets_issue9.result
 create mode 100644 mysql-test/suite/rds/t/feature_innodb_adaptive_concurrency_tickets_issue9.test

diff --git a/mysql-test/suite/rds/my.cnf b/mysql-test/suite/rds/my.cnf
new file mode 100644
index 00000000000..153639c67f8
--- /dev/null
+++ b/mysql-test/suite/rds/my.cnf
@@ -0,0 +1,8 @@
+# Use settings from rpl_1slave_base.cnf
+# add setting to connect the slave to the master by default
+!include ../rpl/rpl_1slave_base.cnf
+!include include/default_client.cnf
+
+
+[mysqld.2]
+
diff --git a/mysql-test/suite/rds/r/feature_innodb_adaptive_concurrency_tickets_issue9.result b/mysql-test/suite/rds/r/feature_innodb_adaptive_concurrency_tickets_issue9.result
new file mode 100644
index 00000000000..1dd8359ea22
--- /dev/null
+++ b/mysql-test/suite/rds/r/feature_innodb_adaptive_concurrency_tickets_issue9.result
@@ -0,0 +1,87 @@
+SET @global_start_value1 = @@global.innodb_rds_adaptive_tickets_algo;
+SELECT @global_start_value1;
+@global_start_value1
+0
+SET @global_start_value2 = @@global.innodb_rds_min_concurrency_tickets;
+SELECT @global_start_value2;
+@global_start_value2
+50
+SET @global_start_value3 = @@global.innodb_thread_concurrency;
+SELECT @global_start_value3;
+@global_start_value3
+0
+SET @global_start_value4 = @@global.innodb_concurrency_tickets;
+SELECT @global_start_value4;
+@global_start_value4
+5000
+SET @global_start_value5 = @@global.innodb_thread_sleep_delay;
+SELECT @global_start_value5;
+@global_start_value5
+10000
+SET @@global.innodb_rds_adaptive_tickets_algo = TRUE;
+SELECT @@global.innodb_rds_adaptive_tickets_algo;
+@@global.innodb_rds_adaptive_tickets_algo
+1
+SET @@global.innodb_rds_min_concurrency_tickets = 5;
+SELECT @@global.innodb_rds_min_concurrency_tickets;
+@@global.innodb_rds_min_concurrency_tickets
+5
+SET @@global.innodb_thread_concurrency = 2;
+SELECT @@global.innodb_thread_concurrency;
+@@global.innodb_thread_concurrency
+2
+SET @@global.innodb_concurrency_tickets=8;
+create table t1(c1 int, c2 int) engine=innodb;
+begin;
+insert into t1
+values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);
+commit;
+select * from t1;
+c1	c2
+1	1
+2	2
+3	3
+4	4
+5	5
+6	6
+7	7
+8	8
+9	9
+10	10
+SET @@global.innodb_rds_adaptive_tickets_algo = FALSE;
+SELECT @@global.innodb_rds_adaptive_tickets_algo;
+@@global.innodb_rds_adaptive_tickets_algo
+0
+select * from t1;
+c1	c2
+1	1
+2	2
+3	3
+4	4
+5	5
+6	6
+7	7
+8	8
+9	9
+10	10
+drop table t1;
+SET @@global.innodb_rds_adaptive_tickets_algo = @global_start_value1;
+SELECT @global_start_value1;
+@global_start_value1
+0
+SET @@global.innodb_rds_min_concurrency_tickets = @global_start_value2;
+SELECT @global_start_value2;
+@global_start_value2
+50
+SET @@global.innodb_thread_concurrency = @global_start_value3;
+SELECT @global_start_value3;
+@global_start_value3
+0
+SET @@global.innodb_concurrency_tickets= @global_start_value4;
+SELECT @global_start_value4;
+@global_start_value4
+5000
+SET @@global.innodb_thread_sleep_delay= @global_start_value5;
+SELECT @global_start_value5;
+@global_start_value5
+10000
diff --git a/mysql-test/suite/rds/t/feature_innodb_adaptive_concurrency_tickets_issue9.test b/mysql-test/suite/rds/t/feature_innodb_adaptive_concurrency_tickets_issue9.test
new file mode 100644
index 00000000000..64dc4133416
--- /dev/null
+++ b/mysql-test/suite/rds/t/feature_innodb_adaptive_concurrency_tickets_issue9.test
@@ -0,0 +1,49 @@
+--source include/have_innodb.inc
+--source include/load_sysvars.inc
+
+SET @global_start_value1 = @@global.innodb_rds_adaptive_tickets_algo;
+SELECT @global_start_value1;
+SET @global_start_value2 = @@global.innodb_rds_min_concurrency_tickets;
+SELECT @global_start_value2;
+SET @global_start_value3 = @@global.innodb_thread_concurrency;
+SELECT @global_start_value3;
+SET @global_start_value4 = @@global.innodb_concurrency_tickets;
+SELECT @global_start_value4;
+SET @global_start_value5 = @@global.innodb_thread_sleep_delay;
+SELECT @global_start_value5;
+
+SET @@global.innodb_rds_adaptive_tickets_algo = TRUE;
+SELECT @@global.innodb_rds_adaptive_tickets_algo;
+
+SET @@global.innodb_rds_min_concurrency_tickets = 5;
+SELECT @@global.innodb_rds_min_concurrency_tickets;
+
+SET @@global.innodb_thread_concurrency = 2;
+SELECT @@global.innodb_thread_concurrency;
+
+SET @@global.innodb_concurrency_tickets=8;
+
+create table t1(c1 int, c2 int) engine=innodb;
+begin;
+insert into t1
+values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);
+commit;
+
+select * from t1;
+
+SET @@global.innodb_rds_adaptive_tickets_algo = FALSE;
+SELECT @@global.innodb_rds_adaptive_tickets_algo;
+
+select * from t1;
+
+drop table t1;
+SET @@global.innodb_rds_adaptive_tickets_algo = @global_start_value1;
+SELECT @global_start_value1;
+SET @@global.innodb_rds_min_concurrency_tickets = @global_start_value2;
+SELECT @global_start_value2;
+SET @@global.innodb_thread_concurrency = @global_start_value3;
+SELECT @global_start_value3;
+SET @@global.innodb_concurrency_tickets= @global_start_value4;
+SELECT @global_start_value4;
+SET @@global.innodb_thread_sleep_delay= @global_start_value5;
+SELECT @global_start_value5;
diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
index 09a0a918410..f3229117621 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
@@ -1,5 +1,5 @@
---- suite/sys_vars/r/sysvars_innodb.result	2017-05-10 10:25:27.196836832 +0200
-+++ suite/sys_vars/r/sysvars_innodb.reject	2017-05-10 17:54:00.922046824 +0200
+--- sysvars_innodb.result
++++ sysvars_innodb.result
 @@ -54,7 +54,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	8
@@ -554,7 +554,20 @@
  VARIABLE_COMMENT	Purge threads can be from 1 to 32. Default is 4.
  NUMERIC_MIN_VALUE	1
  NUMERIC_MAX_VALUE	32
-@@ -2322,7 +2322,7 @@
+@@ -2336,10 +2336,10 @@
+ GLOBAL_VALUE_ORIGIN	COMPILE-TIME
+ DEFAULT_VALUE	50
+ VARIABLE_SCOPE	GLOBAL
+-VARIABLE_TYPE	BIGINT UNSIGNED
++VARIABLE_TYPE	INT UNSIGNED
+ VARIABLE_COMMENT	The lower bound of the concurrency_tickets when rds_adaptive_tickets_algo is enabled
+ NUMERIC_MIN_VALUE	1
+-NUMERIC_MAX_VALUE	18446744073709551615
++NUMERIC_MAX_VALUE	4294967295
+ NUMERIC_BLOCK_SIZE	0
+ ENUM_VALUE_LIST	NULL
+ READ_ONLY	NO
+@@ -2350,7 +2350,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	56
  VARIABLE_SCOPE	GLOBAL
@@ -563,7 +576,7 @@
  VARIABLE_COMMENT	Number of pages that must be accessed sequentially for InnoDB to trigger a readahead.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	64
-@@ -2336,7 +2336,7 @@
+@@ -2364,7 +2364,7 @@
  GLOBAL_VALUE_ORIGIN	CONFIG
  DEFAULT_VALUE	4
  VARIABLE_SCOPE	GLOBAL
@@ -572,7 +585,7 @@
  VARIABLE_COMMENT	Number of background read I/O threads in InnoDB.
  NUMERIC_MIN_VALUE	1
  NUMERIC_MAX_VALUE	64
-@@ -2364,10 +2364,10 @@
+@@ -2392,10 +2392,10 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -585,7 +598,7 @@
  NUMERIC_BLOCK_SIZE	0
  ENUM_VALUE_LIST	NULL
  READ_ONLY	NO
-@@ -2392,7 +2392,7 @@
+@@ -2420,7 +2420,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	128
  VARIABLE_SCOPE	GLOBAL
@@ -594,7 +607,7 @@
  VARIABLE_COMMENT	Number of undo logs to use (deprecated).
  NUMERIC_MIN_VALUE	1
  NUMERIC_MAX_VALUE	128
-@@ -2420,7 +2420,7 @@
+@@ -2448,7 +2448,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -603,7 +616,7 @@
  VARIABLE_COMMENT	An InnoDB page number.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	4294967295
-@@ -2434,7 +2434,7 @@
+@@ -2462,7 +2462,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -612,7 +625,7 @@
  VARIABLE_COMMENT	Deprecated and ignored; only exists to allow easier upgrade from earlier XtraDB versions.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	39
-@@ -2448,7 +2448,7 @@
+@@ -2476,7 +2476,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -621,7 +634,7 @@
  VARIABLE_COMMENT	Deprecated and ignored; only exists to allow easier upgrade from earlier XtraDB versions.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	39
-@@ -2462,7 +2462,7 @@
+@@ -2490,7 +2490,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -630,7 +643,7 @@
  VARIABLE_COMMENT	Deprecated and ignored; only exists to allow easier upgrade from earlier XtraDB versions.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	39
-@@ -2504,7 +2504,7 @@
+@@ -2532,7 +2532,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -639,7 +652,7 @@
  VARIABLE_COMMENT	Deprecated and ignored; only exists to allow easier upgrade from earlier XtraDB versions.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	1000
-@@ -2518,7 +2518,7 @@
+@@ -2546,7 +2546,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -648,7 +661,7 @@
  VARIABLE_COMMENT	Deprecated and ignored; only exists to allow easier upgrade from earlier XtraDB versions.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	1
-@@ -2546,7 +2546,7 @@
+@@ -2574,7 +2574,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	1048576
  VARIABLE_SCOPE	GLOBAL
@@ -657,7 +670,7 @@
  VARIABLE_COMMENT	Memory buffer size for index creation
  NUMERIC_MIN_VALUE	65536
  NUMERIC_MAX_VALUE	67108864
-@@ -2770,7 +2770,7 @@
+@@ -2798,7 +2798,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	1
  VARIABLE_SCOPE	GLOBAL
@@ -666,7 +679,7 @@
  VARIABLE_COMMENT	Size of the mutex/lock wait array.
  NUMERIC_MIN_VALUE	1
  NUMERIC_MAX_VALUE	1024
-@@ -2798,10 +2798,10 @@
+@@ -2826,10 +2826,10 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	30
  VARIABLE_SCOPE	GLOBAL
@@ -679,7 +692,7 @@
  NUMERIC_BLOCK_SIZE	0
  ENUM_VALUE_LIST	NULL
  READ_ONLY	NO
-@@ -2840,7 +2840,7 @@
+@@ -2868,7 +2868,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -688,7 +701,7 @@
  VARIABLE_COMMENT	Helps in performance tuning in heavily concurrent environments. Sets the maximum number of threads allowed inside InnoDB. Value 0 will disable the thread throttling.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	1000
-@@ -2854,7 +2854,7 @@
+@@ -2882,7 +2882,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	10000
  VARIABLE_SCOPE	GLOBAL
@@ -697,7 +710,7 @@
  VARIABLE_COMMENT	Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0 disable a sleep
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	1000000
-@@ -2952,7 +2952,7 @@
+@@ -2980,7 +2980,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	128
  VARIABLE_SCOPE	GLOBAL
@@ -706,7 +719,7 @@
  VARIABLE_COMMENT	Number of undo logs to use.
  NUMERIC_MIN_VALUE	1
  NUMERIC_MAX_VALUE	128
-@@ -2980,7 +2980,7 @@
+@@ -3008,7 +3008,7 @@
  GLOBAL_VALUE_ORIGIN	COMPILE-TIME
  DEFAULT_VALUE	0
  VARIABLE_SCOPE	GLOBAL
@@ -715,7 +728,7 @@
  VARIABLE_COMMENT	Number of undo tablespaces to use.
  NUMERIC_MIN_VALUE	0
  NUMERIC_MAX_VALUE	127
-@@ -3092,7 +3092,7 @@
+@@ -3120,7 +3120,7 @@
  GLOBAL_VALUE_ORIGIN	CONFIG
  DEFAULT_VALUE	4
  VARIABLE_SCOPE	GLOBAL
diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
index e9fc3693889..0fc4211eace 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
@@ -2316,6 +2316,34 @@ NUMERIC_BLOCK_SIZE	NULL
 ENUM_VALUE_LIST	OFF,ON
 READ_ONLY	NO
 COMMAND_LINE_ARGUMENT	NONE
+VARIABLE_NAME	INNODB_RDS_ADAPTIVE_TICKETS_ALGO
+SESSION_VALUE	NULL
+GLOBAL_VALUE	OFF
+GLOBAL_VALUE_ORIGIN	COMPILE-TIME
+DEFAULT_VALUE	OFF
+VARIABLE_SCOPE	GLOBAL
+VARIABLE_TYPE	BOOLEAN
+VARIABLE_COMMENT	Whether to enable the adaptive tickets algorithm
+NUMERIC_MIN_VALUE	NULL
+NUMERIC_MAX_VALUE	NULL
+NUMERIC_BLOCK_SIZE	NULL
+ENUM_VALUE_LIST	OFF,ON
+READ_ONLY	NO
+COMMAND_LINE_ARGUMENT	REQUIRED
+VARIABLE_NAME	INNODB_RDS_MIN_CONCURRENCY_TICKETS
+SESSION_VALUE	NULL
+GLOBAL_VALUE	50
+GLOBAL_VALUE_ORIGIN	COMPILE-TIME
+DEFAULT_VALUE	50
+VARIABLE_SCOPE	GLOBAL
+VARIABLE_TYPE	BIGINT UNSIGNED
+VARIABLE_COMMENT	The lower bound of the concurrency_tickets when rds_adaptive_tickets_algo is enabled
+NUMERIC_MIN_VALUE	1
+NUMERIC_MAX_VALUE	18446744073709551615
+NUMERIC_BLOCK_SIZE	0
+ENUM_VALUE_LIST	NULL
+READ_ONLY	NO
+COMMAND_LINE_ARGUMENT	REQUIRED
 VARIABLE_NAME	INNODB_READ_AHEAD_THRESHOLD
 SESSION_VALUE	NULL
 GLOBAL_VALUE	56
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index ecf85a81638..1f278b04e35 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -15971,6 +15971,10 @@ ha_innobase::external_lock(
 
 	if (trx->n_mysql_tables_in_use == 0) {
 
+		/* Set the n_acquire_tickets_to_enter to zero because it has
+		ended the statement at this moment */
+		trx->n_acquire_tickets_to_enter = 0;
+
 		trx->mysql_n_tables_locked = 0;
 		m_prebuilt->used_in_HANDLER = FALSE;
 
@@ -20377,6 +20381,16 @@ static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter,
   "Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket",
   NULL, NULL, 5000L, 1L, ~0UL, 0);
 
+static MYSQL_SYSVAR_BOOL(rds_adaptive_tickets_algo, srv_adaptive_tickets_algo,
+  PLUGIN_VAR_RQCMDARG,
+  "Whether to enable the adaptive tickets algorithm",
+  NULL, NULL, FALSE);
+
+static MYSQL_SYSVAR_ULONG(rds_min_concurrency_tickets, srv_min_n_free_tickets_to_enter,
+  PLUGIN_VAR_RQCMDARG,
+  "The lower bound of the concurrency_tickets when rds_adaptive_tickets_algo is enabled",
+  NULL, NULL, 50L, 1L, ~0UL, 0);
+
 static MYSQL_SYSVAR_BOOL(deadlock_detect, innobase_deadlock_detect,
   PLUGIN_VAR_NOCMDARG,
   "Enable/disable InnoDB deadlock detector (default ON)."
@@ -21067,6 +21081,8 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
   MYSQL_SYSVAR(checksums),
   MYSQL_SYSVAR(commit_concurrency),
   MYSQL_SYSVAR(concurrency_tickets),
+  MYSQL_SYSVAR(rds_adaptive_tickets_algo),
+  MYSQL_SYSVAR(rds_min_concurrency_tickets),
   MYSQL_SYSVAR(compression_level),
   MYSQL_SYSVAR(data_file_path),
   MYSQL_SYSVAR(temp_data_file_path),
diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h
index 20206f9d9eb..00cc4180b40 100644
--- a/storage/innobase/include/srv0srv.h
+++ b/storage/innobase/include/srv0srv.h
@@ -542,6 +542,10 @@ extern ulong	srv_n_free_tickets_to_enter;
 extern ulong	srv_thread_sleep_delay;
 extern uint	srv_spin_wait_delay;
 
+/* Whether to enable adaptive tickets algorithm */
+extern my_bool	srv_adaptive_tickets_algo;
+extern ulong	srv_min_n_free_tickets_to_enter;
+
 extern ulint	srv_truncated_status_writes;
 /** Number of initialized rollback segments for persistent undo log */
 extern ulong	srv_available_undo_logs;
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 6c9223f31be..4410728d14d 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -984,6 +984,14 @@ struct trx_t {
 					field contains the end offset of the
 					binlog entry */
 	/*------------------------------*/
+	ulint		n_acquire_tickets_to_enter;
+					/*!< number of times the current SQL
+					statement has entered innodb to acquire
+					tickets, increase each time, reset
+					to zero at the end of current SQL
+					statement. This is used for adaptive
+					tickets algorithm */
+	/*------------------------------*/
 	ib_uint32_t	n_mysql_tables_in_use; /*!< number of Innobase tables
 					used in the processing of the current
 					SQL statement in MySQL */
diff --git a/storage/innobase/srv/srv0conc.cc b/storage/innobase/srv/srv0conc.cc
index 9e2aa7d7af4..be0cb28e3f8 100644
--- a/storage/innobase/srv/srv0conc.cc
+++ b/storage/innobase/srv/srv0conc.cc
@@ -49,6 +49,9 @@ Created 2011/04/18 Sunny Bains
 SQL query after it has once got the ticket. */
 ulong	srv_n_free_tickets_to_enter = 500;
 
+my_bool	srv_adaptive_tickets_algo;
+ulong	srv_min_n_free_tickets_to_enter;
+
 /** Maximum sleep delay (in micro-seconds), value of 0 disables it. */
 ulong	srv_adaptive_max_sleep_delay = 150000;
 
@@ -82,6 +85,35 @@ struct srv_conc_t {
 /* Control variables for tracking concurrency. */
 static srv_conc_t	srv_conc;
 
+/*********************************************************************//**
+Calculate how many tickets should assigned to current statement.
+If adaptive algorithm(innodb_rds_adaptive_tickets_algo) is disabled,
+return srv_n_free_tickets_to_enter as Oracle does; otherwise tickets number
+is decreased exponentially according to the number of times current statement
+entered innodb to acquire tickets.
+*/
+static inline
+ulint
+srv_conc_calc_tickets(
+/*================*/
+	trx_t* trx)			/*!< in: transaction that wants to
+					enter InnoDB */
+{
+	ulint tickets = srv_n_free_tickets_to_enter;
+	if (!srv_adaptive_tickets_algo) {
+		return tickets;
+	}
+
+	if (thd_trx_is_read_only(trx->mysql_thd)
+	    && tickets > srv_min_n_free_tickets_to_enter) {
+		tickets >>= (trx->n_acquire_tickets_to_enter - 1);
+	}
+
+	return ((tickets < srv_min_n_free_tickets_to_enter)
+		? srv_min_n_free_tickets_to_enter
+		: tickets);
+}
+
 /*********************************************************************//**
 Note that a user thread is entering InnoDB. */
 static
@@ -92,7 +124,8 @@ srv_enter_innodb_with_tickets(
 					to enter InnoDB */
 {
 	trx->declared_to_be_inside_innodb = TRUE;
-	trx->n_tickets_to_enter_innodb = srv_n_free_tickets_to_enter;
+	trx->n_acquire_tickets_to_enter++;
+	trx->n_tickets_to_enter_innodb = srv_conc_calc_tickets(trx);
 }
 
 /*********************************************************************//**
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 91e60571438..30132be8f37 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -355,6 +355,8 @@ trx_create_low()
 
 	DBUG_LOG("trx", "Create: " << trx);
 
+	trx->n_acquire_tickets_to_enter = 0;
+
 	heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 8);
 
 	alloc = ib_heap_allocator_create(heap);
-- 
2.20.1

