zoukankan      html  css  js  c++  java
  • postgreSql最佳配置详解(connection 申请、回收策略)

    一、引子

    合理配置一个应用的数据库参数,使其运行良好,这很重要。本文以某务中台的生产环境为例,从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设置的空闲连接”,进行保活测试,从而提升连接的质量。

  • 相关阅读:
    http和https的区别与联系
    HTTP请求/响应报文结构
    Java并发包中Lock的实现原理
    Java多线程基础——Lock类
    深入理解Java并发之synchronized实现原理
    ConcurrentHashMap 的实现原理
    【Java集合学习】HashMap源码之“拉链法”散列冲突的解决
    趣谈Java变量的可见性问题
    Java中CAS详解
    LockSupport的park和unpark的基本使用,以及对线程中断的响应性
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/14131720.html
Copyright © 2011-2022 走看看