我们知道,随着数据库访问量越来越大,响应速度会越来越慢,同时访问量大到一定程度时,吞吐量会急剧下降。
我们建立一个数据库连接就会建立一个线程,请求结束后销毁进程。以前也有一种连接池,是在客户端设置的。他可以从缓存中取得,用完不销毁。减少频繁的创建和销毁进程。但是对于高并发时,还是无法保护数据库(雪崩)。
而线程池的一个重要作用就是限制了并发进程数,其他的线程要排队,对数据库起到保护作用。
线程池结构
一个线程池包括:一个timer线程和多个线程组。每个线程组包括一个高优先级队列,一个低优先级队列,一个listener线程和多个worker进程。
其中;
队列:存放待执行的IO任务,高优先级队列的会优先处理。一般,体现事务原子性的语句,太久没执行的语句会放到高优先级队列中。
listener线程:用于监听该线程组的语句,如果队列中待执行语句为0,则该线程转变为worker进程,执行语句。如果队列中待执行语句不为0,则把语句放到队列中,这减少了线程的创建。
worker线程是真正执行动作的线程。
timer线程:用于周期性检查该线程组是否处于阻塞状态。一旦发生阻塞,就会新建一个worker线程或者唤醒一个worker进程处理队列中任务。
具体的,每次worker线程执行时,count会加一,而timer检查完后将count置为零。如果下次timer检查是发现队列不为空但是count是0,说明任务被阻塞了。
工作流程
综合来说,线程池是怎么工作的呢?
首先,一个请求连接会根据id%线程组数,确定他被分配到哪个组中。然后listener进程判断是否执行这个连接,如果队列中待执行语句为0,则该线程转变为worker进程,执行语句。如果队列中待执行语句不为0,则把语句放到队列中。
在这其中,如果一个worker进程很久(默认60s)没有被唤醒,就会自动退出,当然如果请求时发现超过最大数量限制(默认100000),也不会被唤醒。
同时,timer周期性检查该线程组是否处于阻塞状态(默认500ms).