重要: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.? <code>propertyFileName</code>* 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次,連接被拒絕!
怎么辦,難道我在這里坐著等一個小時以后再調試,顯然不可以,不能打著技術“瓶頸”的幌子“磨洋工”。于是,我試著解決這一報錯。
最簡單的解決辦法:
,轉了一圈發現根本不是我改配置能解決的,用這招輕松解決。