mysql线程池的实现原理浅析

今天抽空主要看了一下mysql线程池(cached threads)的实现原理,总体并不那么复杂,也学到了一些设计原理,值得记录一下。为了简化代码,让思路更清晰,我删去了不少错误处理,线程同步锁的代码,mysql中大量使用全局变量,这些都需要锁了控制访问。

先大致说一下几个关键的东西:

1、List结构:这个看名字就知道,是一个list,可以理解为队列,这个数据结构是用来放thd的,就是线程数据的,这里不去深究list如何实现了(暂时)。

2、threads:一个thd的list,这些thd都是活跃线程(wake thread)。

3、cached_threads:也是一个thd的list,但并不活跃,实际上是一个等待thd,即如果当前有数据在里面,且有活跃线程结束,那么这个走进结束流程的线程会取出thd,然后运行这个thd,这样就不用新起线程了。

首先看看创建线程的代码:

void create_new_thread(thd){
    // 检查连接数是否过大
    mysql_mutext_lock(&lock_connection_count);
    if (connetion_count >= max_connections + 1 || abort_loop){
        mysql_mutext_unlock(&lock_connection_count);
        delete thd; // will close the connection
        return;
    }
    ++connection_count;

    if (connection_count > max_used_connections){
        max_used_connections = connection_count;
    }
    mysql_mutext_unlock(&lock_connection_count);

    mysql_mutex_lock(&lock_thread_count);

    // 我在想如果连接很多很多,溢出了怎么办??
    thd->thread_id = thd->variables.pseudo_thread_id = thread_id++;
    thread_count++;

    // add_connection当然是个函数指针,thread_scheduler是一个放了这些函数指针的结构体
    // 为了跨平台,却没有使用C++的多态,而是用了函数指针实现了多态
    thread_scheduler->add_connection(thd);
}
// 这个就是add_connection的实现
void create_thread_to_handle_connection(thd){
    // 注意这个判断,cache_thread_count大于wake_thread的时候才会进来,
    // 当线程结束的时候,线程cachd_thread_count++
    // 当发现有空闲,先wake_thread_count++,cachd_thread_count--
    // 成功启动线程后,wake_thread_count--,这时wake_thread_count和cachd_thread_count又归于平衡
    // 简单来说,cache_thread_count是用来记录可用线程数量的,而wake_thread是用来记录就绪线程数量的
    // 多数时候,wake_thread_count基本是0,而cache_thread_count则大于等于0
    if (cache_thread_count > wake_thread){
        thread_cache.push_back(thd);
        wake_thread++;
        mysql_cond_signal(&cond_thread_cache);
    }
    else {
        // 如果没有cached thread则直接创建新的线程
        thread_created++;
        // threads这个list放着所有正在运行时的thd
        threads.append(thd);
        thd->prior_thr_create_utime = thd->start_utime = my_micro_time();
        // 真正创建一个线程出来
        if (error = mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib
                    handle_one_connection)) {
            // do some clear up
            // ignore the lock operations
            // 创建失败清理一下
            thread_count--;
            connection_count--;
            delete thd;
        }
    }
}

那么什么时候会进入if (connetion_count >= max_connections + 1 || abort_loop)这个if语句当中呢?就是有线程结束,并等待新的thd来的时候,看看线程结束的时候做了些什么:

// 线程结束时的回调
bool one_thread_per_connection_end(thd, bool put_in_cache){
    unlink_thd(thd); // delete thd
    if (put_in_cache) {
        // 关键函数cache_thread
        put_in_cache = cache_thread();
    }
    // 如果已经获得新thd,就不结束了
    if (put_in_cache){
        return 0;
    }

    // 否则结束线程
    my_thread_end();
    mysql_cond_broadcast(&cond_thread_count);

    pthread_exit(0);
    return 0;
}

其中的cache_thread是关键函数:

// 这个函数就是cache thread的关键,他的思路就是线程结束的时候,wait for cache_thread队列
// 取出新的thd,运行之
static bool cache_thread(){
    // 判断是否到达cache 线程的阈值
    if (cache_thread_count < thread_cache_size && !abort_loop && !kill_cache_threads){
        cache_thread_count++; // 进入cache状态    

        // wait for the signal to relive the thread
        // 开始wait for新的thd装进cache_thread list
        // 注意这里用的是while而不是if,因为
        // 1、cond_wait可能虚假唤醒,可能因为竞争,并没有真正获得新的thd
        // 2、若获取失败则再次等新的thd,总之就是尽可能的获得新的thd
        while (!abort_loop && !wake_thread && !kill_cached_threads){
            mysql_cond_wait(&cond_thread_cache, &lock_thread_count);
            cached_thread_count--;
            // 此处没仔细看是什么逻辑,暂时跳过
            if (kill_cached_threads){
                mysql_cond_signal(&cond_flush_thread_cache);
            }
            // 发现有就绪的thd,获取之,然后执行
            if (wake_thread){
                THD* thd;
                wake_thread--;
                thd = thread_cache.get();
                thd->thread_stack = (char*)&thd;
                thd->store_globals();

                thd->mysys_var->abort = 0;
                thd->prior_thr_create_utime = thd->start_utime = my_micro_time();
                // 返回1使得线程不会结束,跑新的thd了
                return 1;
            }
        }
    }
    // 结束线程
    return 0;
}

