我们已经准备好了,你呢?

2024我们与您携手共赢,为您的企业形象保驾护航!

我整理了一些关于[数据库]的项目学习资料+视频(有讲解~~),与大家分享、学习:

线程池是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对连接的管理,本节主要介绍线程池的实现框架以及关键接口。

mysql线程池被打满 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个连接的请求。

mysql线程池被打满 mysql线程池优化_线程池_02

图2(连接池与线程池框架图)

线程池优化

1. 优先级队列

由于一个group会同时处理多个连接,但多个连接并不均等。比如有些连接是第一次发送请求;而有些连接对应的事务已经打开,并持有一些锁资源。为了减少锁资源的争用,后者显然应该先于前者进行处理,以达到尽快释放锁资源的目的。因此在group中可以增加一个优先级队列,将已经持有锁的连接发起的请求放入优先级队列中,工作线程优先从优先级队列中获得任务执行。

2. 大型查询处理

假设一个场景,一个组里的所有连接都是大查询,组里的工作线程数很快就会达到cribe参数设置的值,对于后续的连接请求,响应将不及时(没有更多的连接需要处理),组就会停滞。从前面的分析我们知道timer线程会定期检查这种情况,并创建新的线程来处理请求。如果长查询来自业务请求,此时所有组都会面临这个问题,此时主机可能会因为负载过大而挂掉。这种情况下,线程池本身是无能为力的,因为来源可能是SQL并发性不好,或者SQL没有遵循正确的执行计划。可以使用其他方法,比如SQL高低水位流控或者SQL过滤等进行应急处理。但是还有一种情况,就是dump任务,很多下游依赖数据库的原始数据,通常会通过dump命令将数据拉到下游,这个dump任务通常需要很长时间,所以也可以算是一个大查询。如果dump任务集中在一个group里,导致其他正常的业务请求无法立即响应,这是不能容忍的,因为此时数据库并没有压力,只是因为使用了线程池策略,导致请求响应不及时。为了解决这个问题,我们将group里处理dump任务的线程不计入cribe累计值中,避免上述问题。

一些关于[数据库]的项目学习资料+视频(有讲解~~),你需要的可以看看:

二维码
扫一扫在手机端查看

本文链接:https://by928.com/6328.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线