高并发系统设计五-池化技术

池化技术-如何减少频繁创建数据库连接的性能损耗

1、池化技术

池化技术的核心思想是空间换时间,期望使用预先创建好的对象来减少频繁创建对象的性能开销,同时还可以对对象进行统一的管理,降低了对象的使用成本。

用连接池预先建立数据库连接

  • 数据库连接池
  • HTTP连接池
  • Redis连接池

2、数据库连接池

两个最重要的配置:最小连接数、最大连接数

2.1、流程

  • 如果当前连接数小于最小连接数,则创建新的连接数处理数据库请求
  • 如果连接池中有控件连接则复用空闲连接
  • 如果空闲池中没有连接并且当前连接数小于最大连接数,则创建新的连接处理请求
  • 如果当前连接数大于等于最大连接数,则按照配置中设定的时间等等旧的连接可用
  • 如果超过了这个设定的时间,则向外抛出错误

2.2、线上设置多少合适

  • 对于数据库连接池的设置,线上最小连接数控制在10左右,最大连接数控制在20-30左右即可。
  • 启动一个线程来定期检测连接池中的连接是否可用,SELECT 1 FROM DUAL
  • testOnBorrow 设置为 false

启动一个线程来定期检测连接池中的连接是否可用,比如使用连接发送“select 1”的命令给数据库看是否会抛出异常,如果抛出异常则将这个连接从连接池中移除,并且尝试关闭。目前 C3P0 连接池可以采用这种方式来检测连接是否可用

在获取到连接之后,先校验连接是否可用,如果可用才会执行 SQL 语句。比如 DBCP 连接池的 testOnBorrow 配置项,就是控制是否开启这个验证。这种方式在获取连接时会引入多余的开销,在线上系统中还是尽量不要开启,在测试服务上可以使用。

3、线程池

使用 ThreadPoolExecutor 创建线程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {}

各个参数说明

  • corePoolSize:线程池中的常驻核心线程数

  • maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1

  • keepAliveTime:多余的空闲线程的存活时间 当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁,直到只剩下corePoolSize个线程为止

  • unit:keepAliveTime的单位

  • workQueue:任务队列,被提交但尚未被执行的任务

  • threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认的即可

  • Handler:拒绝策略,表示当队列满了,并且工作线程大于线程池的最大线程数时如何来拒绝请求执行的runnable的策略

4、需要注意

4.1、优先将任务放入队列中暂存起来,而不是创建更多的线程。

它比较适用于执行CPU密集型的任务,因为执行CPU密集型的任务时CPU比较繁忙,因为只需要和CPU核心数相当的线程,避免过多的线程上下文切换。

在Web系统中有大量的IO操作,比如说查询数据库,查询缓存等,任务在执行IO操作的时候,CPU就空闲下来,这时如果增加执行任务的线程数而不是把任务暂存在队列中,就可以在单位时间内执行更多的任务。Tomcat 使用的线程池就不是 JDK 原生的线程池,而是做了改造,当线程数超过 coreThreadCount 时,就会优先创建线程,直到线程数达到 maxThreadCount。这种比较适合 Web系统大量的 IO 操作的场景。

4.2、线程池中使用的队列的堆积量也是我们需要监控的重要指标。

我在实际项目中就曾经遇到过任务被丢给线程池之后,长时间都没有被执行的诡异问题。最初,我认为这是代码的 Bug 导致的,后来经过排查发现,是因为线程池的 coreThreadCount 和 maxThreadCount 设置得比较小,导致任务在线程池里面大量的堆积,在调大了这两个参数之后问题就解决了。跳出这个坑之后,我就把重要线程池的队列任务堆积量,作为一个重要的监控指标放到了系统监控大屏上

4.3、使用线程池请一定记住不要使用无界队列(即没有设置固定大小的队列)

大量的任务堆积会占用大量的内存空间,一旦内存空间被占满就会频繁地触发 Full GC,造成服务不可用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章