分析cache thread思路完毕。

时间: 2024-10-12 03:01:27

mysql线程池的实现原理浅析的相关文章

MySQL线程池(THREAD POOL)的原理

MySQL常用(目前线上使用)的线程调度方式是one-thread-per-connection(每连接一个线程),server为每一个连接创建一个线程来服务,连接断开后,这个线程进入thread_cache或者直接退出(取决于thread_cache设置及系统当前已经cache的线程数目),one-thread-per-connection调度的好处是实现简单,而且能够在系统没有遇到瓶颈之前保证较小的响应时间,比较适合活跃的长连接的应用场景,而在大量短连接或者高并发情况下,one-thread

MySQL详解(7)-----------MySQL线程池总结(一)

线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低,降低了服务质量.线程池是一种通用的技术,通过预先创建一定数量的线程,当有请求达到时,线程池分配一个线程提供服务,请求结束后,该线程又去服务其他请求. 通过这种方式,避免了线程和内存对象的频繁创建和释放,降低了服务端的并发度,减少了上下文切换和资源的竞争,提高资源利用效率.所有服务的线程池本质都是位

MySQL线程池总结

线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低,降低了服务质量.线程池是一种通用的技术,通过预先创建一定数量的线程,当有请求达到时,线程池分配一个线程提供服务,请求结束后,该线程又去服务其他请求. 通过这种方式,避免了线程和内存对象的频繁创建和释放,降低了服务端的并发度,减少了上下文切换和资源的竞争,提高资源利用效率.所有服务的线程池本质都是位

Mysql线程池优化笔记

Mysql线程池优化我是总结了一个站长的3篇文章了,这里我整理到一起来本文章就分为三个优化段了,下面一起来看看. Mysql线程池系列一(Thread pool FAQ) 首先介绍什么是mysql thread pool,干什么用的?使用线程池主要可以达到以下两个目的:1.在大并发的时候,性能不会因为过载而迅速下降.2.减少性能抖动 thread pool的工作原理?线程池使用分而治之的方法来限制和平衡并发性.与默认的thread_handling不同,线程池将连接和线程划分开,所以连接数量和执

线程池的实现原理

1.线程池简介:    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间. 如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能.                一个线程池包括以下四个基本组成部分:                1.线程池管理器(ThreadPool):用于创建并管理线程池,包

Java线程池Executors.newFixedThreadPool原理解析

从事Java多线程开发的程序员来说,了解Java的线程池实现原理是必不可少的,以下将会结合Java线程池代码来说明它的实现原理,首先,我们要思考: 线程池的表现形式 线程池里面的线程什么时候创建 线程池里面的线程什么时候结束或者该不该结束 线程池的实现原理 说道Java线程池就不得不说ExecutorService接口和Executors类了,从源码上来看Executors类里面封装了线程池的创建,并且定义了各自不同的线程池类型,本文着重讲Executors这个类的newFixedThreadP

深入源码分析Java线程池的实现原理

程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对CPU利用的优化手段. 网上有不少介绍如何使用线程池的文章,那我想说点什么呢?我希望通过学习线程池原理,明白所有池化技术的基本设计思路.遇到其他相似问题可以解决. 池化技术 前面提到一个名词--池化技术,那么到底什么是池化技术呢? 池化技术简单点来说,就是提前保存大量的资源,以备不时之需.在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用

Java 线程池ExecutorService运行原理 和FutureTask 的使用

一.线程池ExecutorService运行原理 ThreadPoolExecutor中有corePoolSize(核心线程)和maximumPoolSize(工作线程),默认核心线程和工作线程数量一致.1.当线ExecutorService线程池,使用submit,或者execute时2.先判断运行中的线程是否大于corePoolSize(核心线程)数量3.如果大于corePoolSize(核心线程)且maximumPoolSize(工作线程)未满则把该线程存着到工作线程等待.4.如果大于co

21.线程池ThreadPoolExecutor实现原理

1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题.因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗.通过复用已存在的线程和降低线程关闭的次数来尽可能降低系统性能损耗: 提升系统响应速度.通过复用线程,省去创建线程的过程,因此整体上提升了系统的响应速度: 提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此,需要使用线程池来管理线程. 2. 线程池的