重要:SpringBoot 2.0 开始推 HikariCP ,将默认的数据库连接池从 tomcat jdbc pool 改为了 hikari , HikariCP 在性能和并发方面确实表现不俗(号称最快的连接池)。
一、当我们遇到这个问题该怎么办?用户“***”连接超过了最大资源数。
考虑修改数据库连接池的默认设置或者不使用数据库连接池技术;
二、什么是数据库连接池技术?
数据库连接池是不需要用户申请和释放数据库的连接,他会自动帮您管理数据库的连接用户只管使用这个连接,连接池会自动释放已经用过的连接,当连接不够用的时候他会自动申请新的连接,这样循环周而复始,保证数据库连接的可用性。大家可以理解为连接池是自动帮助用户管理数据库的连接的地方,这样就不会发生上面的数据库连接忘记关闭的问题
数据库连接池会在tomcat启动的时候就自动申请好数据库连接(申请多少数据库连接根据您的配置文件里设置的决定的),放在一个虚拟的池子里面,您调用数据库的时候从这个池子里拿,用完了池子会自动回收你已经用过的这个连接,为下一个连接做准备!
使用了数据库连接池,连接池会有一个配置文件,配置文件里可以规定他在启动的时候申请最大多少个连接,最小多少个连接,初始多少个连接,一个连接可以存活多久等。
三、为什么要使用数据库连接池技术?
1、数据库并发,数据库同时可以被多个人操作,或者多个客户端请求连接数据库;
2、部分用户申请完数据库连接后,忘记关闭这个数据库连接(这样的问题由于程序员的大意也是时有发生的);
这时候就需要一个中间件管理连接,协助用户进行申请和释放数据库的链接。事先创建好数据库连接放在“数据库连接池”中等你来用,用完了就释放,这样就避免了每次请求连接数据库都要创建连接,节省资源,缩短反应时间。数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
四、如何设置连接池
根据自己程序的并发个数;
以20个并发为例子 连接池我们建议设置
最大连接为:19
最小连接为:5
初始连接为:5
过期时间为:120秒
如果并发是30个的话只需要把上面的最大连接数设置为29个即可,其他的不变。如果把最小连接数和初始连接数设置过大,则会影响数据库连接池的性能。
可以参考:http://www.jspkongjian.net/news.jsp?id=790
五、spring boot项目的连接池技术研究
创建Spring boot 项目使用spring-boot-starter-data-jpa,POM下载的依赖包里会包含spring-boot-starter-jdbc的依赖包,该依赖包绑定com.zaxxer:HikariCP.
看看这里的默认配置
找到直接使用的配置文件源码来看看,如下:
HikariDataSource继承HikariConfig实现了接口DataSource, Closeable两个接口。
再来看看HikariConfig的源码:
不合适贴代码,但还是贴出来,免得大家再去找:
public class HikariConfigimplements HikariConfigMXBean
{
private static final LoggerLOGGER = LoggerFactory.getLogger(HikariConfig.class);
private static final char[]ID_CHARACTERS ="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final long CONNECTION_TIMEOUT =SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT =SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT =MINUTES.toMillis(10);
private static final long MAX_LIFETIME =MINUTES.toMillis(30);
private static final int DEFAULT_POOL_SIZE =10;
private static boolean unitTest =false;
// Properties changeable at runtime through the HikariConfigMXBean
//
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
private volatile Stringusername;
private volatile Stringpassword;
// Properties NOT changeable at runtime
//
private long initializationFailTimeout;
private Stringcatalog;
private StringconnectionInitSql;
private StringconnectionTestQuery;
private StringdataSourceClassName;
private StringdataSourceJndiName;
private StringdriverClassName;
private StringjdbcUrl;
private StringpoolName;
private Stringschema;
private StringtransactionIsolationName;
private boolean isAutoCommit;
private boolean isReadOnly;
private boolean isIsolateInternalQueries;
private boolean isRegisterMbeans;
private boolean isAllowPoolSuspension;
private DataSourcedataSource;
private PropertiesdataSourceProperties;
private ThreadFactorythreadFactory;
private ScheduledExecutorServicescheduledExecutor;
private MetricsTrackerFactorymetricsTrackerFactory;
private ObjectmetricRegistry;
private ObjecthealthCheckRegistry;
private PropertieshealthCheckProperties;
private volatile boolean sealed;
/**
* Default constructor
*/
public HikariConfig()
{
dataSourceProperties =new Properties();
healthCheckProperties =new Properties();
minIdle = -1;
maxPoolSize = -1;
maxLifetime =MAX_LIFETIME;
connectionTimeout =CONNECTION_TIMEOUT;
validationTimeout =VALIDATION_TIMEOUT;
idleTimeout =IDLE_TIMEOUT;
initializationFailTimeout =1;
isAutoCommit =true;
String systemProp = System.getProperty("hikaricp.configurationFile");
if (systemProp !=null) {
loadProperties(systemProp);
}
}
仔细看一下,发现初始化配置有3种方式:
第一种:Default constructor
假如我们不写自己的配置时,就使用这个构造器中的默认参数初始化;
minIdle = -1;
maxPoolSize = -1;
maxLifetime =MAX_LIFETIME; //30s
connectionTimeout =CONNECTION_TIMEOUT; //30s
validationTimeout =VALIDATION_TIMEOUT; // 5s
idleTimeout =IDLE_TIMEOUT; // 10s
initializationFailTimeout =1;
isAutoCommit =true;
默认的连接池的大小是10;
final int DEFAULT_POOL_SIZE =10;
第二种:大概就是根据我们在spring boot 项目中的 properties.yml文件中设置的属性
/**
* Construct a HikariConfig from the specified properties object.
*
* @param properties the name of the property file
*/
第三种:根据file类型的配置文件
/**
* Construct a HikariConfig from the specified property file name. propertyFileName
* will first be treated as a path in the file-system, and if that fails the
* Class.getResourceAsStream(propertyFileName) will be tried.
*
* @param propertyFileName the name of the property file
*/
有个大哥总结了一下:https://blog.csdn.net/MyHerux/article/details/80730690
阿里的数据库连接池技术研究:https://www.cnblogs.com/wuyun-blog/p/5679073.html
研究这一问题的起因:
2018-12-05 16:39:12.566 ERROR 5712 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : User 'root' has exceeded the 'max_connections_per_hour' resource (current value: 100)
root用户每一个小时连接资源的次数超过了100次,连接被拒绝!
怎么办,难道我在这里坐着等一个小时以后再调试,显然不可以,不能打着技术“瓶颈”的幌子“磨洋工”。于是,我试着解决这一报错。
最简单的解决办法:
,转了一圈发现根本不是我改配置能解决的,用这招轻松解决。