首页 » 网站建设 » php线程pool技巧_MySQL数据库机能优化之thread pool 事理分析值得收藏

php线程pool技巧_MySQL数据库机能优化之thread pool 事理分析值得收藏

访客 2024-12-09 0

扫一扫用手机浏览

文章目录 [+]

mysqld_mainhandle_connections_socketscreate_new_threadcreate_thread_to_handle_connectionhandle_one_connection

线程建立后,处理要求的堆栈如下:

0 mysql_execute_command1 0x0000000000936f40 in mysql_parse2 0x0000000000920664 in dispatch_command3 0x000000000091e951 in do_command4 0x00000000008c2cd4 in do_handle_one_connection5 0x00000000008c2442 in handle_one_connection6 0x0000003562e07851 in start_thread () from /lib64/libpthread.so.07 0x0000003562ae767d in clone () from /lib64/libc.so.6二、优点及存在的问题

在连接数较小的情形下可以很快的相应客户真个要求,但当连接数非常大时会创建很多线程,这样会引起以下问题:

php线程pool技巧_MySQL数据库机能优化之thread pool 事理分析值得收藏

过多线程之间的切换会加重系统的负载,造成系统资源紧张且相应不及时;频繁的进行线程的创建及销毁以及线程间同时无序的竟争系统资源加重了系统的负载。

thread_pool正是为理解决以上问题而产生的;

php线程pool技巧_MySQL数据库机能优化之thread pool 事理分析值得收藏
(图片来自网络侵删)
三、thread_pool

thread_pool(线程池),是指mysql 创建多少事情线程来共同处理所有连接的用户要求,用户的要求的办法不再是 ‘one thread per connection’,而是多个线程共同吸收并处理多个连接的要求,在数据库的底层处理方面(mysql_execute_command),单线程的处理办法和线程池的处理办法是同等的。

四、thread_pool 的事情事理

启动 thread_pool 的mysql 会创建thread_pool_size 个thread group , 一个timer thread, 每个thread group 最多拥有thread_pool_oversubscribe个活动线程,一个listener线程,listener线程卖力监听分配到thread group中的连接,并将监听到的事宜放入到一个queue中,worker线程从queue中取出连接的事宜并实行详细的操作,实行的过程和one thread per connection 相同。
timer threaad 则是为了监听各个threadgroup的运行情形,并根据是否阴塞来创建新的worker线程。

1、thread_pool 建立连接

thread_pool 建立连接的堆栈如下:

mysqld_mainhandle_connections_socketscreate_new_threadtp_add_connectionqueue_put

2、worker 处理要求

thread group中的 worker 处理要求的堆栈如下:

0 mysql_execute_command1 0x0000000000936f40 in mysql_parse2 0x0000000000920664 in dispatch_command3 0x000000000091e951 in do_command4 0x0000000000a78533 in threadpool_process_request5 0x000000000066a10b in handle_event6 0x000000000066a436 in worker_main7 0x0000003562e07851 in start_thread ()8 0x0000003562ae767d in clone ()

个中worker_main函数是woker线程的主函数,调用mysql本身的do_command 进行解析及处理,和one_thread_per_connection 是一样的逻辑; thread_pool 自行掌握事情的线程个数,进而实现线程的管理。

3、thread_pool中线程的创建

listener线程将监听事宜放入mysql放入queue中时,如果创造当前thread group中的生动线程数active_thread_count为零,则创建新的worker 线程;正在实行的线程壅塞时,如果创造当前thread group中的生动线程数active_thread_count为零,则创建新的worker 线程;timer线程在检测时创造没有listener线程且自上次检测以来没有新的要求时会创建新的worker线程,个中检测的韶光受参数threadpool_stall_limit掌握;timer线程在检测时创造没有实行过新的要求且实行行列步队queue 不为空时会创建新的worker线程;

worker线程的伪码如下:

4、thread_pool中线程的销毁

当从行列步队queue中取出的connection为空时,则此线程销毁,取connection所等待的韶光受参数thread_pool_idle_timeout的掌握; 综上,thread_pool通过线程的创建及销毁来自动处理worker的线程个数,在负载较高时,创建的线程数目较高,负载较低时,会销毁多余的worker线程,从而降落连接个数带来的影响的同时,提升稳定性及性能。
同时,threadpool中引入了Timer 线程,紧张做两个事情。

定期检讨每个thread_group是否壅塞,如果壅塞,则进行唤醒或创建线程的事情;检讨每个thread_group中的连接是否超时,如果超时则关掉连接并开释相应的资源;五、线程池实现

