什么是数据库连接池? 我们为什么需要它?
现代网站架构/业务架构越来越重视横向拓展的能力,随之而来的是 Server 或者容器的数量快速增长,但是传统 RDBMS 的扩展性无法跟上这种步伐,导致大量的数据库连接不断的在数据库端创建、断开,不仅性能方面受到影响,在个别极端情况下也会导致数据库本身出现卡死等影响业务的现象。
因此资源池的理念也被应用于数据库相关的场景,数据库连接池也应运而生。数据库连接池会提前创建,并维护一定数量的长连接,当程序端需要访问数据库时,连接池会“借”一个数据库连接出去,等使用完毕后再“还”给连接池。通过这种方式,避免了在数据库端大量的创建、断开数据库连接,不仅节省了数据库服务器的性能,还避开了业务高峰期对数据库产生的业务峰值。
本章节会介绍一下连接池的常见问题,并列出几个主流的编程语言的连接池配置作为参考。本文可以结合 Oracle 的连接池配置的文章一起使用,来为业务定制合理的配置。
大多数业务都会使用容器或者其他的方式部署多个业务端,来使用同一个数据库实例,那么设置数据库端的连接数限制时,就需要设置成最大可能的连接数。
假设业务部署时使用了 N 个容器(例如 Docker),每个容器设置的连接数硬上限为 40,那么在数据库端至少要把最大连接数设置为 N*40,一般为了安全起见会把这个最大连接数设置为 N*40+100。有一部分业务的客户端没有连接池,而是用 processor,worker,thread 等方式来设置工作、并发线程数,那么这些客户端可能是使用短连接来连接数据库,最大连接数应该设置为 N*max_processor+100,N*max_worker+100 等。
实际已使用的连接数,可以在具体的数据库端进行查看,以 MySQL 为例,执行 show global status like '%Threads_connected%';
进行查看。
c3p0 是 Java 中较常用的连接池,详细配置信息参考文档,多数情况下可以参考如下配置。
<!-- c3p0连接池配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 用户名 -->
<property name="user" value="${username}"/>
<!-- 用户密码 -->
<property name="password" value="${password}"/>
<property name="driverClass" value="${driver_class}"/>
<property name="jdbcUrl" value="${url}"/>
<!--连接池中保留的最大连接数,硬性限制,不会超过这个值。默认值: 15 -->
<property name="maxPoolSize" value="40"/>
<!-- 连接池中保留的最小连接数,默认为:3 -->
<property name="minPoolSize" value="20"/>
<!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3 -->
<property name="initialPoolSize" value="20"/>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
<property name="maxIdleTime">3600</property>
<!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
<property name="checkoutTimeout" value="3000"/>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
<property name="acquireIncrement" value="10"/>
<!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次 -->
<property name="acquireRetryAttempts" value="10"/>
<!--重新尝试的时间间隔,默认为:1000毫秒 -->
<property name="acquireRetryDelay" value="1000" />
<!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
<property name="autoCommitOnClose">false</property>
<!--c3p0将使用设置的 SQL 语句来完成数据库连接是否可用的检测。默认值: null -->
<property name="preferredTestQuery">select 1;</property>
<!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false -->
<property name="breakAfterAcquireFailure">false</property>
<!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
<property name="idleConnectionTestPeriod">60</property>
<!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0 -->
<property name="maxStatements">0</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->
<property name="maxStatementsPerConnection">0</property>
</bean>
tomcat 作为常用的容器,也可以配置连接池,具体各个参数的作用参考官方文档,多数情况下可以参考如下配置。
PS:Durid 的连接池配置基本雷同,也可作为参考,更详细的设置参考 Durid 官方文档。
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
<!-- 检查连接池中的空闲连接。默认值: false -->
testWhileIdle="true"
<!-- 获取连接池的连接时,检查连接是否可用。默认值: true -->
testOnBorrow="true"
<!-- 连接放回连接池时,检查连接是否可用。默认值: false -->
testOnReturn="false"
<!-- 检查连接是否可用时用的语句。默认值: NULL -->
validationQuery="SELECT 1"
<!-- 触发空闲连接检查的时间阈值,单位为毫秒。默认值: 30*60*1000 -->
minEvictableIdleTimeMillis="60000"
<!-- 连接池的最大活跃连接数,硬性限制,不会超过这个值。-->
maxTotal="40"
<!-- 连接池的最少保持的空闲连接数。-->
minIdle="20"
<!-- 连接池的最多保持的空闲连接数。-->
maxIdle="40"
<!-- 启动连接池时,初始化的连接数。-->
initialSize="20"
<!-- 无连接可用时,等待 maxWaitMillis 后再抛出异常,单位为毫秒。-->
maxWaitMillis="3000"
<!-- 用户名。-->
username="root"
<!-- 密码。-->
password="password"
<!-- Driver 包名。-->
driverClassName="com.mysql.jdbc.Driver"
<!-- 数据库连接的 url。-->
url="jdbc:mysql://localhost:3306/mysql"/>
Python 的连接池一般使用 DBUtils,详细使用方式和代码 sample 参考官方文档。
根据实际使用的 python 版本下载对应的 DBUtil 包。
DBUtils提供两种外部接口:
一般来说,PooledDB 的数据库连接耗时更稳定,大多数情况下都推荐使用。
挑选 PooledDB 一部分常用的参数进行说明:
C 和 C++ 可以使用 libzdb 来管理数据库连接池(线程安全),支持Mysql,Oracle,SQLite,PostgreSQL,目前仅能在 Linux 下使用。
具体原理与代码 Sample 参考官方文档。
连接池的配置可以动态修改,需要调用对应的 Set 方法来设置,也可以通过对应的 Get 方法来获取当前设置。
go 语言自带的 database/sql 库中已经包含了连接池的实现,连接池配置的细节信息参考官方文档,此处仅列出常用参数的介绍。
go 语言有关数据库相关的设置分为两个部分,一部分是添加在 DataSourceName 中的,示例如下。
user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true
这类参数中比较常用的包括:
go 语言中关于连接池的参数设置,需要使用 function 来完成。详细的列表参考官方文档。
较常用的 function 包括如下:
对于任何编程语言,都推荐使用数据库连接池来访问数据库,避免一些潜在的风险。虽然无法把所有语言的数据库连接池配置都一一列出,但是配置参数方面,可以参考如下几个通用的策略:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。