December 14, 2013

[MySQL5.6] Percona Server 5.6.14的线程池浅析

  Percona的线程池 基本上是从Mariadb中引入,其实现思路也比较简单,就是在线程调度器那增加了一组新的回调函数。线程池可以有效改善在大并发下的性能; Thread pool的原理在Percona的这篇博客描述的很生动;其实就是限制同时运行的线程数,让大家不要一起挤进来,有序运行负载。线程池的目的不是为了提高性能,而是为了保持性能的稳定。 在使用线程池的场景下,就不是通常的一个连接一个线程(one-thread-per-connection),你可以创建几千甚至上万个Socket连接,MySQL只会创建有限个线程来 以下从不同的点来介绍下相关的代码,这里没有太多的深入,也没有性能测试数据,只是阅读时带着疑问做的一些笔记;   Q:如何控制同时运行的并发线程数   参数thread_pool_size的命令可能会让大家产生误解,它不是指的线程池的大小,而是线程组的大小。类似所有创建的线程都在某一个group里,group的编号从1~thread_pool_size,每个group里的线程数可以通过参数thread_pool_oversubscribe来控制(默认为3),如果把thread_pool_oversubscribe设置为1,那么thread_pool_size就被严格限制为能够同时活跃的最大线程数。 thread_pool_size默认值为CPU核心数,最大为128(MAX_THREAD_GROUPS),在启动时,就会把128个Group对应的结构体(all_groups)初始化好。每个group(编号小于等于thread_pool_size)会创建一个epoll对象;   Q:如何处理新连接   当客户端发起一个连接请求时,会被main线程捕捉到(handle_connections_sockets),然后调用tp_add_connection将新的连接加入到某个线程池的group中(根据thread_id%thread_pool_size),每个连接创建一个connection_t结构体对象(这个connection结构体里包含了连接的相关信息,例如是否登录,thd,是否正在等待,tickets等等), 创建好的connection被加入到其所属group的队列中(thread_group->queue),如果当前该group没有active的线程(thread_group->active_thread_count == 0)尝试去唤醒一个工作线程或者新建一个线程(wake_or_create_thread):a.首先尝试唤醒当前空闲的线程(空闲的线程对象在thread_group->waiting_threads中),如果有的话,则将其从waiting_threads中移除,并发送信号 ; b.否则表示现在该group内还没有线程, 需要创建一个新的worker线程(create_worker); 在创建worker线程时,一个比较有趣的代码段: if (tp_stats.num_worker_threads >= (int)threadpool_max_threads && thread_group->thread_count >= 2) { err= 1; max_threads_reached= true; goto end; } 实际上thread_pool_max_threads不是完全严格的限制总共的线程数,只有当当前线程数大于该max值,且当前group已有2个及以上worker线程时才拒绝新建线程 从上一步可以保证有一个活跃线程被唤醒来处理新连接的登录请求,worker线程的回调函数为worker_main,也就是处理socket请求的真正函数逻辑。   Q:如何处理新请求     worker线程调用worker_main,在一个循环内干两件事儿: #get_event 顾名思义,该函数的目的就是为了获取一个事件,大约有如下的流程: a.如果当前活跃线程数大于thread_pool_oversubscribe,并且该group的stall状态为false(何时设置?),暗示这时候该group的活跃线程数太多了(oversubscribed)。 b.如果oversubscribed为false,则取从队列中取connection_t对象(queue_get),先从高优先级队列(thread_group->high_prio_queue)取,如果没有的话,再从普通队列(thread_group->queue)取。如果存在的话,取得该对象返回 c.如果当前group里没有正在监听的线程(这时候没有任何请求),则把当前worker线程设置为监听线程,进入epoll监听socket请求 对于监听到的新请求,如果当前group里的没有event,,则由监听线程自己来处理监听到的第一个任务,剩下的任务放到队列中,否则把任务加入到队列中,由其他worker线程来处理。 加入队列的规则: 1.当该连接的ticket没用完(初始值为thread_pool_high_prio_tickets, Percona引入) […]