线程池由多个线程组组成,每个组管理一组客户端连接。随着连接的建立,线程池将它们分配给线程组,以轮询方式。
线程池公开了系统变量,可以用来配置其操作:
-
thread_pool_algorithm
:用于调度的并发算法。 -
thread_pool_dedicated_listeners
:在每个线程组中专门用于监听来自连接的入站语句的监听器线程。 -
thread_pool_high_priority_connection
:如何为会话计划语句执行。 -
thread_pool_max_active_query_threads
:每组允许的活动线程数。 -
thread_pool_max_transactions_limit
:线程池插件允许的最大事务数。 -
thread_pool_max_unused_threads
:允许的休眠线程数。 -
thread_pool_prio_kickup_timer
:线程池将语句从低优先级队列移到高优先级队列之前的等待时间。 -
thread_pool_query_threads_per_group
:每个线程组中的查询线程数(默认为单个查询线程)。如果您遇到由于长时间事务导致的响应时间变慢,可以考虑增加该值。 -
thread_pool_size
:线程池中的线程组数。这是控制线程池性能的最重要参数。 -
thread_pool_stall_limit
:执行语句被认为是停滞的时间。 -
thread_pool_transaction_delay
:开始新事务前的延迟期。
要配置线程组的数量,请使用 thread_pool_size
系统变量。默认的组数为 16。有关设置该变量的指南,请参阅 第 7.6.3.4 节,“线程池调整”。
每组的最大线程数为 4096(或在某些系统上为 4095,其中一个线程用于内部使用)。
线程池将连接和线程分离,因此不存在固定的连接和执行语句的线程关系。这与默认的线程处理模型不同,该模型将一个线程与一个连接关联,使得该线程执行来自该连接的所有语句。
默认情况下,线程池尝试确保每个组中最多只有一个线程执行,但有时允许临时执行多个线程以获得最佳性能:
-
每个线程组都有一个监听器线程,用于监听来自组中连接的入站语句。当语句到达时,线程组要么立即执行它,要么将其排队以供后续执行:
-
如果语句是唯一的,并且没有语句排队或正在执行,那么将立即执行。
可以通过配置
thread_pool_transaction_delay
来延迟语句的执行,这将对事务产生节流效果。有关该变量的更多信息,请参阅下面的讨论。 -
如果语句不能立即执行,因为有语句排队或正在执行,那么将其排队。
-
-
变量
thread_pool_transaction_delay
指定了事务延迟时间(以毫秒为单位)。工作线程在执行新事务之前将休眠指定的时间。事务延迟可以在并行事务影响其他操作的性能的情况下使用,例如,如果并行事务影响索引创建或在线缓冲池调整操作,可以配置事务延迟以减少资源争用。延迟对事务具有节流效果。
设置
thread_pool_transaction_delay
不会影响特权连接(分配给Admin
线程组的连接)上的查询。这些查询不受配置的事务延迟的影响。 -
如果立即执行,则监听线程执行它。(这意味着暂时没有线程在组中监听。)如果语句快速完成,执行线程返回监听语句。否则,线程池认为语句被阻塞并启动另一个监听线程(如果必要,创建它)。为了确保没有线程组被阻塞语句阻塞,线程池有一个后台线程定期监控线程组状态。
通过使用监听线程执行可以立即开始的语句,无需创建额外的线程,如果语句快速完成。这确保了在低并发线程数的情况下执行最有效。
当线程池插件启动时,它创建一个组中的线程(监听线程),加上后台线程。根据需要,创建额外的线程来执行语句。
-
系统变量
thread_pool_stall_limit
的值确定了前一项中的“快速完成”的含义。默认情况下,线程被认为是阻塞的时间是 60ms,但可以设置为最长 6s。这参数是可配置的,以便您可以根据服务器工作负载找到合适的平衡。短等待值允许线程更快启动。短值也更适合避免死锁情况。长等待值对包含长时间运行语句的工作负载非常有用,以避免在当前语句执行时启动太多新语句。 -
如果
thread_pool_max_active_query_threads
为 0,则默认算法适用于确定每组的最大活动线程数。默认算法考虑了阻塞线程,并可能暂时允许更多活动线程。如果thread_pool_max_active_query_threads
大于 0,则它对每组的活动线程数施加限制。 -
线程池专注于限制短时间运行语句的并发数。在语句执行到阻塞时间之前,它阻止其他语句开始执行。如果语句执行超过阻塞时间,它被允许继续,但不再阻止其他语句开始执行。这样,线程池尝试确保每个线程组中从不超过一个短时间运行语句,尽管可能有多个长时间运行语句。长时间运行语句不应该阻止其他语句执行,因为可能需要无限等待。例如,在复制源服务器上,一个线程发送二进制日志事件到副本服务器,实际上运行永久。
-
如果语句遇到磁盘 I/O 操作或用户级锁(行锁或表锁),则语句将被阻塞。这将导致线程组变为空闲,因此有回调到线程池,以确保线程池可以立即在该组中启动新线程来执行另一个语句。当阻塞线程返回时,线程池允许它立即重新启动。
-
有两个队列,高优先级队列和低优先级队列。事务中的第一个语句进入低优先级队列。随后的语句如果事务正在进行(语句已经开始执行),则进入高优先级队列,否则进入低优先级队列。队列分配可以受到启用
thread_pool_high_priority_connection
系统变量的影响,该变量使会话中的所有队列语句进入高优先级队列。非事务存储引擎或事务存储引擎(如果
autocommit
启用)中的语句被视为低优先级语句,因为在这种情况下每个语句都是一个事务。因此,给定一个混合的InnoDB
和MyISAM
表语句,线程池将优先处理InnoDB
语句,除非autocommit
启用。在autocommit
启用时,所有语句都具有低优先级。 -
当线程组选择队列中的语句以执行时,它首先在高优先级队列中查找,然后在低优先级队列中查找。如果找到语句,它将从其队列中删除并开始执行。
-
如果语句在低优先级队列中停留太长时间,线程池将其移到高优先级队列。系统变量
thread_pool_prio_kickup_timer
的值控制移动前的时间。对于每个线程组,每 10ms(每秒 100 次)最多移动一个语句从低优先级队列到高优先级队列。 -
线程池重用最活跃的线程,以获得更好的 CPU 缓存利用率。这是一个小调整,但对性能有很大的影响。
-
当线程执行用户连接的语句时,性能模式仪表板将线程活动记入用户连接。否则,性能模式仪表板将活动记入线程池。
以下是线程组可能启动多个线程执行语句的条件:
-
一个线程开始执行语句,但运行时间足够长以被认为是停滞的。线程组允许另一个线程开始执行另一个语句,即使第一个线程仍在执行。
-
一个线程开始执行语句,然后被阻塞并将其报告回线程池。线程组允许另一个线程开始执行另一个语句。
-
一个线程开始执行语句,然后被阻塞,但没有报告回线程池,因为阻塞不发生在已instrumented的代码中。在这种情况下,线程似乎仍在运行。如果阻塞时间足够长以被认为是停滞的,线程组允许另一个线程开始执行另一个语句。
线程池旨在跨越越来越多的连接进行扩展,同时避免可能来自限制活动执行语句数量的死锁。重要的是,不报告回线程池的线程不应该阻止其他语句的执行,从而导致线程池死锁。以下是一些示例:
-
长时间运行的语句。这些语句将使用所有资源,只允许少数语句执行,并可能阻止所有其他语句访问服务器。
-
二进制日志转储线程,它从二进制日志中读取数据并将其发送到副本。这是一种长时间运行的“语句”,它运行了很长时间,不应该阻止其他语句的执行。
-
被行锁、表锁、睡眠或其他阻塞活动阻塞的语句,这些活动没有被 MySQL 服务器或存储引擎报告回线程池。
在每种情况下,为了避免死锁,当语句不快速完成时,它将被移到停滞类别中,以便线程组可以允许另一个语句开始执行。使用这种设计,当线程执行或阻塞很长时间时,线程池将其移到停滞类别中,并且在语句的剩余执行过程中,不会阻止其他语句的执行。
可能出现的最大线程数是 max_connections
和 thread_pool_size
之和。这可能发生在所有连接都处于执行模式的情况下,并且每个组创建了一个额外的线程来监听更多语句。这不是经常发生的情况,但理论上是可能的。
特权连接
当达到 thread_pool_max_transactions_limit
定义的限制时,新的连接似乎挂起,直到一个或多个现有事务完成。同样,当尝试在现有连接上启动新事务时也会发生这种情况。如果现有连接被阻塞或长时间运行,唯一访问服务器的方法是使用特权连接。
要建立特权连接,启动连接的用户必须拥有 TP_CONNECTION_ADMIN
权限。特权连接忽略 thread_pool_max_transactions_limit
定义的限制,允许连接到服务器以增加限制、删除限制或终止正在运行的事务。TP_CONNECTION_ADMIN
权限必须明确授予,不是默认授予任何用户的。
特权连接可以执行语句和启动事务,并被分配到一个名为 Admin
的线程组中。
查询性能模式.tp_thread_group_stats表时,该表报告每个线程组的统计信息,Admin
线程组统计信息将报告在结果集的最后一行。例如,如果SELECT * FROM performance_schema.tp_thread_group_stats\G
返回17行(每个线程组一行),则Admin
线程组统计信息将报告在第17行。