Mybatis + Druid 資料庫連線池的連線快取原理
Mybatis + Druid 資料庫連線池的連線快取原理
Mybatis 預設資料庫連線池快取原理
Mybatis 預設資料庫連線池快取原理,和為什麼要使用連線池, 網站上較多文章可以通過這個連結檢視https://www.cnblogs.com/yougewe/articles/10061276.html
Mybatis+Druid 連線池原理
在Druid中定義了DruidDataSource
類來維護資料庫連線池狀態,定義了connections
陣列來儲存已經建立的資料庫連線。
private volatile DruidConnectionHolder[] connections;
driud 將與資料庫的物理連線(最原始的資料庫
Connection
)儲存在DruidConnectionHolder
中其中,DruidConnectionHolder
儲存了資料庫連線Connection
和連線監控資訊。
Druid 連線池初始化和連接獲取
Mybatis+Druid 資料庫連線池一起使用時,首先是通過SqlSessionFactory.openSession獲取一個SqlSession的例項,執行資料庫操作時又通過SqlSession獲取對應的Mapper,呼叫Mapper的方法,實際資料庫連線Connection
的建立是在執行資料庫操作的時候建立的。獲取資料庫連線的邏輯是在DruidDataSource中
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { //按配置初始化資料庫連線池 init(); if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(this); return filterChain.dataSource_connect(this, maxWaitMillis); } else { return getConnectionDirect(maxWaitMillis); //從資料庫連線池中獲取資料庫連線 } }
在呼叫DruidDataSource.geConnection
時首先會呼叫init方法,init方法按照mybatis的配置進行資料庫連線池的初始化,初始化之後getConnectionDirect
再從連線池中返回資料庫連線。
其中init方法中建立資料庫連線池的部分邏輯如下:
//..校驗配置資訊合法性,通過配置資訊建立載入驅動類
while (poolingCount < initialSize) {
try {
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); //通過讀取的配置創建於資料庫的連線
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo); //用DruidConnectionHolder儲存的連線和連線資訊
connections[poolingCount++] = holder; //將DruidConnectionHolder儲存到連線池中
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}
init() 方法中按配置的初始池連線數初始化資料庫連線,前後部分還的校驗配置的和合法性、統計活躍的連線和殭屍連線、設定連線狀態回撥處理器等操作,這寫內容不展示。
然後下一步getConnectionDirect方法就是直接從連線池中獲取上一步初始化的資料庫連線,關鍵邏輯如下:
if (maxWait > 0) {
holder = pollLast(nanos);//從connections陣列中獲取資料庫連線
} else {
holder = takeLast();
}
if (holder != null) {
activeCount++;
if (activeCount > activePeak) {
activePeak = activeCount;
activePeakTime = System.currentTimeMillis();
}
}
getConnectionDirect
的主要工作就是通過pollLast
方法從connections
陣列中獲取資料庫連線,並返回,實現從連線池中獲取資料庫連線的效果。
Druid 連接回收重用
在Mybatis中SqlSession在非執行緒安全的,不能被共享的, 所以在每一次業務呼叫觸發並返回響應之後,就應該呼叫sqlSession的close()方法關閉它。如果不使用資料庫連線池呼叫sqlSession.close()方法就會連通對應的資料庫連線一同關閉掉。
SqlSession.close的呼叫鏈
//1. DefaultSqlSession.java
@Override
public void close() {
try {
executor.close(isCommitOrRollbackRequired(false));
closeCursors();
dirty = false;
} finally {
ErrorContext.instance().reset();
}
}
//2. CachingExecutor.java
@Override
public void close(boolean forceRollback) {
try {
try {
rollback(forceRollback);
} finally {
if (transaction != null) {
transaction.close();
}
}
} catch (SQLException e) {
// Ignore. There's nothing that can be done at this point.
log.warn("Unexpected exception on closing transaction. Cause: " + e);
} finally {
transaction = null;
deferredLoads = null;
localCache = null;
localOutputParameterCache = null;
closed = true;
}
}
//3. JdbcTransaction.java
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close(); //關閉資料庫連線
}
}
如程式碼所示最後呼叫connection.close()
了,在沒有資料庫連線池的情況下與資料庫的連線connection
就是關閉了,但是在使用Druid資料庫連線池時connection
的是com.alibaba.druid.pool.DruidPooledConnection
類的例項,DruidPooledConnection
類實現了java.sql.Connection
介面,並重寫了close()方法,在重寫的close方法中並沒有直接關閉掉資料庫連線,而是將資料庫連接回收到了DruidDataSource中的connections陣列中。回收方法是DruidDataSource的recycle方法中,處理邏輯如下:
lock.lock();
try {
activeCount--;
closeCount++;
result = putLast(holder, currentTimeMillis);//呼叫putLast方法將DruidConnectionHolder回收到connections中
recycleCount++;
} finally {
lock.unlock();
}
putLast邏輯如下:
boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
if (poolingCount >= maxActive) {
return false;
}
e.lastActiveTimeMillis = lastActiveTimeMillis;
connections[poolingCount] = e;//將當前關閉sqlSession對應的資料庫連線DruidConnectionHolder放回connections中
incrementPoolingCount();
if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = lastActiveTimeMillis;
}
notEmpty.signal();
notEmptySignalCount++;
return true;
}
就是這樣的流程在使用Mybatis+Druid的時候關閉SqlSession
,對應的連資料庫連線不會直接關掉,而是重新儲存到DruidDataSource.connections
中,在下一次執行資料庫操作時可以直接從DruidDataSource.connections
中直接獲取已建立的連線,從而優化建立資料庫連線帶來的效率影響。