|
1 | 1 | # Thread pool
|
2 | 2 |
|
3 |
| -Thread pooling can improve performance and scalability for MySQL databases. This technique reuses a fixed number of threads to handle multiple client connections and execute statements. It reduces the overhead of creating and destroying threads and avoids the contention and context switching that can occur when there are too many threads. |
| 3 | +## Introduction |
| 4 | + |
| 5 | +Thread pooling improves performance and scalability for MySQL-compatible databases by |
| 6 | +reusing a fixed number of pre-created threads to manage multiple client |
| 7 | +sessions. This design reduces resource overhead, lowers contention, and avoids |
| 8 | +context switching bottlenecks during high concurrency. |
| 9 | + |
| 10 | +The default MySQL method creates one thread per client connection. This approach |
| 11 | +works efficiently under moderate connection loads. As the number of active |
| 12 | +connections increases, especially beyond 20,000, system overhead becomes |
| 13 | +significant and throughput decreases. |
| 14 | + |
| 15 | +Percona Server for MySQL includes an integrated thread pool that replaces the |
| 16 | +default model. The thread pool manages connections more efficiently by queuing |
| 17 | +work and reusing threads, especially in OLTP environments with many short-lived |
| 18 | +queries. |
| 19 | + |
| 20 | +## Core concepts and usage examples |
| 21 | + |
| 22 | +### Fixed pool of threads |
| 23 | + |
| 24 | +The server initializes a defined number of threads at startup and reuses them |
| 25 | +to process incoming queries. |
| 26 | + |
| 27 | + |
| 28 | +```ini |
| 29 | +[mysqld] |
| 30 | +thread_handling = pool-of-threads |
| 31 | +``` |
| 32 | +This setting activates the thread pool model. |
| 33 | + |
| 34 | +### Thread groups |
| 35 | + |
| 36 | +The thread pool organizes threads into groups. Each group contains a listener thread, a set of worker threads, and handles a portion of the client connections. |
| 37 | + |
| 38 | + |
| 39 | +```ini |
| 40 | +[mysqld] |
| 41 | +thread_pool_size = 8 |
| 42 | +``` |
| 43 | + |
| 44 | +This configuration spreads work across eight thread groups. |
| 45 | + |
| 46 | +### Priority queues |
| 47 | + |
| 48 | +Each thread group maintains a high-priority queue and a low-priority queue. New queries are added to one of the two based on ticket availability and transaction state. The thread pool always checks the high-priority queue first. |
| 49 | + |
| 50 | +```mysql |
| 51 | +mysql> SHOW STATUS LIKE 'Threadpool%'; |
| 52 | +``` |
| 53 | + |
| 54 | +This command displays queue lengths and thread statistics. |
| 55 | + |
| 56 | +### High-priority ticketing |
| 57 | + |
| 58 | +Each connection begins with a set number of high-priority tickets. These tickets allow prioritization for a limited number of queries. One ticket is used every time the thread pool promotes a query to the high-priority queue. |
| 59 | + |
| 60 | +```ini |
| 61 | +[mysqld] |
| 62 | +thread_pool_high_prio_tickets = 5 |
| 63 | +``` |
| 64 | + |
| 65 | +This assigns five tickets to each new connection. |
| 66 | + |
| 67 | + |
| 68 | +### High-priority modes |
| 69 | + |
| 70 | +The `thread_pool_high_prio_mode` setting controls how the thread pool schedules queries using tickets. |
| 71 | + |
| 72 | +```mysql |
| 73 | +mysql> SET GLOBAL thread_pool_high_prio_mode = 'transactions'; |
| 74 | +``` |
| 75 | + |
| 76 | +This setting limits prioritization to queries within open transactions. |
| 77 | + |
| 78 | + |
| 79 | +### Adaptive scheduling |
| 80 | + |
| 81 | +The server assigns new connections to thread groups using a round-robin method. This approach balances workload evenly and prevents any group from becoming a bottleneck. |
| 82 | + |
| 83 | + |
| 84 | +Connection 1 goes to Group 1 |
| 85 | + |
| 86 | +Connection 2 goes to Group 2 |
| 87 | + |
| 88 | +Connection 3 goes to Group 3 |
| 89 | + |
| 90 | +Connection 4 goes to Group 4 |
| 91 | + |
| 92 | +Connection 5 returns to Group 1 |
| 93 | + |
| 94 | +<img src="../_static/thread-pool-diagram.png" alt="Thread pool diagram" width="250" /> |
| 95 | + |
| 96 | +### Configuration and monitoring |
| 97 | + |
| 98 | +Administrators can fine-tune thread pool behavior by adjusting dynamic and static variables. |
| 99 | + |
| 100 | +```ini |
| 101 | +[mysqld] |
| 102 | +thread_pool_stall_limit = 100 |
| 103 | +``` |
| 104 | + |
| 105 | +This value, in milliseconds, limits how long a task can stall before being redistributed. |
| 106 | + |
| 107 | +```mysql |
| 108 | +mysql> SHOW ENGINE THREAD_POOL STATUS; |
| 109 | +``` |
| 110 | + |
| 111 | +This command returns information about thread usage and group activity. |
| 112 | + |
4 | 113 |
|
5 |
| -If you have fewer than 20,000 connections, using the thread pool does not provide significant benefits. It’s better to keep thread pooling disabled and use the default method. |
6 | 114 |
|
7 |
| -The default method, called one-thread-per-connection, creates a new thread for each client that connects to the MySQL server. This thread manages all queries and responses for that connection until it’s closed. This approach works well for a moderate number of connections, but it can become inefficient as the number of connections increases. |
8 | 115 |
|
9 |
| -MySQL supports thread pooling through the thread pool plugin, which replaces the default one-thread-per-connection model. When a statement arrives, the thread group either begins executing it immediately or queues it for later execution in a round-robin fashion. The high-priority queue consists of several thread groups, each managing client connections. Each thread group has a listener thread that listens for incoming statements from the connections assigned to the group. The thread pool exposes several system variables that can be used to configure its operation, such as thread_pool_size, thread_pool_algorithm, thread_pool_stall_limit, and others. |
10 | 116 |
|
11 | 117 | The thread pool plugin consists of several thread groups, each of which manages a set of client connections. As connections are established, the thread pool assigns them to thread groups using the round-robin method. This method assigns threads fairly and efficiently. Here's how it works:
|
12 | 118 |
|
@@ -56,23 +162,73 @@ Starting with 8.0.14, Percona Server for MySQL uses the upstream implementation
|
56 | 162 |
|
57 | 163 | Implemented in 8.0.12-1: We ported the `Thread Pool` feature from Percona Server for MySQL 5.7.
|
58 | 164 |
|
59 |
| -## Priority connection scheduling |
| 165 | +## Thread pool priority queues |
| 166 | + |
| 167 | +The thread pool limits the number of concurrently running queries to improve |
| 168 | +performance under high load. Even when concurrency is constrained, the number |
| 169 | +of open transactions can remain high. The thread pool manages these connections |
| 170 | +using priority queues. |
| 171 | + |
| 172 | +When a new connection starts a transaction, the thread pool evaluates whether |
| 173 | +to place it in the high-priority queue. It does so if both of the following |
| 174 | +conditions are true: |
| 175 | + |
| 176 | +- The connection has an open transaction. |
| 177 | +- The connection has a non-zero number of high-priority tickets, as defined by |
| 178 | + the `thread_pool_high_prio_tickets` variable. |
60 | 179 |
|
61 |
| -The thread pool limits the number of concurrently running queries. The number of open transactions may remain high. Connections with already-started transactions are added to the end of the queue. A high number of open transactions has implications for the currently running queries. The [thread_pool_high_prio_tickets](#thread_pool_high_prio_tickets) variable controls the high-priority queue policy and assigns tickets to each new connection. |
| 180 | +Each time the thread pool schedules work, it checks the high-priority queue |
| 181 | +first. If that queue is empty, it pulls from the low-priority queue. This |
| 182 | +behavior ensures that transactions already in progress receive preferential |
| 183 | +treatment. |
62 | 184 |
|
63 |
| -The thread pool adds the connection to the high-priority queue and decrements the ticket if the connection has the following attributes: |
| 185 | +If `thread_pool_high_prio_tickets` is set to `0`, all connections go to the |
| 186 | +low-priority queue. If the value is greater than `0`, each new connection |
| 187 | +receives that number of high-priority tickets. The thread pool decrements a |
| 188 | +ticket each time it prioritizes a connection. |
64 | 189 |
|
65 |
| -* Has an open transaction |
| 190 | +## Configuring `thread_pool_high_prio_mode` |
66 | 191 |
|
67 |
| -* Has a non-zero number of high-priority tickets |
| 192 | +The `thread_pool_high_prio_mode` variable controls how the thread pool assigns |
| 193 | +priority to connections, and accepts the following values: |
68 | 194 |
|
69 |
| -Otherwise, the variable adds the connection to the low-priority queue with the initial value. |
| 195 | +* `transactions`: Prioritizes statements that are part of an open transaction |
| 196 | + (default). |
| 197 | + |
| 198 | +* `statements`: Prioritizes all statements from a connection with available |
| 199 | + high-priority tickets. |
| 200 | + |
| 201 | +* `none`: Disables high-priority scheduling. All connections go to the |
| 202 | + low-priority queue. |
| 203 | + |
70 | 204 |
|
71 |
| -Each time, the thread pool checks the high-priority queue for the next connection. When the high-priority queue is empty, the thread pool picks connections from the low-priority queue. The default behavior is to put events from already started transactions into the high-priority queue. |
72 | 205 |
|
73 |
| -If the value equals `0`, all connections are put into the low-priority queue. If the value exceeds zero, each connection could be put into a high-priority queue. |
| 206 | +### Static configuration |
| 207 | + |
| 208 | +To set the variable persistently, add the following to your `my.cnf` file: |
| 209 | + |
| 210 | +```ini |
| 211 | +[mysqld] |
| 212 | +thread_pool_high_prio_mode = transactions |
| 213 | +``` |
| 214 | + |
| 215 | +### Dynamic configuration |
| 216 | + |
| 217 | +You can also change this setting at runtime without restarting: |
| 218 | + |
| 219 | +``` |
| 220 | +mysql> SET GLOBAL thread_pool_high_prio_mode = 'transactions'; |
| 221 | +``` |
| 222 | + |
| 223 | +This change takes effect immediately for new connections only. Existing client sessions continue to operate under the previously active mode. To ensure all clients reflect the new behavior, either: |
| 224 | + |
| 225 | +* Restart the server, or |
| 226 | + |
| 227 | +* Have connected clients disconnect and reconnect manually. |
| 228 | + |
| 229 | +This dynamic adjustment enables fine-tuning thread scheduling behavior in production systems without downtime. |
| 230 | + |
74 | 231 |
|
75 |
| -The [thread_pool_high_prio_mode](#thread_pool_high_prio_mode) variable prioritizes all statements for a connection or assigns connections to the low-priority queue. To implement this new [thread_pool_high_prio_mode](#thread_pool_high_prio_mode) variable |
76 | 232 |
|
77 | 233 | ## Low-priority queue throttling
|
78 | 234 |
|
@@ -123,6 +279,28 @@ This variable can limit the time an idle thread should wait before exiting.
|
123 | 279 |
|
124 | 280 | ### `thread_pool_high_prio_mode`
|
125 | 281 |
|
| 282 | +--- |
| 283 | +### `thread_pool_high_prio_mode` |
| 284 | + |
| 285 | +| Option | Description | |
| 286 | +| -------------- | --------------------------------------------------------------------------- | |
| 287 | +| Command-line: | Yes | |
| 288 | +| Config file: | Yes | |
| 289 | +| Scope: | Global | |
| 290 | +| Dynamic: | Yes | |
| 291 | +| Data type: | Enumeration (`transactions`, `statements`, `none`) | |
| 292 | +| Default value: | `transactions` | |
| 293 | + |
| 294 | +Controls how the thread pool schedules high-priority work. When set to |
| 295 | +`transactions`, only statements within an open transaction are eligible for |
| 296 | +high-priority execution. The `statements` option prioritizes all statements |
| 297 | +from connections that still have unused high-priority tickets. Setting this |
| 298 | +variable to `none` disables high-priority queueing entirely. |
| 299 | + |
| 300 | +Changes take effect immediately for new connections. Existing sessions must |
| 301 | +reconnect to adopt the new behavior. |
| 302 | + |
| 303 | + |
126 | 304 | This variable provides more fine-grained control over high-priority scheduling globally or per connection.
|
127 | 305 |
|
128 | 306 | The following values are allowed:
|
@@ -248,3 +426,16 @@ This status variable shows the number of idle threads in the pool.
|
248 | 426 |
|
249 | 427 | This status variable shows the number of threads in the pool.
|
250 | 428 |
|
| 429 | +### Limitations and recommendations |
| 430 | + |
| 431 | +| Limitation | Description | Recommendation | |
| 432 | +|----------------------------------|-----------------------------------------------------------------------------|------------------------------------------------------------------------------| |
| 433 | +| Limited benefit at low loads | Thread pool offers marginal gains below ~512–1000 connections. | Use default threading for small workloads. Evaluate before enabling. | |
| 434 | +| No effect on existing sessions | Dynamic changes only affect new connections. | Prompt clients to reconnect or plan for a rolling restart. | |
| 435 | +| Long-running query contention | Extended queries block worker threads, reducing throughput. | Isolate long-running queries to separate servers or use query timeouts. | |
| 436 | +| Thread starvation risk | High-priority queues may crowd out low-priority connections. | Monitor queue activity; balance ticket settings and connection patterns. | |
| 437 | +| Complexity in tuning | Poor variable configuration can degrade performance. | Test settings in staging; tune iteratively based on observed behavior. | |
| 438 | +| Limited monitoring | Native tools offer minimal queue and saturation visibility. | Integrate with monitoring systems (e.g., PMM, Prometheus) for better insight. | |
| 439 | +| Static design | Not a plugin; cannot be toggled dynamically. | Decide at deployment; changes require a configuration update and restart. | |
| 440 | + |
| 441 | + |
0 commit comments