1. ProxySQL的线程

ProxySQL由多个模块组成 , 是一个多线程的daemon类程序。每个模块都有一个或多个线程去执行任务。

例如 , 以下是刚启动ProxySQL时的进程情况 , 一个main进程 , 一个主线程 , 23个线程。

[root@s1 ~]# pstree | grep roxy
        |-proxysql---proxysql---23*[{proxysql}]

下面是正常运行时使用的线程列表

1.1. Main thread

这其实是一个进程 , 该进程只负责引导、启动核心模块以及启动其它核心线程。

1.2. Admin thread

该线程负责以下几件事 :

  • 初始化并引导启动Admin接口。

  • 从磁盘数据库或配置文件中加载配置 , 为ProxySQL的运行提供环境。

  • 启动一个监听者 , 让其负责监听并接受到Admin接口的新连接 , 并为每个这样的连接创建一个新线程。

所以 , 每连接一次admin接口 , 就会新生成一个线程。每次退出admin接口时 , 减去一个线程。

1.3 MySQL workers

mysql-threads线程负责处理MySQL流量 , 包括所有来自客户端的连接以及所有到后端服务器节点的连接。也就是 : 用少量线程处理任意多数量的连接。

MySQL workers线程在相同的端口上进行监听。当新客户端发起连接请求 , 其中一个MySQL worker线程将成功接受该连接 , 并创建一个MySQL会话(session) : 客户端和会话绑定在该worker线程上 , 直到连接断开。换句话说 , 在断开连接之前 , 某客户端的所有连接总是被同一个worker线程处理。

默认情况下 , MySQL worker的线程数量为4。

mysql> select @@mysql-threads;
+-----------------+
| @@mysql-threads |
+-----------------+
| 4               |
+-----------------+

mysql-threads变量修改后 , 必须重启ProxySQL才能生效 , 这是少有的需要重启的变量之一(另一个是mysql-stacksize)。例如 , 修改为8mysql worker线程。

set mysql-threads=8;
save mysql variables to disk;
​
select * from runtime_global_variables 
 where variable_name='mysql-threads';
+---------------+----------------+
| variable_name | variable_value |
+---------------+----------------+
| mysql-threads | 4              |
+---------------+----------------+
​
service proxysql stop
service proxysql start
​
select * from runtime_global_variables 
 where variable_name='mysql-threads';
+---------------+----------------+
| variable_name | variable_value |
+---------------+----------------+
| mysql-threads | 8              |
+---------------+----------------+

1.4 MySQL auxiliary threads

这些线程其实就是空闲线程(idle threads)

如果proxysql使用--idle-threads选项启动 , 每个worker线程都会伴随启动一个auxiliary线程。每个worker线程以及它的auxiliary线程一起工作 : 第一个线程处理活动的连接 , 并将所有的空闲连接派遣到第二个线程上 , 但第二个线程只要等待到了发生在空闲连接上的一个事件(或超时) , 就会将连接还给第一个线程。

当活动的客户端连接数量远少于空闲客户端连接数量时 , 强烈建议使用"idle threads"。这使得ProxySQL可以处理几十万个连接(测试时是100W个连接)。

1.5 Monitor模块相关的线程

monitor模块有自己的线程管理系统 , 还有自己的线程池。正常情况下 , monitor模块有以下几个线程 :

  • 一个master线程 , 负责生成、协调其它monitor相关的线程。

  • 一个负责监控connect的线程。

  • 一个负责监控ping的线程。

  • 一个负责监控read_only的线程。

  • 一个负责监控replication lag的线程。

  • 一个线程池提供monitor worker线程 , 上面每个监控线程都是任务的生产者 , worker线程从任务队列消费一个任务并执行该任务。该线程池初始时默认为mysql thread的两倍。

线程池负责执行所有的检查任务 , 并通过以上各调度线程来监控调度情况。线程池会基于监控队列中待检查的数量多少而自动增长、收缩。基于检查的结果 , 会使用相同的线程对结果进行处理 , 例如避开一个节点、重新配置一个主机组。

1.6 Query Cache purge thread

该线程是需要时才生成的 , 它扮演的是垃圾收集器 , 回收查询缓存。通过垃圾收集器 , 可保证在客户端等待响应的过程中绝不会回收缓存。

1.7 其它线程

