在之前的文章里,为大家介绍了MySQL的连接管理线程的工作方式,在这一篇里为大家介绍管理连接的第二种方式,线程池。
MySQL默认的连接控制方式采用的是每个连接使用一个线程执行客户端的请求。MySQL的线程池是包含在企业版里面的服务器插件。使用线程池的目的是为了改善大量并发连接所带来的性能下降。在大量并发连接的工作负载下,使用线程池可以解决无法利用CPU缓存、上下文切换开销过大以及资源争用等问题。
线程池功能由插件库文件、服务器系统变量及Performance Schema里面的检测点组成。
线程池是由一定数量的线程组(默认为16个通过thread_pool_size
进行配置)构成,每个线程组管理一组客户端连接,最大连接数为4096。连接创建之后会以轮询的方式分配给线程组。连接池打破了每个连接与线程一一对应的关系,这一点与MySQL默认的线程控制方式不同,默认方式将一个线程与一个连接相关联,以便给定的线程从其连接执行所有的语句。
默认情况下,线程池试图确保每个组中每次最多执行一个线程,但有时为了获得最佳性能,允许临时执行多个线程。每组里面有一个监听线程,负责监听分配给该组的连接。线程会选择立即执行或稍后执行连接里面的语句,如果语句是唯一接收到的,并且当前没有排队或正在执行的语句,该语句就会立即执行。其它情况则会选择稍后执行。当该语句被判断为立即执行时,监听线程负责执行该语句,如果能够快速完成执行,该线程会返回监听状态,如果执行语句时间过长产生停滞,线程组会开启一个新的监听线程。线程池插件使用一个后台线程监控线程组状态,以确保线程组不会因为停滞的语句阻塞线程组。
可以通过thread_pool_stall_limit 配置等待值时长,短等待值允许线程更快启动,也有助于避免死锁情况。长时间等待值对于长时间运行的工作负载非常有用,可以避免在当前语句执行时启动太多新语句。
通过thread_pool_max_active_query_threads设置运行的最大线程,如果该值不为0,则该数值为允许运行的最大线程数量,设置为0使用默认最大值。
线程池侧重于限制短时间运行语句的并发数量。在执行语句达到待值时长之前,它会阻止其他语句开始执行。如果语句执行超过了待值时长,允许其继续执行,但不再阻止其他语句启动。通过这种方式,线程池尝试确保每个线程组中永远不会有超过一个的短时间运行语句,但可能有多个长时间运行的语句。
如果遇到磁盘I/O操作或用户级锁(行锁或表锁),语句就会被阻塞,将导致线程组无法使用。线程池的回调功能,可以确保线程池立即启动该组中的新线程来执行另一条语句。当一个被阻塞的线程返回时,线程池允许它立即重新启动。
线程池包含两个队列,高优先级队列和低优先级队列。当前正在执行的语句及该事务后续关联的语句将进入高优先级队列,其它语句进入低优先级队列。
此外,线程池重用活跃的线程,以更好地利用CPU缓存。这是一个对性能有很大影响的调整。
理论上,可能出现的最大线程数是 max_connections和thread_pool_size的总和。当所有连接都处于执行模式,并且每个组都创建了一个额外的线程来监听,可能会发生这种情况。
总结一下,MySQL的线程池被设计为扩展连接、避免死锁,通过对线程进行分组、区分优先级、轮询调度,高效利用CPU缓存、减少上下文切换开销,提升MySQL服务器性能!