
mysql5.6问世之前,MySQL处理连接的方式是一个连接的每个线程的,即每一个数据库连接,MySQL服务器会创建一个独立的线程服务。在请求完成后,销毁线程。另一个连接请求,然后创建一个连接的破坏结束后,这种方法会导致频繁的创作和在高并发情况下的线程释放。当然,通过线程缓存,我们可以在未来使用线程缓存,避免频繁的创建和释放问题,但不能解决与连接数量激增的connections.one-connection-per-thread高数量的问题,导致需要创建相同的多服务线程,高并发线程意味着高内存消耗,更多的上下文切换(CPU缓存命中率)和资源的竞争,导致服务抖动。相对于每连接模式一个线程,一个线程对应一个连接。在线程池实现中,线程处理的最小单位是语句(语句)。线程可以处理多个连接的请求。通过这种方式,可以通过确保充分利用硬件资源(合理设置线程池的大小)来避免突然连接数量突然增加而导致的服务器抖动。
调度方法的实现
MySQL服务器也支持3个连接的管理方法,包括没有线程,每个连接一个线程,pool-threads.no-threads说处理由主线程连接,无需额外的线程被创建,这种方式主要用于调试;每个连接一个线程是最常用的前线程池的出现,为每个连接创建一个线程服务;池中的线程是讨论线程pool.mysql-server支持3连接管理模式通过一组函数指针。在特定的方式中,函数指针被设置为一个特定的回调函数。连接管理的thread_handling参数控制。
如果(thread_handling scheduler_one_thread_per_connection)
one_thread_per_connection_scheduler(thread_scheduler,
max_connections,
connection_count);
如果(thread_handling = scheduler_no_threads)
one_thread_scheduler(thread_scheduler);
其他的
pool_of_threads_scheduler(thread_scheduler,max_connections,connection_count);
连接管理过程
通过轮询监听mysql端口的连接请求
当接收到连接时,将调用接受接口来创建通信套接字。
初始化THD的实例,使用对象,等等
调度函数指针初始化基于thread_handling模式THD实例
调用调度程序的具体add_connection函数建立一个新的连接
下面的代码显示了scheduler_functions模板的实现和线程池模板回调函数,它是多个连接管理的核心。
结构scheduler_functions
{
单位max_threads;
connection_count uint *;
max_connections ulong *;
Bool(初始化)(void);
Bool(* init_new_connection_thread)(void);
void(* add_connection)(THD * THD);
void(* thd_wait_begin)(总谐波失真THD int *,wait_type);
void(* thd_wait_end)(THD * THD);
void(* post_kill_notification)(THD * THD);
Bool(* end_thread)(THD * THD,布尔cache_thread);
无效(*)(无效);
};
静态scheduler_functions tp_scheduler_functions =
{
0,
/ / max_threads
无效的,
无效的,
tp_init,
初始化
无效的,
/ / init_new_connection_thread
tp_add_connection,
/ / add_connection
tp_wait_begin,
/ / thd_wait_begin
tp_wait_end,
/ / thd_wait_end
tp_post_kill_notification,
/ / post_kill_notification
无效的,
/ / end_thread
tp_end
结束
};
线程池的相关参数
thread_handling:代表线程池模型。
thread_pool_size:代表一个线程池中的组数,这是一般设置为当前CPU内核的数量。理想情况下,一组是一个积极的工作者线程来充分利用CPU的目的。
thread_pool_stall_limit:用于定时器线程定期检查是否是停滞不前,和参数表示的检测间隔。
thread_pool_idle_timeout:,当一个工人被闲置一段时间,它会自动退出。它确保线程池中的工作线程在满足请求时保持相对较低的级别。
thread_pool_oversubscribe:这个参数是用来控制线程超频的CPU核心数。此参数设定值不包含监听线程数。
threadpool_high_prio_mode:代表优先级队列模式。
线程池的实现
上面描述了MySQL服务器如何管理连接,它着重于线程池的实现框架以及关键接口。如图1所示
每一个绿色广场站为一组,和组数由thread_pool_size参数确定。每一组包含一个优先队列和队列包含一个监听线程,多个线程,监听线程和工作线程可以动态转换,工作负荷是同时通过线程的数量决定的,由thread_pool_oversubscribe.in之外,整个线程池有一个计时器线程监控组预防组从停滞不前。
关键接口
1。tp_add_connection { }处理新的连接
1)创建连接对象
2)确定哪些组是根据thread_id % group_count分配给连接
3)将连接放入相应的组队列中
4)如果当前活动线程数为0,就会创建一个工作线程。
2。worker_main {工作线程}
1)调用get_event得到请求
2)如果有要求,handle_event调用的处理
3)否则,它表示队列和出口中没有请求。
三.get_event {获取请求}
1)获得连接请求
2)如果存在,立即返回并结束
3)如果此时没有组中的侦听器,则线程被转换为侦听器线程,阻塞等待。
4)如果有侦听器,则将线程添加到等待队列头中。
5)对线程休眠指定的时间(thread_pool_idle_timeout)
6)如果仍然没有唤醒,它是超时,那么线程结束,退出完成。
7)否则,它表示队列中有连接请求,跳转为1。
注意:在获取连接请求之前,它将决定当前线程是否超过了当前线程数。
thread_pool_oversubscribe + 1,如果超过,线程进入休眠状态。
4。handle_event { }处理请求
1)确定连接是否登录和验证,如果没有,则进行登录验证。
2)关联实例信息
3)获取网络包,分析请求
4)调用do_command功能回收要求
5)得到THD实例套接字句柄和确定处理是epoll侦听器列表
6)如果没有,叫epoll_ctl关联
7)结束
5侦听器{监听线程}
1)电话epoll_wait监测组相关的插座,阻塞等待
2)如果请求到达,从阻塞中恢复
3)根据连接的优先级别,确定是否放入公共队列或优先队列。
4)确定队列中的任务是否为空。
5)如果队列是空的,侦听器将转换为工作线程。
6)如果组中没有活动线程,请唤醒线程
注意:这里epoll_wait监视所有连接插座组然后听连接
请求推送到队列,而工作线程从队列获取任务,然后执行它。
6。timer_thread {监视线程}
1)如果没有监听线程,并没有io_event事件最近
2)创建唤醒或创建工作线程
3)如果组没有处理请求一段时间,并且队列中有请求,
4)指示组已经处于停滞状态,然后唤醒或创建线程。
5)检查是否有连接超时。
注意:定时器线程确定集团在失速状态通过调用check_stall检查是否客户端连接的时间通过调用timeout_check。
7。tp_wait_begin {进入等待状态的过程}
1)active_thread_count减去1,waiting_thread_count加1
2)设置连接>等待=真
3)如果活动线程的数量是0,任务队列不是空的,或者不监听线程,
4)唤醒或创建线程
8。tp_wait_end {结束等待状态的过程}
1)将连接的等待状态设置为false。
2)active_thread_count加1,waiting_thread_count减1
备注:uff1a
1)waiting_threads,列表中的线程是一个空闲线程,而不是等待线程。所谓空闲线程就是线程,它可以随时处理任务,等待线程无法处理任务,因为等待锁或等待IO操作。
2)对tp_wait_begin和tp_wait_end主要作用是由于报告的状态,即使active_thread_count和waiting_thread_count信息更新。
9。tp_init / tp_end
初始化和调用thread_group_init和thread_group_close分别销毁线程池
线程池和连接池
连接池通常是在客户端实现,这意味着应用程序(客户端)事先创建某个连接,并使用这些连接来服务所有的客户数据库请求。如果空闲连接数小于要求的分贝数在同一时间,请求需要排队等待免费的连接处理,连接池可以重用连接,避免频繁的创建和释放连接,从而减少请求的平均响应时间。当请求忙,排队请求能缓冲应用DB的影响。线程池现在在服务器,通过对数据库服务请求创建多个线程,每个线程一个服务线程连接连接,线程池服务声明中的最小单位,一个线程可以对应多个活动连接。通过线程池,我们可以控制服务器端的服务线程的数量在一定程度上减少了系统资源的竞争和线程上下文切换消耗,并避免高连接数造成的高并发问题,连接池和线程池相得益彰,通过连接池可以减少创建和释放SE的连接,提高请求的平均响应时间,以及应用控制好数据库连接数、连接数但不能控制簇的大小,从而导致大量的连接,线程池可以很好的处理连接数高,确保服务器端可以提供稳定的服务。如图2所示,每个Web服务器端保持3连接池。连接池的每个连接实际上不是独占DB服务器的工作人员,但可以与其他连接共享。假定DB服务器只有3个组,每个组只有一个工人,每个工人处理2个连接。
线程池优化
1。调度死锁的解决方案
线程池的线程的引入解决了高并发的问题,但也带来了隐患。A、B两交易假设,被分配在不同的组,交易已经开始,并持有锁,但因为一组很忙,执行语句,可以立即获得调度执行;B事务依赖事务释放锁资源,虽然交易可以B计划,但无法获得锁的资源,还需要等待,这就是所谓的调度死锁。因为组会同时处理多个连接,多个连接不对等。例如,一些连接发送请求的第一ST;某些关联交易已被打开,有些锁资源举行。为了减少锁定的资源竞争,后者显然之前应先前以达到释放锁资源尽快的目的。因此,在集团,一个优先队列可以被添加,放置请求已经被锁或连接发起的交易变成一个优先队列举行。工作线程首先从优先级队列获得任务执行。
2。大的查询处理
如果一个场景,连接到一个组进行大量的查询,然后工作线程组内将很快达到thread_pool_oversubscribe参数为后续的连接请求的设置将不能及时响应(没有连接处理,这段时间发生失速)组。通过前面的分析,它是已知的,定时器线程定期检查这种情况和创建新的工作线程来处理请求。如果长时间查询来自业务的要求,所有的组都将面临这样的问题在这个时候。在这个时候,主持人会挂住由于负荷太大。在这种情况下,线程池本身是无能为力的,因为源可能是烂SQL并发,或未能执行SQL的执行计划,也可以通过其他方法处理,如SQL水位高低的限制电流或SQL过滤。但有另一种情况下,垃圾场的任务。许多下游数据依赖于数据库的原始数据。通常,通过转储命令将数据拖到下游。这些转储任务通常需要很长的时间,所以它也可以被视为一个大的查询。如果在一组转储任务,并导致其他正常服务请求不能立即作出反应,这是难以忍受的,因为数据库不仅是因为压力,以导致请求响应线程池策略不及时,为了解决这个问题,我们将在组线包括在thread_pool_oversubscribe累计值来避免上述问题转储任务。
每个线程的一个连接
根据scheduler_functions模板,我们也可以列出一个连接每个线程模式的几个关键功能。
静态scheduler_functions con_per_functions =
{ / / max_connection + 1,max_threads
无效的,
无效的,
空初始化
init_new_connection_handler_thread / / init_new_connection_thread
create_thread_to_handle_connection / / add_connection
空 / / thd_wait_begin
空 / / thd_wait_end
空 / / post_kill_notification
one_thread_per_connection_end / / end_thread
空 / /结束
};
1.init_new_connection_handler_thread
这是一个简单的界面,主要通过调用pthread_detach,设置线程的分离状态,并自动释放所有资源的线程结束后。
2.create_thread_to_handle_connection
这个界面处理线程池的新接口,将从相应的thread_id % group_size组获得一个线程来处理,而一个连接一个线程将决定thread_cache可以使用,如果没有新的线程来处理。具体逻辑如下:
(1)。确定是否使用高速缓存中的线程数(比较blocked_pthread_count和wake_pthread尺寸)
(2)。如果有一个缓存线,添加到队列waiting_thd_list THD和唤醒一个线程等待cond_thread_cache
(3)。如果没有,创建一个新线程处理,线程的入口函数是do_handle_one_connection
(4)。电话add_global_thread加入THD阵列。
3.do_handle_one_connection
该接口由create_thread_to_handle_connection用来处理请求的主要实现接口。
(1)。循环调用do_command,从套接字中读取网络数据包,并解析执行;
(2)。当远程客户端发送一个关闭连接命令(如com_quit,com_shutdown),退出循环
(3)。电话close_connection关闭连接(THD ->断开());
(4)。打电话给one_thread_per_connection_end功能确认是否线程可以重复使用
(5)。根据返回结果,确定退出线程或继续循环执行命令。
4.one_thread_per_connection_end
确定是否一个线程的主要功能(thread_cache)可重复使用,与逻辑如下:
(1)。电话remove_global_thread删除线程THD实例
(2)。电话block_until_new_connection确定线程可以重复使用
(3)。判断缓存线超过阈值,如果没有,blocked_pthread_count + +;
(4)。阻塞等待条件变量cond_thread_cache
(5)。唤醒后,发现一个新的THD需要重复使用,消除了THD从waiting_thd_list,并初始化THD -> thread_stack线程的THD
(6)。电话add_global_thread加入THD阵列。
(7)。如果可以重用,返回false或返回真值。
线程池和epoll
在线程池的介绍,服务器层只有一个线程监控,负责监控MySQL端口和每个新连接的地方unixsocket请求将被分配一个单独的线程来处理,所以监测任务更容易,MySQL通过投票或选择模式来实现复用IO。在引入线程池,每个服务器都有一个监听线程监听所有连接插座组除监听线程在服务器级别。辅助线程不负责监测,但只处理请求。一个overscribe 1000线程池,每个线程需要听取1000插座的要求,和监听线程使用epoll的监测方法。
选择,poll和epoll都IO复用机制。IO复用可以监视多个FD(苗树付)通过一种机制,如插座,一旦FD的准备(读或准备),它可以通知程序读写相应。epoll与select、poll相比已经有了很大的提高,第一个epoll epoll_ctl功能通过注册,注册,复制所有的FD为内核,只是复制一份,不需要重复,每一次投票或选择呼叫,FD需要从用户空间拷贝到内核空间(epoll等epoll_wait其次,epoll);每个描述符指定一个回调函数,当设备准备就绪,等待醒来,将被添加到回调函数的就绪表描述符,不需要像选择,调查采用轮询模式E;最后选择的默认只支持1024 FD epoll,没有限制,具体数目可以参考猫/程序/系统/ FS / file-max.in使用epoll在线整个过程进程池的设置,我描述了如何创建和使用epoll,摧毁生命周期描述如何使用线程的epoll。
线程池的初始化,并创建epoll epoll文件描述符通过epoll_create功能,且功能thread_group_init;
当端口侦听器侦听请求时,将创建套接字,并在相应的组队列中创建THD和连接对象。
当工作线程获取连接对象时,如果没有登录,则执行登录验证。
如果插座没有注册到epoll的epoll_ctl注册、登记epoll_ctl_add,和连接的对象放在epoll_event结构
如果是一个老的连接请求,你还是需要打电话epoll_ctl登记和登记的方式是epoll_ctl_mod
The monitor thread within the group calls the epoll_wait to monitor the registered FD, and epoll is a synchronous IO, so it will wait
当请求到达时,得到的连接在epoll_event结构放在组中的队列
当线程池被摧毁,这thread_group_close叫做关闭epoll。
备注:uff1a
1、在epoll的FD注册,如果要求准备相应的事件,将事件的阵列,和FD的交易类型将被清空。因此,旧的连接请求,epoll_ctl(pollfd,epoll_ctl_mod,FD,EV)仍需登记。
线程池函数调用关系
(1)创建epoll
thread_group_init tp_init -> -> -> -> epoll_create io_poll_create tp_set_threadpool_size
(2)关闭epoll
tp_end -> thread_group_close -> thread_group_destroy ->关闭(pollfd)
(3)关联套接字描述符
start_io handle_event -> -> -> -> epoll_ctl io_poll_start_read io_poll_associate_fd
(4)处理连接请求
handle_event -> threadpool_process_request -> do_command -> -> -> mysql_execute_command mysql_parse dispatch_command
(5)当工作线程空闲时
worker_main -> get_event -> pthread_cond_timedwait
等待thread_pool_idle_timeout后,退出。
(6)监控实现
worker_main -> get_event -> -> -> epoll_wait io_poll_wait听众
(7)端口侦听线程
主要mysqld_main -> -> ->调查handle_connections_sockets
每个线程函数调用关系的一个连接
(1)等待请求的工作线程
handle_one_connection -> do_handle_one_connection -> do_command ->
net_read_packet my_net_read -> -> -> -> net_read_packet_header net_read_raw_loop
vio_read -> vio_socket_io_wait -> vio_io_wait ->调查
备注:有侦听线程来帮助线程侦听器请求,这与线程池的工作线程不同。当每个线程工作线程的一个连接空闲时,它将调用轮询阻塞并等待网络数据包的到来。
线程池的工作线程只需要集中在请求上,因此使用得更充分。
(2)端口侦听线程
与线程池相同(7)