下图是线程池的实现框架,以及关键接口

每一个绿色的方框代表一个group,group数目由thread_pool_size参数决定。
每个group包含一个优先行列步队和普通行列步队,包含一个listener线程和多少个事情线程,listener线程和worker线程可以动态转换,worker线程数目由事情负载决定,同时受到thread_pool_oversubscribe设置影响。
此外,全体线程池有一个timer线程监控group,防止group“结束”。

关键接口

1. tp_add_connection[处理新连接]

1) 创建一个connection工具2) 根据thread_id%group_count确定connection分配到哪个group3) 将connection放进对应group的行列步队4) 如果当前生动线程数为0,则创建一个事情线程

2. worker_main[事情线程]

1) 调用get_event获取要求2) 如果存在要求,则调用handle_event进行处理3) 否则,表示行列步队中已经没有要求,退出结束。

3. get_event[获取要求]

1) 获取一个连接要求2) 如果存在,则立即返回,结束3) 若此时group内没有listener,则线程转换为listener线程,壅塞等待4) 若存在listener,则将线程加入等待行列步队头部5) 线程休眠指定的韶光(thread_pool_idle_timeout)6) 如果依然没有被唤醒,是超时,则线程结束,结束退出7) 否则,表示行列步队里有连接要求到来,跳转1

备注:获取连接要求前,会判断当前的生动线程数是否超过了thread_pool_oversubscribe+1,若超过了,则将线程进入休眠状态。

4. handle_event[处理要求]

1) 判断连接是否进行登录验证,若没有,则进行登录验证2) 关联thd实例信息3) 获取网络数据包,剖析要求4) 调用do_command函数循环处理要求5) 获取thd实例的套接字句柄,判断句柄是否在epoll的监听列表中6) 若没有,调用epoll_ctl进行关联7) 结束

5.listener[监听线程]

1) 调用epoll_wait进行对group关联的套接字监听,壅塞等待2) 若要求到来,从壅塞中规复3) 根据连接的优先级别,确定是放入普通行列步队还是优先行列步队4) 判断行列步队中任务是否为空5) 若行列步队为空,则listener转换为worker线程6) 若group内没有生动线程,则唤醒一个线程

备注:这里epoll_wait监听group内所有连接的套接字,然后将监听到的连接

要求push到行列步队,worker线程从行列步队中获取任务,然后实行。

6. timer_thread[监控线程]

1) 若没有listener线程,并且最近没有io_event事宜2) 则创建一个唤醒或创建一个事情线程3) 若group最近一段韶光没有处理要求,并且行列步队里面有要求,则4) 表示group已经stall,则唤醒或创建线程5)检讨是否有连接超时

备注:timer线程通过调用check_stall判断group是否处于stall状态,通过调用timeout_check检讨客户端连接是否超时。

7.tp_wait_begin[进入等待状态流程]

1) active_thread_count减1,waiting_thread_count加12)设置connection->waiting= true3) 若生动线程数为0,并且任务行列步队不为空,或者没有监听线程,则4) 唤醒或创建一个线程

8.tp_wait_end[结束等待状态流程]

1) 设置connection的waiting状态为false2) active_thread_count加1,waiting_thread_count减1

备注:

waiting_threads这个list里面的线程是空闲线程,并非等待线程,所谓空闲线程是随时可以处理任务的线程,而等待线程则是由于等待锁,或等待io操作等无法处理任务的线程。
tp_wait_begin和tp_wait_end的紧张浸染是由于申报请示状态,纵然更新active_thread_count和waiting_thread_count的信息。

9. tp_init/tp_end

分别调用thread_group_init和thread_group_close来初始化和销毁线程池

六、线程池与连接池

连接池常日实现在Client端,是指运用(客户端)创建预先创建一定的连接,利用这些连接做事于客户端所有的DB要求。
如果某一个时候,空闲的连接数小于DB的要求数,则须要将要求排队,等待空闲连接处理。
通过连接池可以复用连接,避免连接的频繁创建和开释,从而减少要求的均匀相应韶光,并且在要求繁忙时,通过要求排队,可以缓冲运用对DB的冲击。

