我學習的過程中,對於連接池和數據源分得不是很清楚,而且我發現有的人將資料庫等同於數據源,或者將數據源等同於連接池,實際上這些說法並不准確。 ...
前言
我學習的過程中,對於連接池和數據源分得不是很清楚,而且我發現有的人將資料庫等同於數據源,或者將數據源等同於連接池,實際上這些說法並不准確。
在某次工作中,同事 A 說道,這個數據源不行,那麼換一個數據源就可以了,結果我看他操作,原來是改寫了配置中的資料庫連接的 URL,當時我在想,這就是換數據源了?我以為說是把 Druid 這個數據源換掉。至於為什麼會這麼想,主要是因為有個 DruidDataSource。
現在,搞清楚它們的區別不妨聽我說說,歡迎大家在評論區說出你的看法!
資料庫
一提到資料庫,大家都會想到 MySQL、Oracle、PostgreSQL 這些。我們也習慣這樣講:我這個項目的資料庫使用的是 MySQL,是吧。
實際上,嚴格來講,這些是資料庫管理系統(Database Management System,DBMS),它們是一種可以操作和管理資料庫(Database)的軟體。真正的資料庫是指存儲數據的倉庫,這些數據都是持久化存儲在電腦的硬碟上的。
比如 MySQL,我們在 MySQL 客戶端使用 CREATE DATABASE db_demo;
命令,這樣就創建了一個名為 db_demo
的資料庫。
我們可以使用 SHOW VARIABLES LIKE '%datadir';
命令查看資料庫存放在哪個地方。
資料庫連接池
那什麼是資料庫連接池呢?在說什麼是連接池之前,我們先說說什麼是連接(Connection、Connect)。
連接
在一開始學習 MySQL 的時候,我們通過 MySQL 的客戶端來連接上 MySQL 的服務端:
mysql -u root -p 123456
當出現如下輸出時,就說明我們成功連接上 MySQL 的服務端,接著就能輸入各種 SQL 語句了:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 81
Server version: 5.7.39 MySQL Community Server (GPL)
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
以上的連接,只是連接到 MySQL 服務端,並沒有指定連接到哪一個資料庫,當然我們可以通過 USE 資料庫名稱
來切換到指定的資料庫,後續的操作都是在該資料庫上進行。
此處的連接,是一個動作,即 Connect。
我們在學習 JDBC 的時候,知道了想要通過 Java 去操作資料庫,那麼就需要藉助 JDBC 來操作。
在這個過程中,我們首先需要載入資料庫的驅動,然後建立 Java 程式與某個資料庫的連接:
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/db_demo";
String username = "root";
String password = "123456";
// 載入驅動
Class.forName(driver);
// 獲取該資料庫連接(即幫我們創建了一個可以操作db_demo的連接對象)
Connection conn = DriverManager.getConnection(url, username, password);
獲取完之後,我們就能通過連接獲取相關的 Statement 對象(比如預編譯的 PreparedStatement 對象),將我們的 SQL 語句丟給 Statement 對象,通過 Statement 對象執行操作。
操作完畢後就關閉了資料庫連接。
conn.close();
這裡的連接,是一個動作,也是一個對象,因為 Java 是面向對象的,抽象出了一個連接對象,這個連接對象包含了驅動信息、連接的 URL、DBMS 的用戶名和密碼,主要表明瞭此次連接到的是哪個資料庫。
池化技術
現在,我們每進行一次相關的資料庫操作,就需要經過打開/建立連接,執行相關操作,銷毀/關閉連接這麼一個過程。
對於一個簡單的、對資料庫的操作不是很頻繁的應用來說,問題不大,不會有很明顯的性能開銷。
但是,對於一個經常操作資料庫的應用來說,當有許多操作的請求過來時,多次的創建與銷毀連接對象,是相當耗費資源的,比如網路帶寬,記憶體,CPU 的運算等等。當然除了耗費資源,創建與銷毀也會耗費時間。所以就有了資料庫連接池的出現,這種是屬於「池化技術」。
池化技術有這樣的特點,就是提前準備,然後進行復用。對於資料庫連接池,就是提前準備好一定量的連接對象放在一個「池子」中,你可以想象水池,有一定量的水。當有需要的時候,就從這個池子中獲取連接對象,然後進行資料庫的操作,操作完了後,就把對象放回池子中。這就是所謂的「資料庫連接池」。
這裡也就有種用空間換時間的感覺,通過準備一定量的連接對象,避免由調用者手動去打開和關閉連接,進而提高效率。
自己實現一個資料庫連接池
選擇你喜歡的一個地方新建一個類和一個配置文件,我將這兩個東西放在了同一個目錄下:
db.properties:
# 資料庫相關配置
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_one_demo
username=root
password=123456
# 初始化的資料庫連接池大小
initialPoolSize=5
ConnectionPool.java:
/**
* @author god23bin
* @description 簡單的資料庫連接池
*/
public class ConnectionPool {
private static String driver;
private static String url;
private static String username;
private static String password;
/**
* 使用一個List來存放連接,將這個List作為連接池
**/
private static List<Connection> connectionPool;
/**
* 標記對應的連接是否被使用,是為 true,否為 false
**/
private static List<Boolean> usedConnections;
/**
* 連接池大小,即池子中連接的個數
**/
private static int initialPoolSize;
// 讀取配置文件只需要一次就夠了,所以用static代碼塊
static {
//讀取文件配置
InputStream inputStream = null;
Properties properties = new Properties();
try {
// 如果你的是 Spring Boot 應用,db.properties 放在 resource 目錄下,則可以通過 ClassPathResource 來獲取這個配置文件
// inputStream = new ClassPathResource("db.properties").getInputStream();
inputStream = ConnectionPool.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
initialPoolSize = Integer.parseInt(properties.getProperty("initialPoolSize"));
connectionPool = new ArrayList<>(initialPoolSize);
usedConnections = new ArrayList<>(initialPoolSize);
// 載入驅動
Class.forName(driver);
// 創建連接並將連接放到List集合中,標記為未被使用
for (int i = 0; i < initialPoolSize; i++) {
Connection connection = DriverManager.getConnection(url, username, password);
connectionPool.add(connection);
usedConnections.add(false);
}
} catch (IOException | SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 獲取連接
* @return java.sql.Connection 返回連接對象
**/
public synchronized Connection getConnection() throws SQLException {
// 判斷是否有空閑的連接可用,有的話就標記為使用中,接著返回這個連接
for (int i = 0; i < initialPoolSize; i++) {
if (!usedConnections.get(i)) {
usedConnections.set(i, true);
return connectionPool.get(i);
}
}
// 如果沒有可用的連接,那麼創建一個新的連接,把它加入到池中,並返回,簡單處理,這裡的創建並沒有上限
Connection connection = DriverManager.getConnection(url, username, password);
connectionPool.add(connection);
usedConnections.add(true);
initialPoolSize++;
return connection;
}
/**
* 釋放連接,將其標記為未使用
* @param connection 連接
**/
public synchronized void releaseConnection(Connection connection) {
int index = connectionPool.indexOf(connection);
usedConnections.set(index, false);
}
}
目前我知道的開源的資料庫連接池有 DBCP、C3P0,還有阿裡的 Druid。
數據源
數據源(Data Source),即數據的來源。在咱們開發的應用中,數據可以來源於網路,也可以來源於本地的文件,還可以來源於資料庫。
簡而言之,數據源指定了數據從哪裡來。換句話說,數據源是指存儲數據的位置。
在 Java 中,有一個 javax.sql.DataSource
介面,這個介面定義了一組獲取資料庫連接的方法。
Connection getConnection() throws SQLException
Connection getConnection(String username, String password) throws SQLException
以上,就是所謂的數據源。
數據源和連接池的關係
有的人會把數據源等同於連接池,那到底是不是呢?從概念上看,明顯不是一個東西,數據源是數據來源,連接池則是連接的緩存池,用於存儲和管理資料庫連接。
我認為,出現這種看法是因為我們在配置數據源的時候,把連接池也進行了相關的配置。所以才會把數據源等同於連接池。
不過,雖然不是同個東西,但是數據源和連接池是緊密相關的,它們一起協同工作來管理資料庫連接並提供訪問資料庫的功能。
在日常開發中,數據源除了指數據來自哪裡,還可以有其他信息!對於數據源對象來說,它定義了資料庫連接參數以及連接資料庫所需的所有信息,例如資料庫伺服器的地址、用戶名和密碼等。
有的連接池,會從數據源對象中獲取連接參數並使用它們來創建和管理資料庫連接,就比如當我們在項目中使用開源的資料庫連接池的時候,就需要進行相關的配置。對於開源的資料庫連接池,它們都具有實現 Java 的標準數據源介面 javax.sql.DataSource
的類,可以直接使用。
以 Druid 為例,這個類是 com.alibaba.druid.pool.DruidDataSource
,可以用於創建和管理資料庫連接。
在我看來,Druid 是一個既包含數據源功能又包含連接池功能的開源項目。它可以用作數據源,通過配置和管理連接池來提供訪問資料庫的功能。Druid 提供了一組高效的連接池和監控工具,可用於管理和監控資料庫連接。
這裡給出 Druid 的一些配置:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本屬性 url、user、password -->
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="5" />
<property name="minIdle" value="10" />
<property name="maxActive" value="20" />
<!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="2000" />
<!-- 配置一個連接在池中最小生存的時間,單位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="600000" />
<property name="maxEvictableIdleTimeMillis" value="900000" />
</bean>
從上面的配置中可以看到,我們在這裡進行了數據源配置,這裡不僅僅配置了連接對象連接的是哪一個資料庫,它的用戶名和用戶密碼是多少,還配置了資料庫連接池的初始化大小、最小和最大連接數等屬性。
總結
一開始,我主要說明瞭資料庫和資料庫管理系統(DBMS)的區別。雖然我們通常會將 MySQL、Oracle、PostgreSQL 等軟體稱為資料庫,但它們實際上是一種可以操作和管理資料庫的軟體,而真正的資料庫是指存儲數據的倉庫。
接著,講了什麼是資料庫連接池,資料庫連接池是一種池化技術,它可以提前準備好一定數量的連接對象並將它們放在一個「池子」中。這些連接對象可以被重覆使用,當需要進行資料庫操作時,可以從連接池中獲取連接對象並執行相關操作。執行完畢後,連接對象會被放回到池子中,以供後續的操作使用。這種技術可以避免頻繁地創建和銷毀連接對象,從而提高應用程式的性能和效率。
最後,講了什麼是數據源以及它與連接池的關係,它們兩者是不同的,或者說有這麼三個詞:「數據源」、「資料庫連接池」、「數據源對象」。單獨說數據源,那麼就顧名思義,數據的來源。資料庫連接池,用於管理連接的,方便連接的復用,提升效率。數據源對象,它包含了連接池的配置,也配置了數據源,即數據從哪裡來。
以上,也不知道我又沒有說清楚,歡迎大家評論!
最後的最後
希望各位屏幕前的靚仔靚女們
給個三連!你輕輕地點了個贊,那將在我的心裡世界增添一顆明亮而耀眼的星!
咱們下期再見!