池化技术

池化技术

Posted by Liangjf on December 31, 2019

池化技术

池化技术是一个非常常见且实用的技术。

下面以一个案例来展开。初创公司的前期,为了产品快速开发,上线,而且由于用户量前期比较少,因此并发也比较少,这时候会选择最简单的架构,单体单机架构。

这是最简单的项目架构,我们应该非常熟悉。但是随着时间的推移,用户量慢慢上来了,老板,产品和运营等可能会通过策划活动,推广等,用户量就会激增。

这时候慢慢的系统开始请求的响应时间变慢,由一开始的毫秒级到如今的秒级,或者由1kQPS到如今的200QPS。通过分析日志,发现响应变慢的原因是出现在数据库的访问上。

数据库的一次交互是以下的过程:

  • 1、创建数据库连接(tcp长连接)
  • 2、查询数据库等操作
  • 3、销毁数据库连接,释放资源

这样下来,每次的请求访问数据库都是创建连接-查询-销毁连接释放资源的过程。可能业务逻辑也就1ms,但是数据库一次的交互是4ms,这样QPS是200,1s的数据库访问量是200,但是里面数据库交互就占了四分之三了。时间白白浪费在与数据库的交互上。

这时候可以引入连接池。先说结论吧,引入了连接池,一般都会大大提高并发量,对于系统的性能提高的帮助是大大的。可能引入了数据库连接池后,QPS就变成1K了。

连接池是一种池化技术。一般有数据库连接池,redis连接池,Http连接池。

连接池是一种空间换时间的技术。预先创建一定量的连接,在系统需要的时候开业直接从“池”中取出来直接使用。(这里的取出来,表明是有地方存放这些连接的。一般是通过链表,队列等数据结构来存储。)在使用完毕的时候,直接把当前的连接和使用者解绑,然后把连接放入原本的“池”中。(这里的放回,实质是指把连接,比如是tcp的fd插入到链表中),达到连接复用的作用,减少创建销毁的过程,提高了系统的性能。

一般连接池会有两个比较重要的参数:

  • 最小连接数
  • 最大连接数

这两个参数控制连接池的动作。

连接池从创建到销毁的过程:

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

常见的这两个参数的设置值分别为10,30。

当然,连接池有时也会报错。比如wait_waitout,可能原因是数据库的ip变了,旧的连接还在被使用,所以实际访问不到,于是就一直等待,知道最终的超时报错。

连接池可用

有两种方法来使连接池可用:

  • 使用select 1的方式,作为定时探针来查询连接是否可用,不可用就从连接池中剔除掉。
  • 在使用的时候先判断当前的数据库连接是否可用,不可用并未达到最大连接数就创建连接并且再次访问数据库。但是这样一来就提高了访问数据库的开销了。

一般是推荐第一种,因为第一种把时间的开销均摊到真个连接池的生命周期,实质的开销是可以接受的。第二种可能会对性能有较大的影响,但是对于最终的目的,还是可以接受到。

逐渐的业务越来越复杂,比如有些接口需要访问多次数据库才能够满足所需。时候也可以引入线程池。多次的数据库访问变同步为异步,最终组装它们结果再返回给前端。

根据上面的连接池,很容易知道线程池,其实也是池化技术的一种应用。

线程池,在达到最大线程数时,有请求过来,可以放到队列从存起来,也可以创建更多的线程来处理。

放到队列从存起来

一般是适用于cpu密集型的系统。因为过多的线程(超出cpu核),会造成cpu的频繁切换,造成更多不必要的性能开销。这时候开启和核数一致的线程数就好了。

创建更多的线程来处理

一般适用于io密集型的系统。因为这时的瓶颈是在io,在等待io的时候会让出cpu,这时候有多余的线程,实质是可以拿到cpu的控制权,进而可以处理更多的请求的。一般的web接口适用这种情况。

注意:

  • 线程池中使用的队列的堆积量,最小和最大两个参数是控制堆积的主角。所以在实际业务中,需要根据经验来动态调节这两个参数,然后有一定的监控来监控线程池中的队列的堆积量这个指标。然后调整为更合适的值。

  • 在使用线程池的时候不要使用无界队列。在使用无界队列的时候,因为没有最大线程数的限制,系统会一直的创建线程,慢慢的,必然会把系统的资源耗光,直至服务器所在的机器崩了。

池化技术,适用于频繁创建和销毁的场景,这时候引入各种池化的实现,基本都会大大提高原来系统的性能。

总结

池化技术上面也提到了,是一种空间换时间的抉择。在具体的业务中,要看情况而取舍。