Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


7.6.3.3 线程池操作

线程池由多个线程组组成,每个组管理多个客户端连接。随着连接的建立,线程池将其分配给线程组,以轮询方式分配。

线程池公开系统变量,可以用来配置其操作:

要配置线程组的数量,请使用thread_pool_size系统变量。默认的线程组数为16。关于设置该变量的指导,请参阅第7.6.3.4节,“线程池调整”

每个线程组中的最大线程数为4096(或4095在某些系统上,其中一个线程用于内部使用)。

线程池将连接和线程分开,因此连接和执行语句的线程之间没有固定的关系。这与默认的线程处理模型不同,该模型将一个线程与一个连接关联,使得给定的线程执行该连接中的所有语句。

默认情况下,线程池尝试确保每个组中最多只有一个线程执行,但有时允许更多线程执行,以获得最佳性能:

  • 每个线程组都有一个监听线程,监听分配给该组的连接中的 incoming 语句。当语句到达时,线程组将其立即执行或将其排队以便后续执行:

    • 立即执行发生在语句是唯一的语句,并且没有排队或当前执行的语句时。

      立即执行可以被配置thread_pool_transaction_delay延迟,具有对事务的 throttling 效果。更多信息,请参阅以下讨论中的该变量描述。

    • 排队发生在语句不能立即执行,因为当前排队或执行的语句。

  • thread_pool_transaction_delay变量指定了事务延迟的毫秒数。worker线程将睡眠指定的时间,然后执行新事务。

    事务延迟可以在并行事务影响其他操作的性能时使用,例如在并行事务影响索引创建或在线缓冲池重大小操作时,可以配置事务延迟以减少资源竞争。延迟对事务具有 throttling 效果。

    thread_pool_transaction_delay设置不影响来自特权连接(分配给Admin线程组)的查询。这些查询不受配置的事务延迟影响。

  • 如果立即执行发生,监听线程将执行它。如果语句快速完成,执行线程将返回到监听语句。如果语句长时间执行,线程池将认为语句被阻塞,并启动另一个线程作为监听线程(如果必要)。以确保没有线程组被阻塞的语句,线程池有一个背景线程定期监控线程组状态。

    使用监听线程执行可以立即开始的语句,无需创建额外线程,如果语句快速完成。这确保了在低并发线程数的情况下执行的最优性能。

    当线程池插件启动时,它创建一个线程组(监听线程),加上背景线程。需要时创建更多线程以执行语句。

  • 该系统变量thread_pool_stall_limit的值确定了前一项中的“快速完成”的含义。默认情况下,线程将在60ms后被认为是阻塞的,但可以设置到6秒的最大值。该参数可以配置,以便在服务器负载中找到一个平衡。短的等待值允许线程更快地启动。短的值也更适合避免死锁情况。长的等待值适用于包含长时间运行语句的工作负载,以避免在当前语句执行时启动太多新的语句。

  • 如果thread_pool_max_active_query_threads为0,系统将使用默认算法来确定每个组中的活动线程数。默认算法将考虑阻塞线程,并可能暂时允许更多的活动线程。如果thread_pool_max_active_query_threads大于0,它将对每个组中的活动线程数施加限制。

  • 线程池的目的是限制并发的短运行语句。在执行语句达到阻塞时间前,它将防止其他语句开始执行。如果语句执行超出阻塞时间,它将继续执行,但不再防止其他语句开始。这样,线程池尝试确保每个线程组中只有一个短运行语句,虽然可能有多个长运行语句。让长运行语句阻塞其他语句是不可取的,因为没有限制等待的可能。例如,在复制源服务器上,一条发送二进制日志事件到副本的线程将永远运行。

  • 语句如果遇到磁盘I/O操作或用户级锁(行锁或表锁),将被阻塞。阻塞将使线程组变得不可用,因此线程池将回调线程池,以确保线程池可以立即启动一个新的线程来执行另一个语句。當阻塞线程返回时,线程池将允许它立即重新启动。

  • 有两个队列,一個是高优先级队列,另一个是低优先级队列。事务的第一条语句将被发送到低优先级队列。事务的后续语句将被发送到高优先级队列,如果事务仍在执行(语句已经开始执行),否则将被发送到低优先级队列。队列分配可以受到启用thread_pool_high_priority_connection系统变量的影响,该变量将所有队列语句的会话发送到高优先级队列。

    对于非事务存储引擎或在autocommit启用时的事务存储引擎,语句被视为低优先级语句,因为每个语句都是一个事务。因此,在混合使用InnoDBMyISAM表的语句时,线程池将优先执行InnoDB的语句,而不是MyISAM的语句,除非autocommit启用。在autocommit启用时,所有语句都具有低优先级。

  • 当线程组选择一个排队语句执行时,它首先查看高优先级队列,然后是低优先级队列。如果找到语句,它将从队列中移除并开始执行。

  • 如果语句在低优先级队列中呆太久,线程池将移到高优先级队列。控制时间的系统变量是thread_pool_prio_kickup_timer。对于每个线程组,每秒最多可以移动100个语句从低优先级队列到高优先级队列。

  • 线程池重用最活跃的线程以获得更好的CPU缓存使用。这是一个小的调整,但对性能有很大的影响。

  • 当线程执行来自用户连接的语句时,性能架构 instrumentation 将线程活动记入用户连接。否则,性能架构将活动记入线程池。