线程池实现在server端,通过创建一定数量的线程做事DB要求,相对付one-conection-per-thread的一个线程做事一个连接的办法,线程池做事的最小单位是语句,即一个线程可以对应多个生动的连接。
通过线程池,可以将server真个做事线程数掌握在一定的范围,减少了系统资源的竞争和线程高下文切换带来的花费,同时也避免涌现高连接数导致的高并发问题。
连接池和线程池相辅相成,通过连接池可以减少连接的创建和开释,提高要求的均匀相应韶光,并能很好地掌握一个运用的DB连接数,但无法掌握全体运用集群的连接数规模,从而导致高连接数,通过线程池则可以很好地应对高连接数,担保server端能供应稳定的做事。

如图所示,每个web-server端掩护了3个连接的连接池,对付连接池的每个连接实际不是独占db-server的一个worker,而是可能与其他连接共享。

这里假设db-server只有3个group,每个group只有一个worker,每个worker处理了2个连接的要求。

七、threadpool干系参数

1、thread_pool_high_prio_mode

有三个取值:transactions / statements / none

transactions(default): 利用优先行列步队和普通行列步队,对付事务已经开启的statement,放到优先行列步队中,否则放到普通行列步队中statements:只利用优先行列步队none: 只是用普通行列步队,实质上和statements相同,都是只是用一个行列步队

2、thread_pool_high_prio_tickets

取值0~4294967295,当开启了优先行列步队模式后(thread_pool_high_prio_mode=transactions),每个连接最多许可thread_pool_high_prio_tickets次被放到优先行列步队中,之后放到普通行列步队中,默认为4294967295

3、thread_pool_idle_timeout

worker线程最大空闲韶光,单位为秒,超过限定后会退出,默认60

4、thread_pool_max_threads

threadpool中最大线程数目,所有group中worker线程总数超过该限定后不能连续创建更多线程,默认100000

5、thread_pool_oversubscribe

一个group中线程数过载限定,当一个group中线程数超过次限定后,连续创建worker线程会被延迟,默认3

6、thread_pool_size

threadpool中group数量,默认为cpu核心数,server启动时自动打算

7、thread_pool_stall_limit

timer线程检测间隔,单位为毫秒,默认500

八、threadpool优化

1.调度去世锁办理

引入线程池办理了多线程高并发的问题,但也带来一个隐患。
假设,A,B两个事务被分配到不同的group中实行,A事务已经开始,并且持有锁,但由于A所在的group比较繁忙,导致A实行一条语句后,不能立即得到调度实行;而B事务依赖A事务开释锁资源,虽然B事务可以被调度起来,但由于无法得到锁资源,导致仍旧须要等待,这便是所谓的调度去世锁。
由于一个group会同时处理多个连接,但多个连接不是对等的。
比如,有的连接是第一次发送要求;而有的连接对应的事务已经开启,并且持有了部分锁资源。
为了减少锁资源争用,后者显然该当比前者优先处理,以达到尽早开释锁资源的目的。

因此mysql数据库在group里面添加一个优先级行列步队,将已经持有锁的连接,或者已经开启的事务的连接发起的要求放入优先行列步队,事情线程首先从优先行列步队获取任务实行。

2.大查询处理

假设一种场景,某个group里面的连接都是大查询,那么group里面的事情线程数很快就会达到thread_pool_oversubscribe参数设置值,对付后续的连接要求,则会相应不及时(没有更多的连接来处理),这时候group就发生了stall。
通过前面剖析知道,timer线程会定期检讨这种情形,并创建一个新的worker线程来处理要求。
如果长查询来源于业务要求,则此时所有group都面临这种问题,此时主机可能会由于负载过大,导致hang住的情形。
这种情形线程池本身无能为力,由于源头可能是烂SQL并发,或者SQL没有走对实行操持导致,通过其他方法,比如SQL高低水位限流或者SQL过滤手段可以应急处理。
但是,还有其余一种情形,便是dump任务。
很多下贱依赖于数据库的原始数据,常日通过dump命令将数据拉到下贱,而这种dump任务常日都是耗时比较长,以是也可以认为是大查询。
如果dump任务集中在一个group内,并导致其他正常业务要求无法立即相应,这个是不能容忍的,由于此时数据库并没有压力,只是由于采取了线程池策略,才导致了要求相应不及时,为理解决这个问题,mysql数据库将group中处理dump任务的线程不计入thread_pool_oversubscribe累计值,避免上述问题。

后面会分享更多devops和DBA方面的内容,感兴趣的朋友可以关注下~

标签:

相关文章

聪明党建源码php技巧_聪慧党建源码

聪慧建源码:借力科技,推动建当代化段落1:弁言聪慧建源码,作为一项基于科技的创新举措,对付推动建当代化具有主要意义。源码的开拓和运...

网站建设 2024-12-11 阅读0 评论0