我整理了一些关于[数据库]的项目学习资料+视频(有讲解~~),与大家分享、学习:
线程池是MySQL 5.0的一个核心功能,对于服务器应用来说,无论是Web应用服务还是DB服务,高并发请求始终是一个无法回避的话题,当有大量并发请求时,必须不断创建和释放资源,导致资源利用率低,服务质量降低。线程池是一种常见的技术,它预先创建一定数量的线程,当一个请求到达时,线程池分配一个线程提供服务,请求结束后,该线程再去服务其他请求。这样就避免了线程和内存对象的频繁创建和释放,降低了服务器的并发,减少了上下文切换和资源竞争,提高了资源利用效率。所有服务的线程池本质上都是为了提高资源利用效率,实现方法大体都是一样的。本文主要讲解MySQL线程池的实现原理。
在.6之前,MySQL处理连接的方式是One--Per-way,即对于每一个数据库连接,MySQL--都会创建一个独立的线程服务,在请求完成后销毁该线程,如果有另外一个连接请求,则会再创建一个连接,在请求完成后销毁。这种方式在高并发下会导致线程的频繁创建和释放。当然通过--cache,我们可以缓存线程以供下次使用,避免频繁创建和释放的问题,但是无法解决连接数高的问题。采用One--Per-way,随着连接数的增加,需要创建同样数量的服务线程。高并发线程意味着内存消耗高,上下文切换更多(降低CPU缓存命中率)和资源竞争更多,导致服务抖动。相比于One--Per-way,一个线程对应一个连接。在--Pool实现方式中,线程处理的最小单位是(),一个线程可以处理多个连接的请求。这样,在保证硬件资源充分利用(合理设置线程池大小)的同时,还能避免连接数瞬时增加导致的服务器抖动。
调度方法实现
MySQL同时支持三种连接管理方式,包括No-、One--Per-和Pool-。No-表示使用主线程来处理连接,不会创建额外的线程,这种方式主要用于调试;One--Per-是线程池出现之前最常用的方式,为每个连接创建一个线程服务;Pool-是本文讨论的线程池方式。MySQL通过一组函数指针同时支持三种连接管理方式,对于具体的方法,将函数指针设置为具体的回调函数,通过参数来控制连接管理方式,代码如下:
登录并复制
if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)
one_thread_per_connection_scheduler(thread_scheduler,
&max_connections,
&connection_count);
else if (thread_handling == SCHEDULER_NO_THREADS)
one_thread_scheduler(thread_scheduler);
else
pool_of_threads_scheduler(thread_scheduler, &max_connections,&connection_count);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
连接管理进程
1.通过poll监听mysql端口的连接请求
2.收到连接后,调用接口创建通信
3.初始化thd实例、vio对象等。
4.根据模式设置,初始化thd实例的函数指针
5.调用特定函数创建新连接
下面的代码展示了模板和线程池对模板回调函数的实现,这是多连接管理的核心。
登录并复制
struct scheduler_functions
{
uint max_threads;uint *connection_count;
ulong *max_connections;
bool (*init)(void);
bool (*init_new_connection_thread)(void); void (*add_connection)(THD *thd);
void (*thd_wait_begin)(THD *thd, int wait_type);
void (*thd_wait_end)(THD *thd);
void (*post_kill_notification)(THD *thd);
bool (*end_thread)(THD *thd, bool cache_thread);
void (*end)(void);
};
static scheduler_functions tp_scheduler_functions=
{
0, // max_threads
NULL,
NULL,
tp_init, // init
NULL, // 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
NULL, // end_thread
tp_end // end};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
线程池相关参数
1.:表示线程池模型。
2.:表示线程池的组数,一般设置为当前CPU核心数,理想情况下一个组内有一个活跃的工作线程,这样才能充分利用CPU。
3.mit:定时器线程用来定期检测群组是否“停滞”,参数表示检测间隔。
4.eout:空闲一段时间后会自动退出,保证满足请求的同时,线程池的工作线程数保持在较低的水平。
5.cribe:该参数用于控制在CPU核心上“超频”的线程数,该参数设置值不包括线程数。
6._mode:表示优先队列的模式。
线程池实现
上面介绍了MySQL对连接的管理,本节主要介绍线程池的实现框架以及关键接口。
每个绿色框代表一个组,组数由参数决定。每个组包含一个优先级队列和一个普通队列,一个线程和若干个工作线程,线程可以动态切换。线程数由工作负载决定,也会受到cribe设置的影响。另外,整个线程池有一个timer线程监控组,防止组“停滞”。
主要接口
1.[处理新连接]
1)创建一个对象
2)根据%确定分配到哪个组。
3)放入相应组的队列中
4)如果当前活跃线程数为0,则创建一个工作线程
2. [工作线程]
1)调用 get 请求
2)如果有请求,则调用它进行处理
3)否则,表示队列中没有更多请求,进程退出。
3.【获取请求】
1)获取连接请求
2)如果存在,立即返回并结束
3)如果此时组中没有线程,则将该线程转换为线程并阻塞等待
4)如果存在,则将该线程添加到等待队列的头部
5)线程休眠指定时间(eout)
6)如果还是没有被唤醒,就是超时了,那么线程结束并退出
7)否则,表示队列中有一个连接请求到达,跳转到1
注意:获取连接请求前会判断当前活跃线程数是否超过
cribe+1,如果超过,则线程进入睡眠状态。
4.【处理请求】
1)判断连接是否需要登录验证,若不需要,则进行登录验证
2)关联thd实例信息
3)获取网络数据包并分析请求
4)调用函数loop处理请求
5)获取thd实例的句柄,并判断该句柄是否在epoll监听列表中
6)如果没有,请致电联系
7)结束
5.[聆听线索]
1)调用监视与组关联的套接字并阻塞并等待
2)如果有请求到来,从阻塞中恢复
3)根据连接优先级确定将连接放入普通队列还是优先队列
4)判断队列中任务是否为空
5)如果队列为空,则将其转换为线程
6)如果组中没有活跃线程,则唤醒一个线程
注意:这将监听组中所有已连接的套接字,然后监听连接
将请求推送到队列,线程从队列中获取任务,然后执行。
6.[监控线程]
1)如果没有线程且没有最近事件
2)创建唤醒或创建工作线程
3)如果该组最近没有处理任何请求,并且队列中有请求,则
4)表示该组已停滞,因此唤醒或创建线程
注意:计时器的目的是为了防止组处于停顿状态。
7.[进入等待状态流程]
1)活跃线程数减少1,等待线程数增加1
2)如果活跃线程数为0,且任务队列不为空,或者没有监听线程,则
3)唤醒或创建线程
注意:这里的线程是空闲线程,而不是等待线程。
空闲线程是可以随时处理任务的线程,而等待线程则是等待锁或者IO操作。
无法处理任务的线程。
8.【结束等待状态流程】
1)状态设置为false
2)活跃线程数加1,等待线程数减1
线程池和连接池
连接池通常实现在端,即应用程序(客户端)预先创建一定数量的连接,并使用这些连接来服务来自客户端的所有DB请求。如果某一时刻空闲连接数小于DB请求数,则需要将请求排队,等待空闲连接处理。连接池可以复用连接,避免频繁创建和释放连接,从而减少请求的平均响应时间,当请求繁忙时,可以通过请求排队来缓冲应用程序对DB的冲击。线程池实现在端,创建一定数量的线程来服务DB请求。相比于一个线程服务一个连接,线程池服务的最小单位是一条语句,即一个线程可以对应多个活跃连接。通过线程池可以将端上的服务线程数控制在一定范围内,减少对系统资源的竞争和线程上下文切换带来的消耗,同时也避免了连接数过多带来的高并发问题。连接池和线程池相得益彰。连接池可以减少连接的创建和释放,提高请求的平均响应时间,可以很好的控制应用的DB连接数,但是无法控制整个应用集群的连接规模,导致连接数很高。线程池可以很好的应对高连接数,保证终端能够提供稳定的服务。如图2所示,每个web端维护一个3个连接的连接池,连接池中每个连接其实并不是db端独享的,可能会和其他连接共享,这里假设db端只有3组,每组只有一个,每个组处理2个连接的请求。
图2(连接池与线程池框架图)
线程池优化
1. 优先级队列
由于一个group会同时处理多个连接,但多个连接并不均等。比如有些连接是第一次发送请求;而有些连接对应的事务已经打开,并持有一些锁资源。为了减少锁资源的争用,后者显然应该先于前者进行处理,以达到尽快释放锁资源的目的。因此在group中可以增加一个优先级队列,将已经持有锁的连接发起的请求放入优先级队列中,工作线程优先从优先级队列中获得任务执行。
2. 大型查询处理
假设一个场景,一个组里的所有连接都是大查询,组里的工作线程数很快就会达到cribe参数设置的值,对于后续的连接请求,响应将不及时(没有更多的连接需要处理),组就会停滞。从前面的分析我们知道timer线程会定期检查这种情况,并创建新的线程来处理请求。如果长查询来自业务请求,此时所有组都会面临这个问题,此时主机可能会因为负载过大而挂掉。这种情况下,线程池本身是无能为力的,因为来源可能是SQL并发性不好,或者SQL没有遵循正确的执行计划。可以使用其他方法,比如SQL高低水位流控或者SQL过滤等进行应急处理。但是还有一种情况,就是dump任务,很多下游依赖数据库的原始数据,通常会通过dump命令将数据拉到下游,这个dump任务通常需要很长时间,所以也可以算是一个大查询。如果dump任务集中在一个group里,导致其他正常的业务请求无法立即响应,这是不能容忍的,因为此时数据库并没有压力,只是因为使用了线程池策略,导致请求响应不及时。为了解决这个问题,我们将group里处理dump任务的线程不计入cribe累计值中,避免上述问题。
一些关于[数据库]的项目学习资料+视频(有讲解~~),你需要的可以看看:
扫一扫在手机端查看
-
Tags : mysql线程池被打满 mysql线程池优化 MySQL博客 51CTO博客
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。