本节提供有关确定线程池性能最佳配置的指南,性能使用指标衡量,例如每秒事务数。
最重要的因素是线程池中的线程组数量,它可以在服务器启动时使用 --thread-pool-size
选项设置;这在运行时无法更改。此选项的推荐值取决于使用的主要存储引擎是 InnoDB
还是 MyISAM
如果主要存储引擎是
InnoDB
,则线程池大小的推荐值是主机上可用物理内核的数量,最多 512。如果主要存储引擎是
MyISAM
,则线程池大小应相当低。通常在 4 到 8 之间的值可获得最佳性能。更高的值往往会对性能产生轻微的负面影响,但影响不大。
线程池插件可以处理的并发事务数量的上限由 thread_pool_max_transactions_limit
的值决定。此系统变量的推荐初始设置是物理内核数量乘以 32。您可能需要根据给定的工作负载调整此值;此值的合理上限是预期最大并发连接数;Max_used_connections
状态变量的值可以作为确定此值的参考。一个好的方法是从将 thread_pool_max_transactions_limit
设置为此值开始,然后向下调整它,同时观察对吞吐量的影响。
线程组中允许的最大查询线程数量由 thread_pool_query_threads_per_group
的值决定,它可以在运行时调整。此值与线程池大小的乘积大约等于可用于处理查询的线程总数。获得最佳性能通常意味着在应用程序之间找到 thread_pool_query_threads_per_group
和线程池大小之间的适当平衡。较大的 thread_pool_query_threads_per_group
值降低了线程组中所有线程在工作负载包含长运行查询和短运行查询的情况下同时执行长运行查询并阻塞较短查询的可能性。您应该牢记,当使用较小的线程池大小值和较大的 thread_pool_query_threads_per_group
值时,每个线程组的连接轮询操作的开销会增加。因此,我们建议将 thread_pool_query_threads_per_group
的起始值设置为 2;将此变量设置为较低的值通常不会带来任何性能优势。
为了在正常情况下获得最佳性能,我们还建议您在高并发的情况下将 thread_pool_algorithm
设置为 1。
此外,thread_pool_stall_limit
系统变量的值决定了对阻塞和长运行语句的处理。如果将所有阻塞 MySQL 服务器的调用报告给线程池,它将始终知道何时执行线程被阻塞,但这可能并不总是正确的。例如,阻塞可能发生在没有使用线程池回调进行检测的代码中。对于这种情况,线程池必须能够识别出似乎被阻塞的线程。这是通过 thread_pool_stall_limit
的值确定的超时来完成的,它确保服务器不会完全阻塞。 thread_pool_stall_limit
的值表示 10 毫秒间隔的数量,因此 600
(最大值)表示 6 秒。
thread_pool_stall_limit
还使线程池能够处理长运行语句。如果允许长运行语句阻塞线程组,则分配给该组的所有其他连接将被阻塞,并且无法开始执行,直到长运行语句完成。在最坏的情况下,这可能需要数小时甚至数天。
应选择thread_pool_stall_limit
的值,使得执行时间超过该值的语句被视为卡住。卡住的语句会产生很多额外开销,因为它们涉及额外上下文切换,在某些情况下甚至会额外创建线程。另一方面,如果将thread_pool_stall_limit
参数设置过高,意味着长时间运行的语句会比必要时间更长时间地阻塞大量短时间运行的语句。较短的等待值允许线程更快地启动。较短的值也有助于避免死锁情况。较长的等待值对于包含长时间运行语句的工作负载很有用,可以避免在当前语句执行时启动太多新语句。
假设服务器执行一个工作负载,其中 99.9% 的语句在 100 毫秒内完成,即使服务器处于负载状态,剩余的语句也均匀分布在 100 毫秒到 2 小时之间。在这种情况下,将thread_pool_stall_limit
设置为 10 (10 × 10 毫秒 = 100 毫秒) 是有意义的。默认值为 6 (60 毫秒),适用于主要执行非常简单语句的服务器。
可以在运行时更改thread_pool_stall_limit
参数,以使您能够找到适合服务器工作负载的平衡点。假设tp_thread_group_stats
表已启用,可以使用以下查询来确定卡住的执行语句的比例
SELECT SUM(STALLED_QUERIES_EXECUTED) / SUM(QUERIES_EXECUTED)
FROM performance_schema.tp_thread_group_stats;
此数字应该尽可能低。为了降低语句卡住的可能性,请增加thread_pool_stall_limit
的值。
当一个语句到达时,它在实际开始执行之前可以被延迟的最长时间是多少?假设以下条件适用
低优先级队列中有 200 个语句排队。
高优先级队列中有 10 个语句排队。
thread_pool_prio_kickup_timer
设置为 10000 (10 秒)。thread_pool_stall_limit
设置为 100 (1 秒)。
在最坏的情况下,这 10 个高优先级语句代表 10 个继续执行很长时间的事务。因此,在最坏的情况下,没有任何语句可以移动到高优先级队列,因为它总是已经包含等待执行的语句。10 秒后,新语句有资格移动到高优先级队列。但是,在它可以移动之前,它前面的所有语句也必须移动。这可能需要另外 2 秒,因为每秒最多只能将 100 个语句移动到高优先级队列。现在当语句到达高优先级队列时,它前面可能会有很多长时间运行的语句。在最坏的情况下,这些语句中的每一个都会卡住,并且在检索到下一个语句之前,每个语句都需要 1 秒。因此,在这种情况下,新语句需要 222 秒才能开始执行。
此示例展示了应用程序的 worst case。如何处理它取决于应用程序。如果应用程序对响应时间有很高的要求,它最有可能自己以更高级别限制用户。否则,它可以使用线程池配置参数设置某种最大等待时间。