ProxySQL运行过程中 , 偶尔会派生临时线程 , 这些临时线程是为了向后端发送KILL语句 , 以便杀掉后端服务器上对查询长时间无响应的查询线程。

此外 , ilbmariadbclient库还会使用一些后台线程 , 这些线程是为了和后端MySQL server进行一些特定的异步交互任务。

还有一些模块 , 例如内置的Http server , 正处于实验阶段的clusterClickHouse ServerSQLite3 Server , 如果启用了这些功能 , 则会按需创建它们对应的线程。

2.ProxySQL的线程池

ProxySQL中 , 有两个地方使用了线程池 :

  • 快速建立和后端MySQL的连接 : ProxySQL为了尽快和后端MySQL建立新的TCP连接 , 使用了一个线程池来等待accept()返回新连接。

  • Monitor模块 : 为了尽快执行各监控线程生产的监控任务 , monitor模块提供了一个monitor worker线程池 , 可以快速从任务队列中消费任务。

需要注意的是 , 正常情况下 , MySQL worker线程是最繁忙、最消耗CPU资源的部分 , 但在一个极其繁忙的环境下 , monitor模块需要监控的连接数过多 , 消耗的CPU也是不可忽视的。

3.ProxySQL的连接池

ProxySQL同样有两个连接池 , 和线程池部分是对应的。

3.1 快速链接到后端MySQL节点

线程池是为了快速和后端建立新的TCP连接 , 而这里的连接池是为了快速和后端建立连接。

ProxySQL使用一个连接池来存放一定数量的"之前已经和某后端建立连接 , 但当前是空闲连接"的连接。当需要向这些连接对应的后端发送新的数据包时 , 可以快速地取回连接 , 因为这些连接早已经被打开。

当应用程序发送了一个MySQL请求给ProxySQL时 , ProxySQL首先解析要路由到哪个后端 , 如果连接池中已经有和该后端的连接 , 将重用该连接 , 否则将创建一个新的和后端的连接。

当处理完客户端的请求后 , 连接会还回主机组管理器(HostGroup Manager)。如果主机组管理器判断了该连接是可以被安全共享的 , 且连接池未满 , 则该连接会放进连接池。

放进连接池的连接都是空闲连接 , 正在使用的连接是不可能进入连接池的。ProxySQL会定期发送ping消息来维持空闲连接。如果某连接从上一次ping之后 , 如果还没有被使用 , 则该连接被定义为空闲连接。对于空闲连接ping的时间间隔由变量mysql-ping_interval_server_msec控制。

但是 , 不是所有的未使用的连接都会放进连接池。该变量用来控制某后端的空闲连接和最大总连接数的百分比。对于每个hostgroup/backend , 主机组管理器只会保持连接池中的最大连接数为mysql-free_connections_pct * mysql_servers.max_connections / 100。池中的每个空闲连接都通过间断性的ping来维持它的打开状态。

当一个连接放回连接池时 , 会计算这个连接之后还能处理多少个语句 , 当处理的语句数量达到该阈值后 , 将关闭该连接(v1.4.3之前)或者重置该连接(从v1.4.4开始)。

3.1.1 相关变量

  • mysql-ping_interval_server_msec : ProxySQL为了维持和后端的空闲连接 , 每隔一段时间发送一次ping , 该变量指定发起ping的时间间隔。默认值为10000毫秒(即10秒)。

  • mysql-ping_timeout_server : ProxySQL为了维持和后端的空闲连接 , 每隔一段时间发送一次ping。该变量指定ping得到回复的超时时间。默认值为200毫秒。

  • mysql-connection_max_age_ms : 当该变量设置的值大于0时(单位毫秒) , 如果某个空闲连接(当前没有任何会话使用)的空闲时长超过了这里设置的值 , 则这个连接会关闭。默认值为0 , 表示不会因为存活时间而关闭空闲连接。

3.2 Monitor模块的连接池

Monitor有它自己的连接池。当连接池中空闲连接的空闲时长达到了3 * mysql-monitor_ping_interval(毫秒)后 , 该空闲连接将自动被purge

变量mysql-monitor_ping_interval的默认值为1分钟(60000毫秒) , 所以 , monitor连接池中的空闲连接默认最长维持3分钟。


参考链接

MySQL中间件之ProxySQL(5) : 线程、线程池、连接池 - 骏马金龙 - 博客园


熊熊