MySQL 数据库服务端是由 Server 层 和 引擎层组成
通过 mysql client 或者 驱动程序连接 MySQL时,每创建一个连接,MySQL 的 Server层 就会创建一个线程来处理该连接(实际情况更可能是 MySQL server 是用 thread pool 线程池来处理连接请求的:当客户端1的连接断开时,对应的线程进入线程池而不是销毁;客户端2马上建立连接就会发现客户端1用过的THREAD_OS_ID在处理客户端2的连接;站在操作系统角度看对于建立和断开链接不会存在线程的创建和销毁,当然前提是线程池里有空闲线程);此处的线程就是通过 show processlist 查看到的列表,你可能会有疑问 show processlist 的 id 和操作系统 ps 查看到的进程或线程id 有什么关系? 其实是一一对应的,比如 show processlist 的 id 844227 就是 performance_schema.threads 表的列 PROCESSLIST_ID 从而可以得知对应的 THREAD_OS_ID 为6789,通过 ps -eLf 就可以看到该值。(具体可以并发处理多少连接是由环境变量 max_connections 决定的,这是 MySQL Server层的行为还未进入引擎层,也就是说和环境变量 innodb_thread_concurrency 还没有关系呢) 具体如下:
mysql> show processlist;
+--------+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+--------+------+-----------+------+---------+------+----------+------------------+
| 844227 | root | localhost | NULL | Sleep | 530 | | NULL |
| 844228 | root | localhost | NULL | Query | 0 | starting | show processlist |
| 844230 | root | localhost | NULL | Sleep | 505 | | NULL |
| 844231 | root | localhost | NULL | Sleep | 959 | | NULL |
| 844232 | root | localhost | NULL | Sleep | 930 | | NULL |
| 844233 | root | localhost | NULL | Sleep | 845 | | NULL |
| 844234 | root | localhost | NULL | Sleep | 834 | | NULL |
| 844236 | root | localhost | NULL | Sleep | 836 | | NULL |
| 844237 | root | localhost | NULL | Sleep | 583 | | NULL |
+--------+------+-----------+------+---------+------+----------+------------------+
9 rows in set (0.00 sec)
mysql>
mysql> //show processlist 为9个连接,对应的Threads_connected 也为9,
mysql> //同时 Threads_cached 为0代表线程池无空闲线程;第10个连接进来时会创建新的线程
mysql> SHOW STATUS LIKE 'Threads%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 0 |
| Threads_connected | 9 |
| Threads_created | 2477 |
| Threads_running | 1 |
+-------------------+-------+
4 rows in set (0.01 sec)
mysql>
mysql> select * from performance_schema.threads where type = 'FOREGROUND' and name like '%conn%' ;
+-----------+---------------------------+------------+----------------+------------------+------------------+----------------+---------------------+------------------+-------------------+---------------------------------------------------------------------------------------------+------------------+------+--------------+---------+-----------------+--------------+
| THREAD_ID | NAME | TYPE | PROCESSLIST_ID | PROCESSLIST_USER | PROCESSLIST_HOST | PROCESSLIST_DB | PROCESSLIST_COMMAND | PROCESSLIST_TIME | PROCESSLIST_STATE | PROCESSLIST_INFO | PARENT_THREAD_ID | ROLE | INSTRUMENTED | HISTORY | CONNECTION_TYPE | THREAD_OS_ID |
+-----------+---------------------------+------------+----------------+------------------+------------------+----------------+---------------------+------------------+-------------------+---------------------------------------------------------------------------------------------+------------------+------+--------------+---------+-----------------+--------------+
| 844252 | thread/sql/one_connection | FOREGROUND | 844227 | root | localhost | NULL | Sleep | 581 | NULL | NULL | NULL | NULL | YES | YES | Socket | 6789 |
| 844253 | thread/sql/one_connection | FOREGROUND | 844228 | root | localhost | NULL | Query | 0 | Sending data | select * from performance_schema.threads where type = 'FOREGROUND' and name like '%conn%' | NULL | NULL | YES | YES | Socket | 28020 |
| 844255 | thread/sql/one_connection | FOREGROUND | 844230 | root | localhost | NULL | Sleep | 556 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28276 |
| 844256 | thread/sql/one_connection | FOREGROUND | 844231 | root | localhost | NULL | Sleep | 1010 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28036 |
| 844257 | thread/sql/one_connection | FOREGROUND | 844232 | root | localhost | NULL | Sleep | 981 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28356 |
| 844258 | thread/sql/one_connection | FOREGROUND | 844233 | root | localhost | NULL | Sleep | 896 | NULL | NULL | NULL | NULL | YES | YES | Socket | 6785 |
| 844259 | thread/sql/one_connection | FOREGROUND | 844234 | root | localhost | NULL | Sleep | 885 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28277 |
| 844261 | thread/sql/one_connection | FOREGROUND | 844236 | root | localhost | NULL | Sleep | 887 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28222 |
| 844262 | thread/sql/one_connection | FOREGROUND | 844237 | root | localhost | NULL | Sleep | 634 | NULL | NULL | NULL | NULL | YES | YES | Socket | 28360 |
+-----------+---------------------------+------------+----------------+------------------+------------------+----------------+---------------------+------------------+-------------------+---------------------------------------------------------------------------------------------+------------------+------+--------------+---------+-----------------+--------------+
9 rows in set (0.00 sec)
mysql>
[root@dbs-test-rob ~]# //看最后一行的线程的启动时间 16:43 可知,这就是在线程池中无空闲线程时为接下来的新建连接(第10个连接)而创建的线程
[root@dbs-test-rob ~]# ps -eLf | grep mysqld
mysql 4105 1 28020 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28036 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28222 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28276 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28277 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28356 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 28360 0 37 9月06 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 6785 0 37 9月08 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 6789 0 37 9月08 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
mysql 4105 1 23536 0 37 16:43 ? 00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
[root@dbs-test-rob ~]#
innodb_thread_concurrency:InnoDB tries to keep the number of operating system threads concurrently inside InnoDB less than or equal to the limit given by this variable (InnoDB uses operating system threads to process user transactions). Once the number of threads reaches this limit, additional threads are placed into a wait state within a “First In, First Out” (FIFO) queue for execution(一旦数量达到了限制,额外的线程被放到了 FIFO队列里). Threads waiting for locks are not counted in the number of concurrently executing threads.
innodb_thread_sleep_delay:Defines how long InnoDB threads sleep before joining the InnoDB queue, in microseconds. The default value is 10000. A value of 0 disables sleep. You can set innodb_adaptive_max_sleep_delay to the highest value you would allow for innodb_thread_sleep_delay, and InnoDB automatically adjusts innodb_thread_sleep_delay up or down depending on current thread-scheduling activity. This dynamic adjustment helps the thread scheduling mechanism to work smoothly during times when the system is lightly loaded or when it is operating near full capacity.
innodb_concurrency_tickets:Determines the number of threads that can enter InnoDB concurrently. A thread is placed in a queue when it tries to enter InnoDB if the number of threads has already reached the concurrency limit. When a thread is permitted to enter InnoDB, it is given a number of “ 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. After that point, the thread again becomes subject to the concurrency check (and possible queuing) the next time it tries to enter InnoDB. The default value is 5000.
With a small innodb_concurrency_tickets value, small transactions that only need to process a few rows compete fairly with larger transactions that process many rows. The disadvantage of a small innodb_concurrency_tickets value is that large transactions must loop through the queue many times before they can complete, which extends the amount of time required to complete their task.(大事务在完成之前必须多次通过队列,这增大了他们完成事务的总时间)
With a large innodb_concurrency_tickets value, large transactions spend less time waiting for a position at the end of the queue (controlled by innodb_thread_concurrency) and more time retrieving rows. Large transactions also require fewer trips through the queue to complete their task. The disadvantage of a large innodb_concurrency_tickets value is that too many large transactions running at the same time can starve smaller transactions by making them wait a longer time before executing.
With a nonzero innodb_thread_concurrency value, you may need to adjust the innodb_concurrency_tickets value up or down to find the optimal balance between larger and smaller transactions. The SHOW ENGINE INNODB STATUS report shows the number of tickets remaining(剩下的tickets) for an executing transaction in its current pass through the queue. This data may also be obtained from the TRX_CONCURRENCY_TICKETS column of the Information Schema INNODB_TRX table.
mysql> show engine innodb status \G
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2023-09-18 10:51:17 0x7f1c3171f700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 19 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 494 srv_active, 0 srv_shutdown, 49571163 srv_idle
srv_master_thread log flush and writes: 49571657
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 152
OS WAIT ARRAY INFO: signal count 129
RW-shared spins 0, rounds 194, OS waits 84
RW-excl spins 0, rounds 142, OS waits 0
RW-sx spins 15, rounds 193, OS waits 1
Spin rounds per wait: 194.00 RW-shared, 142.00 RW-excl, 12.87 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 21627
Purge done for trx's n:o < 21625 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421234659759952, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421234659760864, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
515 OS file reads, 2656 OS file writes, 587 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 1 buffer(s)
Hash table size 34673, node heap has 1 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 13325540
Log flushed up to 13325540
Pages flushed up to 13325540
Last checkpoint at 13325531
0 pending log flushes, 0 pending chkp writes
399 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 239644
Buffer pool size 8191
Free buffers 7657
Database pages 532
Old database pages 211
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 472, created 60, written 2191
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 532, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=4105, Main thread ID=139759309551360, state: sleeping
Number of rows inserted 20397, updated 78, deleted 3, read 24652
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
1 row in set (0.00 sec)
mysql>
mysql>
mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G
*************************** 1. row ***************************
trx_id: 162612
trx_state: RUNNING
trx_query: insert into testti3 select * from testti3
trx_operation_state: NULL
trx_concurrency_tickets: 10
*************************** 2. row ***************************
trx_id: 422212176322720
trx_state: RUNNING
trx_query: insert into testui select * from testui
trx_operation_state: sleeping before entering InnoDB
trx_concurrency_tickets: 0
2 rows in set (0.32 sec)
从trx_operation_state中可以看到他们不断的在进行轮换的进入的innodb层次,同时我们还能看到
活跃事物trx_concurrency_tickets这个tickets不断的减少,而处于sleeping before entering InnoDB
的事物其trx_concurrency_tickets为0。