线程池由多个线程组组成,每个线程组管理一组客户端连接。建立连接后,线程池将以循环方式将它们分配给线程组。
线程组的数量可以使用thread_pool_size系统变量进行配置 。组的默认数目为16。有关设置此变量的准则,请参见第5.6.3.4节“线程池调整”。
每个组的最大线程数为4096(在内部使用一个线程的某些系统上为4095)。
线程池将连接和线程分开,因此连接与执行从那些连接接收的语句的线程之间没有固定的关系。这不同于默认的线程处理模型,该模型将一个线程与一个连接相关联,以使给定线程执行其连接中的所有语句。
默认情况下,线程池尝试确保在任何时间在每个组中最多执行一个线程,但有时允许更多线程临时执行以达到最佳性能。该算法以以下方式工作:
-
每个线程组都有一个侦听器线程,该线程侦听分配给该组的连接中的传入语句。当一条语句到达时,线程组要么立即开始执行它,要么将其排队等待以后执行:
- 如果该语句是唯一接收到的语句,并且没有语句排队或当前正在执行,则立即执行。
- 如果语句不能立即开始执行,则会发生排队。
-
如果立即执行,则执行由侦听器线程执行。(这意味着该组中暂时没有线程正在侦听。)如果该语句快速完成,则正在执行的线程将返回侦听语句。否则,线程池将认为该语句已停止,并启动另一个线程作为侦听器线程(如有必要,请创建该线程)。为确保没有任何线程组被停止的语句阻塞,线程池具有一个后台线程,该线程定期监视线程组状态。
通过使用侦听线程执行可以立即开始的语句,如果该语句快速完成,则无需创建其他线程。这样可以确保在并发线程数较少的情况下尽可能最有效的执行。
当线程池插件启动时,它将为每个组创建一个线程(侦听器线程),再加上后台线程。根据需要创建其他线程以执行语句。
-
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已启用)的语句被视为低优先级语句,因为在这种情况下,每个语句都是一个事务。因此,给定混合的forInnoDB和MyISAMtable 语句InnoDB,MyISAM除非autocommit启用了线程池, 否则 优先于for的线程池 。随着autocommit启用时,所有的语句将低优先级。 -
当线程组选择一个排队的语句来执行时,它首先在高优先级队列中查找,然后在低优先级队列中查找。如果找到一条语句,则将该语句从其队列中删除并开始执行。
-
如果一条语句在低优先级队列中停留的时间过长,则线程池将移至高优先级队列。
thread_pool_prio_kickup_timer系统变量的值 控制移动之前的时间。对于每个线程组,每10ms最多有一条语句或每秒钟最多100条语句将从低优先级队列移至高优先级队列。 -
线程池重用最活跃的线程,以更好地利用CPU缓存。这是一个很小的调整,会对性能产生重大影响。
-
当线程从用户连接执行语句时,性能架构检测会将线程活动记入用户连接。否则,性能架构会将活动计入线程池。
这是一个线程组可能有多个线程开始执行语句的条件示例:
- 一个线程开始执行一条语句,但是运行了足够长的时间才被认为已停止。即使第一个线程仍在执行,线程组也允许另一个线程开始执行另一个语句。
- 一个线程开始执行一条语句,然后被阻塞,并将其报告回线程池。线程组允许另一个线程开始执行另一个语句。
- 一个线程开始执行一条语句,被阻塞,但不会报告该语句已被阻塞,因为在使用线程池回调进行检测的代码中不会发生阻塞。在这种情况下,线程在线程组中似乎仍在运行。如果该块的持续时间足够长,以至于该语句被认为已停止,则该组将允许另一个线程开始执行另一个语句。
线程池被设计为可在越来越多的连接上进行扩展。它还旨在避免因限制正在执行的语句数而引起的死锁。重要的是,不向线程池报告的线程不会阻止其他语句的执行,从而导致线程池死锁。此类声明的示例如下:
- 长时间运行的语句。这些将导致仅少数语句使用所有资源,并且它们可能阻止所有其他语句访问服务器。
- 二进制日志转储线程读取二进制日志并将其发送到副本。这是一种长时间运行的 “ 语句 ”,可以长时间运行,并且不应阻止其他语句的执行。
- 对行锁,表锁,睡眠或其他任何未由MySQL Server或存储引擎报告回线程池的阻塞活动而阻塞的语句。
在每种情况下,为防止死锁,该语句在未快速完成时将其移至停顿类别,以便线程组可以允许另一个语句开始执行。通过这种设计,当线程执行或长时间阻塞时,线程池将线程移至停滞类别,并且在语句执行的其余部分,它不会阻止其他语句执行。
可以出现的最大线程数是max_connections和 的总和 thread_pool_size。在所有连接都处于执行模式并且为每个组创建一个额外的线程以侦听更多语句的情况下,可能会发生这种情况。这不一定是经常发生的状态,但理论上是可能的。