以下是一些条件,线程组可能会启动多个线程执行语句的示例:

  • 一个线程开始执行语句,但运行足够长时间被认为是阻塞。线程组允许另一个线程开始执行另一个语句,即使第一个线程仍在执行。

  • 一个线程开始执行语句,然后变为阻塞并报告阻塞回线程池。线程组允许另一个线程开始执行另一个语句。

  • 一个线程开始执行语句,然后变为阻塞,但由于阻塞不在已instrumented的代码中,因此线程组认为线程仍在运行。如果阻塞持续到语句被认为是阻塞,线程组允许另一个线程开始执行另一个语句。

线程池旨在跨越增加的连接数 scales。它也旨在避免死锁,例如限制活动执行语句的数量。线程池不应阻止线程不报告回线程池的语句,以免阻塞线程池。以下是一些示例:

  • 长时间运行的语句。这些语句将导致只有少数语句使用资源,并且可能阻止其他语句访问服务器。

  • 二进制日志dump线程读取二进制日志并将其发送到复制服务器。这是一种长时间运行的语句”,该语句运行很长时间,但不应该阻止其他语句执行。

  • 由于MySQL Server或存储引擎未将阻塞的语句报告回线程池,因此语句被阻塞在行锁、表锁、睡眠或其他阻塞活动中。

在每种情况下,以防止死锁,语句将被移到阻塞队列中,以便线程组允许另一个语句开始执行。这样,当一个线程执行或长时间阻塞时,线程池将该线程移到阻塞队列中,并且在剩余语句执行期间,不会阻止其他语句执行。

线程池中的最大线程数是max_connectionsthread_pool_size的和。这可能会在所有连接都处于执行模式且每个组中创建一个额外线程以监听更多语句时发生。这不是一个非常常见的状态,但理论上是可能的。

thread_pool_max_transactions_limit定义的限制被达到时,新的连接将出现假死状态,直到一个或多个现有事务完成。同样,在尝试在现有连接上启动新事务时也会出现这种情况。如果现有连接阻塞或长时间运行,唯一的方式是使用特权连接访问服务器。

要建立特权连接,连接的用户必须拥有TP_CONNECTION_ADMIN特权。特权连接忽略thread_pool_max_transactions_limit定义的限制,可以连接到服务器以增加限制、删除限制或杀死正在运行的事务。TP_CONNECTION_ADMIN特权必须被授予明确地。它不被默认授予任何用户。

特权连接可以执行语句并启动事务,并且被分配到名为Admin的线程组中。

当查询performance_schema.tp_thread_group_stats表时,该表报告每个线程组的统计信息,Admin线程组的统计信息将在结果集的最后一行中。例如,如果SELECT * FROM performance_schema.tp_thread_group_stats\G返回17行(每行一个线程组),Admin线程组的统计信息将在第17行中报告。