5.5.3.4 线程池调整

本节提供有关设置线程池系统变量以获得最佳性能的准则,该准则使用诸如每秒事务之类的度量标准进行度量。

thread_pool_size是控制线程池性能的最重要参数。只能在服务器启动时设置。我们在测试线程池方面的经验 table 明:

  • 如果主存储引擎是InnoDB,则最佳thread_pool_size设置很可能在 16 到 36 之间,最常见的最佳值往往是从 24 到 36.我们还没有看到设置超过 36 的最佳情况。在某些特殊情况下,小于 16 的值是最佳的。

对于诸如 DBT2 和 Sysbench 之类的工作负载,InnoDB的最佳值通常通常约为 36.对于写入量非常大的工作负载,最佳设置有时可能会更低。

  • 如果主存储引擎为MyISAM,则thread_pool_size设置应相当低。最佳性能通常是 4 到 8 之间的值。较高的值往往会对性能产生轻微的负面影响,但影响不大。

另一个系统变量thread_pool_stall_limit对于处理阻塞的和长时间运行的语句很重要。如果所有阻止 MySQL 服务器的调用都报告给线程池,它将始终知道何时阻止执行线程。但是,这可能并不总是正确的。例如,在未使用线程池回调进行检测的代码中可能发生块。对于这种情况,线程池必须能够识别似乎被阻塞的线程。这是通过超时来完成的,可以使用thread_pool_stall_limit系统变量来调整其长度。此参数确保服务器不会被完全阻止。 thread_pool_stall_limit的上限为 6 秒,以防止服务器死锁的风险。

thread_pool_stall_limit还使线程池能够处理长时间运行的语句。如果允许长时间运行的语句阻止线程组,则分配给该组的所有其他连接都将被阻止,并且直到长时间运行的语句完成后才能开始执行。在最坏的情况下,这可能需要数小时甚至数天。

应该选择thread_pool_stall_limit的值,以使执行时间比其值长的语句被认为是停止的。停顿的语句会产生很多额外的开销,因为它们涉及额外的上下文切换,并且在某些情况下甚至会涉及额外的线程创建。另一方面,将thread_pool_stall_limit参数设置得太高则意味着长时间运行的语句将阻塞许多短时间运行的语句,其持续时间超过了必要。较短的 await 值使线程可以更快地启动。较短的值也可以更好地避免死锁情况。长 await 值对于包含长时间运行的语句的工作负载很有用,以避免在当前语句执行时启动太多新语句。

假设服务器执行一个工作负载,即使加载了服务器,其中 99.9%的语句在 100ms 内完成,而其余语句花费 100ms 到 2 个小时相当均匀地散布。在这种情况下,将thread_pool_stall_limit设置为 10(意味着 100ms)是有意义的。默认值 60ms 适用于主要执行非常简单的语句的服务器。

可以在运行时更改thread_pool_stall_limit参数,以使您达到适合服务器工作负载的平衡。假设已启用TP_THREAD_GROUP_STATStable,则可以使用以下查询来确定已暂停的已执行语句的比例:

SELECT SUM(STALLED_QUERIES_EXECUTED) / SUM(QUERIES_EXECUTED)
FROM INFORMATION_SCHEMA.TP_THREAD_GROUP_STATS;

此数字应尽可能低。要减少语句停顿的可能性,请增加值thread_pool_stall_limit

一条语句到达时,在实际开始执行之前最多可以延迟多少时间?假设满足以下条件:

在最坏的情况下,这 10 个高优先级语句 table 示 10 个长时间连续执行的事务。因此,在最坏的情况下,不会将任何语句移到高优先级队列,因为它始终已经包含 await 执行的语句。 10 秒钟后,新语句可以移动到高优先级队列中。但是,在可以移动它之前,必须先移动它之前的所有语句。这可能还要花费 2 秒的时间,因为每秒最多将 100 条语句移到高优先级队列。现在,当语句到达高优先级队列时,它前面可能会存在许多长时间运行的语句。在最坏的情况下,这些语句中的每一个都将停顿,并且从高优先级队列中检索下一条语句之前,每个语句将花费 1 秒。因此,在这种情况下,将需要 222 秒才能开始执行新语句。

此示例显示了应用程序的最坏情况。如何处理取决于应用程序。如果应用程序对响应时间有很高的要求,则它很可能应该将用户本身限制在更高的级别。否则,它可以使用线程池配置参数来设置某种最大 await 时间。