咱們訪問數據庫時,須要經過TCP協議與數據庫創建鏈接,鏈接使用完之後要釋放鏈接,TCP協議是一個面向鏈接的協議,而創建鏈接須要三次握手,釋放鏈接須要四次揮手,這個過程是比較耗時的,假如頻繁訪問數據庫,每次都是直接與數據創建鏈接,會帶來很大的性能問題,對於這樣的問題如何解決呢?鏈接池就此誕生了。java
鏈接池是池化思想的一種應用,基於亨元模式作了落地的實現,就是在內存中開闢一塊區域,來存儲建立好的鏈接,讓這此鏈接能夠獲得重用,進而提升數據庫的訪問性能。mysql
在java中全部鏈接池的實現都必須遵照java中的一個設計規範,此規範爲javax.sql.DataSource,經過這個規範獲取鏈接池的具體實現,進而取到鏈接,實現與數據庫通信。程序員
HikariCP是由日本程序員開源的一個數據庫鏈接池組件,代碼很是輕量,而且速度很是快。根據官方提供的數據,在i7,開啓32個鏈接的狀況下,進行隨機數據庫讀寫操做,單從性能角度看,性能從高到低分別爲:HikariCP,druid,tomcat-jdbc,dbcp,c3p0;HikariCP的速度 是目前經常使用鏈接池中最快的。
HikariCP是SpringBoot2.0默認的鏈接池,全世界使用範圍也很是廣,不過對於大部分業務來講,使用哪一款鏈接池,要看業務的需求,自由選擇。
下圖是HikariCP官網給出的性能對比:
算法
mysql -uroot -proot
set names utf8;
source d:/goods.sql
drop database if exists dbgoods; create database dbgoods default character set utf8; use dbgoods; create table tb_goods( id bigint primary key auto_increment, name varchar(100) not null, remark text, createdTime datetime not null )engine=InnoDB; insert into tb_goods values (null,'java','very good',now()); insert into tb_goods values (null,'mysql','RDBMS',now()); insert into tb_goods values (null,'Oracle','RDBMS',now()); insert into tb_goods values (null,'java','very good',now()); insert into tb_goods values (null,'mysql','RDBMS',now()); insert into tb_goods values (null,'Oracle','RDBMS',now()); insert into tb_goods values (null,'java','very good',now());
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
spring.main.banner-mode=off spring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8 spring.datasource.username=root spring.datasource.password=root
spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=15 spring.datasource.hikari.auto-commit=true spring.datasource.hikari.idle-timeout=30000 spring.datasource.hikari.pool-name=DatebookHikariCP spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-test-query=SELECT 1
單元測試API設計及應用分析,如圖所示:
spring
在項目中添加單元測試類及測試方法,代碼以下:sql
package com.cy.pj.common; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @SpringBootTest public class DataSourceTests { /**DataSource爲java中定義的一個鏈接池規範,全部鏈接池 * 產品中必須定義一個這個規範的實現 */ @Autowired private DataSource dataSource; //請問這個dataSource指向的具體對象是誰 @Test public void testGetConnection() throws SQLException { //獲取dataSource變量指向的對象的具體類型的名字 //com.zaxxer.hikari.HikariDataSource System.out.println(dataSource.getClass().getName()); //請問,經過dataSource獲取鏈接的大概過程是怎樣的? //1.經過dataSource獲取鏈接池(鏈接池不存在時則建立池)-HikariPool-->ConnectionBag-->CopyOnWriteArrayList //2.底層基於jdbc獲取與數據庫的鏈接,並將鏈接存儲到池中 //3.返回池中鏈接. Connection conn = dataSource.getConnection(); System.out.println(conn); //HikariProxyConnection@1865982601 } }
基於HikariCP,藉助JDBC技術訪問商品閘中的數據數據庫
API設計,如圖所示:
api
基於業務需求,進行商品查詢過程的時序圖設計,如圖所示:
數組
第一步:定義GoodsDao接口tomcat
package com.cy.pj.goods.dao; import java.util.List; import java.util.Map; /** * 商品模塊數據訪問層接口 */ public interface GoodsDao { /** * 查詢全部商品信息,將每一行記錄存儲到一個map對象,而後將多個存儲到list集合. */ List<Map<String,Object>> findGoods(); }
第二步:建立GoodsDao接口實現類
package com.cy.pj.goods.dao; /** * 此對象爲一個商品數據層訪問對象,如今要求在此類中定義一個方法,這個方法基於JDBC從從數據庫獲取商品信息,並將其封裝到map集合,要求一個行記錄一個map對象(key爲表中字段名,值爲字段名對應的值),多個map存儲到list集合. @Repository此註解一般用於描述數據層實現類對象,本質上就是一個特殊的@Component, 都是要交給spring框架管理的一個Bean對象 */ @Repository public class DefaultGoodsDao implements GoodsDao{ @Autowired private DataSource dataSource;//hikariCP /**查詢商品信息,一行記錄映射爲內存中的一個map對象*/ public List<Map<String,Object>> findGoods(){ Connection conn=null;//java.sql.* Statement stmt=null; ResultSet rs=null; String sql="select * from tb_goods"; //1.獲取鏈接(從鏈接池獲取) try { conn=dataSource.getConnection(); //2.建立statement對象 stmt=conn.createStatement(); //3.發送sql rs=stmt.executeQuery(sql); //4.處理結果 List<Map<String,Object>> list=new ArrayList<>(); while(rs.next()){//循環一次取一行,一行記錄映射爲一個map對象 list.add( rowMap(rs));//將存儲了一行記錄的map對象再存儲到list集合 } return list; }catch (SQLException e){ e.printStackTrace(); throw new RuntimeException(e);//轉換爲非檢查異常(編譯時不檢測的異常) }finally{ //5. 釋放資源 close(rs,stmt,conn); } }
定義行映射方法
private Map<String,Object> rowMap(ResultSet rs)throws SQLException{ Map<String,Object> rowMap=new HashMap<>(); //方法1映射 //rowMap.put("id",rs.getInt("id")); //rowMap.put("name",rs.getString("name")); //rowMap.put("remark",rs.getString("remark")); //rowMap.put("createdTime",rs.getTimestamp("createdTime")); //方法2映射 ResultSetMetaData rsmd=rs.getMetaData();//獲取元數據(包括表中的字段名) int columnCount=rsmd.getColumnCount();//獲取列的數量 for(int i=0;i<columnCount;i++){ rowMap.put(rsmd.getColumnLabel(i+1),rs.getObject(rsmd.getColumnLabel(i+1))); //getColumnLabel(i+1);獲取表中字段名或字段名對應的別名 } return rowMap; }
定義釋放資源的方法
private void close(ResultSet rs,Statement stmt,Connection conn){ if(rs!=null)try{rs.close();}catch(Exception e){e.printStackTrace();} if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();} //這裏的鏈接是返回到了池中 if(conn!=null)try{conn.close();}catch(Exception e){e.printStackTrace();} } }
定義單元測試類,並對其查詢過程進行單元測試,例如:
package com.cy.pj.goods.dao; @SpringBootTest public class GoodsDaoTests { @Autowired private GoodsDao goodsDao; @Test void testFindGoods(){ List<Map<String,Object>> list= goodsDao.findGoods(); for(Map<String,Object> map:list){ System.out.println(map); } } }
數據庫驅動
JDBC
會
會帶來必定的內存開銷,以空間換時間
數組--隨機訪問效率比較高,增刪比較慢
鏈表--適合隨機插入和刪除,查詢比較慢
FIFO(先進先出),FILO(先進後出)
線程安全(鎖-鎖的粒度會影響性能和併發)
最多有多少鏈接,多餘的鏈接什麼時間被釋放
創建鏈接時會經過TCP/IP協議(創建鏈接三次握手、釋放鏈接四次揮手)
總之,數據庫鏈接池的爲咱們的項目開發及運行帶來了不少優勢,具體以下:
因爲數據庫鏈接獲得複用,減小了大量建立和關閉鏈接帶來的開銷,也大大減小了內存碎片和數據庫臨時進程、線程的數量,使得總體系統的運行更加平穩。
使用了數據庫鏈接池之後,因爲資源重用,大大減小了頻繁關閉鏈接的開銷,大大下降了TIME_WAIT的出現頻率。
數據庫鏈接池在應用初始化的過程當中通常都會提早準備好一些數據庫鏈接,業務請求能夠直接使用已經建立的鏈接,而不須要等待建立鏈接的開銷。初始化數據庫鏈接配合資源重用,使得數據庫鏈接池能夠大大縮短系統總體響應時間。
數據庫鏈接池做爲一款中間件,用戶能夠自行配置鏈接的最小數量、最大數量、最大空閒時間、獲取鏈接超時間、心跳檢測等。另外,用戶也能夠結合新的技術趨勢,增長數據庫鏈接池的動態配置、監控、故障演習等一系列實用的功能。