TL&DR: Depending on the workload, the thread pool plugin can cause serious performance drops.

This post was motivated by two recent cases I’ve worked with. Two setups running in cluster mode: one in MariaDB+Galera and one with Percona XtraDB Cluster (Percona Server+Galera). In both cases, the clusters had the thread pool plugin enabled and were serving requests coming from ProxySQL. Pretty standard setups, nothing odd. In each situation, I’ve seen huge drops in the performance where the QPS were going to almost zero. 

When analyzing the workload, I found that the performance drop was caused by applications executing some very long queries like UPDATE affecting many rows, INSERT….SELECT without limit clause and other similar DMLs. 

After removing the thread pool plugin from the clusters, the performance was more steady, even when running these queries. We decided to run a few benchmarks to understand how the thread pool affects long-running transactions.

The method:

When thread pool should be used in a nutshell: CPU bound workload, i.e., a lot of short running queries with very high concurrency. 

For this reason, I’ve set up a server with the following specs: 

  • r6a.2xlarge ec2 VM (8 vCPU – 64GB of RAM)
  • 1TB gp2 drive (roughly 3k IOPS) 
  • Percona Server 8.0.34-26
  • my.cnf:

A basic configuration with nothing remarkable. This server is not intended to be a perfectly tuned one but just a baseline to compare the behavior under different concurrency configurations. 

We executed two rounds of benchmarks using sysbench: 

– created five tables with 10MM rows each, roughly 12GB of data (meaning the dataset fits completely in memory)
– executed the following script to capture metrics after warming up the buffer pool: 

–  the script was executed in three rounds, with three changes in configuration:

  • innodb_thread_concurrency=0 (default)
  • innodb_thread_concurrency=8
  • thread pool enabled 

– one important setting used in the script is the –reconnect flag that forces the connections to be closed and reopened at every x events. This is especially important when the thread pool is used because it is where it should outperform the one-thread-per-connection setting. 

The goal was to saturate the CPU while increasing concurrency and analyze how performance behaves with the different ways of handling high concurrent scenarios in  MySQL using different algorithms. 

Baseline numbers

Default configuration (innodb_thread_concurrency=0)

Thread Pool Plugin

innodb_thread_concurrency=8

thread pool enabled

Generally speaking, the maximum amount of QPS were around 70k, and the best performance under high concurrency was provided by innnodb_thread_concurrency=8. It’s slightly better than the other two configurations, and at the same time, I haven’t observed significant differences in CPU utilization. 

Now, let’s make the workload a little uglier. 

– I’ve executed an additional sysbench in the background while the main sysbench was running to introduce some extra noise of random queries that execute read/write OLTP load within short events:

And then manually introduced two queries to create and fill a table and update another non–related table, knowing these actions will be performance killers because they run full table scans: 

Metrics with long-running queries

Default configuration (innodb_thread_concurrency=0)

innodb_thread_concurrency=8

thread pool enabled

thread pool enabled

As expected, we’ve observed a drop in performance on average in the first two scenarios with the thread pool disabled. Adding the INSERT…SELECT and UPDATE queries, the performance degradation was around 15%. 

But with the thread pool enabled, the results are really scary. The performance impact is roughly 25% when the INSERT….SELECT and UPDATE queries kicked in. However, in a sort of random way we can see a huge performance drop with queries per second going to almost zero with no other load than the random sysbench while loop. In addition, while this issue was happening, the CPU utilization went down too. 

Some quick conclusions:

  • Personally, I’ve never been a big fan of the thread pool because I think it may work only under certain very specific circumstances (i.e., CPU-bound workload with super fast and optimized queries).
  • The thread pool dispatcher seems to be the bottleneck and is highly affected by the execution time of queries being processed, even if forcing a reconnection. So eventually, this can cause a huge performance drop even if the server looks idle.
  • When being CPU-bound, it seems best to configure MySQL by limiting the concurrency through innodb_thread_concurrency parameter.
  • Additionally, innodb_thread_concurrency is a dynamic parameter, so finding a sweet spot for configuration is easier than using the thread pool plugin that requires a restart.
  • For setting up a pool of threads to handle massive amounts of connections from applications I’d rather use 3rd party tools like ProxySQL or application native drivers like Hikari.

 

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments