1 体系结构说明
Percona Server for MySQL 的线程池架构旨在优化高并发环境下的性能,通过减少线程创建和销毁的开销,以及提高资源利用率,来保持系统性能的稳定。
Thread Pool由一个Timer线程和多个Thread Group组成,而每个Thread Group又由两个队列、一个listener线程和多个worker线程构成。
1.1 基本组件
- 线程组 (Thread Group):线程池由多个线程组组成,每个线程组包含一组工作线程(worker threads)。线程组的数量通常配置为数据库实例的 CPU 核心数量,通过参数 thread_pool_size 设置。
- Timer 线程:除了工作线程外,线程池还包含一个 Timer 线程,用于处理定时任务或超时事件。Timer线程周期性检查group是否处于处于阻塞状态,当出现阻塞的时候,会通过唤醒线程或者新建线程来解决。
worker线程:worker线程是真正干活的线程。
1.2 监听与分配
- Listener 线程:每个线程组都有一个 Listener 线程,负责监听来自现有连接的新请求。当监听到新请求时,Listener 线程会从线程组的工作线程中分配一个线程来提供服务。
- 连接分配:新连接在到达时,会根据一定的策略(如线程ID对线程组数取模)被分配到某个线程组中。在线程组内,连接通过竞争方式获取工作线程来处理。
1.3 任务队列
高优先级队列和低优先级队列:线程池使用两个队列来存放待执行的 IO 任务。高优先级队列的任务会优先被处理,通常用于存放需要快速响应的任务。低优先级队列则用于存放其他任务。任务的优先级可以根据实际情况进行调整,例如非事务引擎或开启了 autocommit 的事务引擎的任务可能会被放入低优先级队列。
1.4 线程状态与管理
线程池会监控线程的状态,包括空闲、忙碌、死亡等。当线程空闲超过一定时间(由 thread_pool_stall_limit 参数控制)时,线程可能会被销毁以释放资源。同时,当新连接到达且没有空闲线程时,线程池会根据配置决定是否创建新线程。
2 运行机制
2.1 Thread Pool运作机制
Step1:请求连接到MySQL,根据threadid%thread_pool_size确定落在哪个group;
Step2:group中的listener线程监听到所在的group有新的请求以后,检查队列中是否有请求还未处理。如果没有,则自己转换为worker线程立即处理该请求,如果队列中还有未处理的请求,则将对应请求放到队列中,让其他的线程处理;
Step3:group中的thread线程检查队列的请求,如果队列中有请求,则进行处理,如果没有请求,则休眠,一直没有被唤醒,超过thread_pool_idle_timeout后就自动退出。线程结束。当然,获取请求之前会先检查group中的running线程数是否超过thread_pool_oversubscribe+1,如果超过也会休眠;
Step4:timer线程定期检查各个group是否有阻塞,如果有,就对wokrer线程进行唤醒或者创建一个新的worker线程。
2.2 Thread Pool的分配机制
线程池会根据参数thread_pool_size的大小分成若干的线程组(thread group),每个线程组(thread group)各自维护客户端发起的连接,当客户端发起连接到MySQL的时候,MySQL会跟进连接的线程id(thread_id)对thread_pool_size进行取模,从而落到对应的线程组(thread group)。
thread_pool_oversubscribe参数控制每个线程组(thread group)的最大并发线程数,每个线程组的最大并发线程数为thread_pool_oversubscribe+1个。若对应的线程组达到了最大的并发线程数,则对应的连接就需要等待。
3 参数说明
mysql> show variables like 'thread_%';
+-------------------------------+-----------------+
| Variable_name | Value |
+-------------------------------+-----------------+
| thread_cache_size | 58 |
| thread_handling | pool-of-threads |
| thread_pool_high_prio_mode | transactions |
| thread_pool_high_prio_tickets | 4294967295 |
| thread_pool_idle_timeout | 60 |
| thread_pool_max_threads | 100000 |
| thread_pool_oversubscribe | 32 |
| thread_pool_size | 96 |
| thread_pool_stall_limit | 10 |
| thread_stack | 262144 |
| thread_statistics | OFF |
+-------------------------------+-----------------+
11 rows in set (0.01 sec)
thread_handling:该参数是配置线程模型,默认情况是
one-thread-per-connection
,即不启用线程池;将该参数设置为pool-of-threads
即启用了线程池。thread_pool_size:该参数是设置线程池的线程组(thread group)的数量,默认为系统CPU的个数,充分利用CPU资源。
thread_pool_oversubscribe:该参数设置每个线程组(thread group)中的最大线程数,每个线程组(thread group)中的最大线程数为
thread_pool_oversubscribe+1
,注意listener线程不包含在内。thread_pool_high_prio_mode:高优先级队列的控制参数,有三个值(transactions/statements/none),默认是transactions,三个值的含义如下:
1) transactions:对于已经启动事务的语句放到高优先级队列中,不过还取决于后面的thread_pool_high_prio_tickets
参数。
2) statements:这个模式所有的语句都会放到高优先级队列中,不会使用到低优先级队列。
3) none:这个模式不使用高优先级队列。thread_pool_high_prio_tickets:该参数控制每个连接最多语序多少次被放入高优先级队列中,默认为4294967295,注意这个参数只有在thread_pool_high_prio_mode为transactions的时候才有效果。
thread_pool_idle_timeout:worker线程最大空闲时间,默认为60秒,超过限制后会退出。
thread_pool_max_threads:该参数用来限制线程池最大的线程数,超过该限制后将无法再创建更多的线程,默认为100000。 实际最多的线程数 =
thread_pool_size
*(thread_pool_oversubscribe+1)
thread_pool_stall_limit:该参数设置timer线程的检测线程组(thread group)是否异常的时间间隔,默认为500ms。
thread_cache_size:用于控制线程缓存的大小。当客户端连接关闭时,档设置了一个非零值,MySQL 会尝试缓存该线程而不是立即销毁它。对于后续的连接请求,MySQL 可以从缓存中快速获取一个已经存在的线程,而不是重新创建一个新的线程,从而减少线程创建和销毁的开销,这在高并发场景下尤为重要,可以显著提高性能。