zl程序教程

您现在的位置是:首页 >  .Net

当前栏目

postgreSql最佳配置详解(connection 申请、回收策略)

2023-02-18 16:36:30 时间

一、引子

合理配置一个应用的数据库参数,使其运行良好,这很重要。本文以某务中台的生产环境为例,从Apollo上拔下来一套配置,分析是否合理。

二、MybatisPlus配置

由于我们使用Apollo配置参数,所以分两部分:1.个体配置 2.全局配置

2.1 mybatisplus个体配置

mybatis-plus.mapper-locations = classpath*:/mapper/*Mapper.xml  mapper文件地址匹配
mybatis-plus.type-aliases-package =xx.po 映射的实体包路径,
mybatis-plus.tenant-config.ignoretable = table1,table2
mybatis-plus.auth-config = []
mybatis-plus.global-config.sql-parser-cache = true 缓存sql解析

2.2 mybatis-plus全局配置

mybatis-plus.mapper-locations = classpath:/mapper/*Mapper.xml  mapper文件地址匹配
mybatis-plus.configuration.map-underscore-to-camel-case = true    下划线转驼峰
mybatis-plus.global-config.logic-delete-value = true  逻辑已删除值
mybatis-plus.global-config.logic-not-delete-value = false  逻辑未删除值
mybatis-plus.max-query-records-size = 10000 

三、Datasource配置

3.1 dataSource个体配置

=========数据库配置=========
spring.datasource.connectionProperties = druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 通过connectProperties属性来打开mergeSql功能;慢SQL记录5秒

spring.datasource.type = com.alibaba.druid.pool.DruidDataSource 使用德鲁伊连接池
spring.datasource.driver-class-name = org.postgresql.Driver 驱动类名
spring.datasource.url = xx  数据库连接url
spring.datasource.username = ${dbUserName} 用户名
spring.datasource.password = ${dbPassword} 密码
spring.datasource.minIdle = 5 最小空闲连接数 5
spring.datasource.initialSize = 5 初始连接数 5
spring.datasource.maxActive = 100 最大连接数
spring.datasource.maxWait = 60000 获取连接等待超时的时间 60s=1分钟

spring.datasource.filters = stat,wall 监控统计拦截,用于监控界面sql统计
spring.datasource.poolPreparedStatements = false 是否启用缓存PreparedStatements  
spring.datasource.maxPoolPreparedStatementPerConnectionSize = 20 指定每个连接上preStatement缓存数---》未生效!!!

 

=========健康检查=========
spring.datasource.validationQuery = SELECT 1 连接池的健康检查SQL
spring.datasource.testOnBorrow = false 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.testOnReturn = false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
spring.datasource.testWhileIdle = true 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。

#每timeBetweenEvictionRunsMillis毫秒检查一次连接池中空闲的连接,把空闲时间超过minEvictableIdleTimeMillis毫秒的连接断开,直到连接池中的连接数到minIdle为止
spring.datasource.minEvictableIdleTimeMillis = 300000 最小可驱逐空闲时间,连接保持空闲而不被驱逐的最长时间,单位是毫秒 300s=5分钟
spring.datasource.timeBetweenEvictionRunsMillis = 60000 间隔多久才进行一次驱逐检测,单位是毫秒 60s=1分钟


=========连接超时=========
# 关闭abanded连接时输出错误日志,预生产/生产不建议开启,对性能影响
spring.datasource.logAbandoned = false
# 是否清除已经超过“removeAbandonedTimout”设置的无效连接。
spring.datasource.removeAbandoned = true 
# 连接超过指定时间未关闭,就会被强行回收 180s=3分钟
spring.datasource.removeAbandonedTimeoutMillis = 180000  

四、源码剖析

看完配置,大家心里还是懵逼对吧,参数如何生效,druid到底如何运行?

下面,带着问题,深入源码,直接剖析druid如何申请连接、释放连接、连接泄露检查。

4.1.申请连接

最终跟进到DruidDataSource的getConnectionDirect(long maxWaitMillis),获取得到连接后,validationQuery有效性检查,源码如下:

1.testOnBorrow =true,先直接校验,执行validationQuery,失败就关闭连接JdbcUtils.close(realConnection);

2.testWhileIdle=true,如果testOnBorrow =false, 测试空闲的连接,执行validationQuery,失败就关闭连接JdbcUtils.close(realConnection);

3.removeAbandoned=true,如果开启了泄露回收:把连接添加进Map<DruidPooledConnection, Object> activeConnections 。供泄露回收时使用。

分支1和2只会有一个执行。

4.2.释放连接

德鲁伊连接池在获取连接时,会调用一次DruidDataSource的init()。方法中createAndStartDestroyThread()开启了一个销毁线程。

销毁连接的线程包含了run(),如下:

在一个for空条件循环中,根据配置的timeBetweenEvictionRunsMillis连接检测间隔时间,执行一次DestroyTask.run()就休眠一次间隔时间。未设置默认60s。(实际源码中定义了60spublic static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = 60 * 1000L;,所以用户未设置,默认60s,上图中else分支sleep1秒不会执行到)

追踪DestroyTask.run()如下:

2个步骤:

  1. shrink()收缩校验
  2. removeAbandoned()连接泄露移除

shrink()收缩校验

DruidDataSource内部定义了DruidConnectionHolder[] 类型的3个数组:

  • 1.connections:可用连接数组。申请连接就从这里数组队尾拿连接。
  • 2.evictConnections:待移除连接数组。
  • 3.keepAliveConnections:待保活检测数组。

塞进数组

shrink()中计算出需要校验的数量checkCount,执行收缩校验核心逻辑:

  • 校验物理连接的超时时间phyTimoutMills:超时放入evictConnections中,等待移除。
  • 空余时间大于minEvictableIdleTimeMillis(受保最小空闲时间),并且索引(poolingCount)小于checkCount的连接则放入evictConnections;
  • 空余时间大于minEvictableIdleTimeMillis(受保最小空闲时间),并且索引大于checkCount的连接,假若空余时间大于maxEvictableIdleTimeMillis则放入evictConnections,否则放入keepAliveConnections中进行keepAlive检测。

如下图:

数组处理

1.evictConnections:待移除连接数组。使用JdbcUtils.close()  关闭连接。

2.keepAliveConnections:待保活检测数组。根据配置的validationQuery查询SQL执行连接可用性校验。校验通过后再put(holder)塞进connections可用连接数组。

4.3.泄露连接移除

如果开启了removeAbandoned ,执行removeAbandoned()。移除泄露连接逻辑如下:

实际上,就是对可能的连接泄露(打开连接后长时间不关闭)兜底。

1)遍历活跃连接Map<DruidPooledConnection, Object> activeConnections。

2)跳过运行中的连接,running定义:执行SQL前赋值true ,执行完后置false。---》问题1得到答案,不会暴力关闭执行中的连接。

3)如果当前连接已连接时间>=removeAbandonedTimeoutMillis ,直接从activeConnections map 中移除。

这里消耗性能主要两步骤:

  • 1.内存中记录+移除泄露连接
  • 2.打印相关日志的IO---》logAbandoned=false 可关闭写日志

spring 的druid 连接池一般不会造成泄露。如果出现连接泄露,应该找到问题解决。---》问题2得到答案,目前关闭了写日志,就剩下了第一点“内存占用+过滤的性能”成本,要求不高的场景可以作为兜底方案使用。如果项目已稳定,推荐关闭。

五.分析&总结

本节为我们根据:申请、释放连接相关的参数配置,剖析策略是否合理。

5.1 配置分析

spring.datasource.testOnBorrow = false 申请连接时执行validationQuery检测连接是否有效

spring.datasource.testOnReturn = false 归还连接时执行validationQuery检测连接是否有效

spring.datasource.testWhileIdle = true testOnBorrow=false时才生效,申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。

spring.datasource.initialSize = 5 初始连接数 5

spring.datasource.maxActive = 100 最大连接数

spring.datasource.minIdle = 5 最小空闲连接数 5

timeBetweenEvictionRunsMillis= 60000 60s=1分钟检测一次

minEvictableIdleTimeMillis=300000 300s=5分钟 最小空闲不移除时间

maxEvictableIdleTimeMillis 未设置最大空闲移除时间,默认DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 60L * 7 = 7小时。

keepAlive: 未设置保活开关,默认false关闭。不执行保活测试策略。

上述配置对应的策略:

1.初始策略

初始5个连接,最多可开启100个连接。

2.申请策略

申请连接的时候检测,如果连接空闲时间大于1分钟(检测间隔时间),执行validationQuery检测连接是否有效。---》这里可确保我们空闲时间超过1分钟的连接,校验后使用。

3.回收策略

每一分钟执行一次检测,策略如下:

1.连接空闲小于5分钟,不移除。

2.连接空闲大于5分钟,保留”minIdle设置的5个idle连接”,可移除(总数-5)个连接。

3.连接空闲大于7小时,可移除“minIdle设置的5个idle连接”。---》因为没有设置maxEvictableIdleTimeMillis ,默认空闲7小时后才会移除。不过一共就5个倒也没什么事。

4.连接空闲5分钟~7小时,由于没开启keepAlive保活开关,无法对“minIdle设置的5个idle连接”保活测试。-->minIdle设置的5个idle连接,这段时间一直不回收,也不做保活测试,连接是否有效无法保证。

5.2总结

1.现有项

removeAbandoned=true 开启连接泄露检测,要求不高的场景可以作为兜底方案使用。如果项目已稳定,推荐关闭。

2.可添加项

phyTimeoutMillis:看需要开启。物理超时时间。不管空闲时间,超时直接移除。---》这个是终极兜底方案,可以确保超时强制移除。

maxEvictableIdleTimeMillis:建议开启,实现精细化控制。

keepAlive: 建议开启。可针对“minIdle设置的空闲连接”,进行保活测试,从而提升连接的质量。