當前位置:新聞中心 > 要聞 > 正文
    全球快播:「數據庫、數據庫連接池、數據源」這些概念你真的理解了嗎?
    2023-04-22 08:47:39 來源: 博客園
    前言

    我學習的過程中,對于連接池和數據源分得不是很清楚,而且我發現有的人將數據庫等同于數據源,或者將數據源等同于連接池,實際上這些說法并不準確。


    (資料圖)

    在某次工作中,同事 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 81Server 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 itsaffiliates. Other names may be trademarks of their respectiveowners.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.Driverurl=jdbc:mysql://localhost:3306/db_one_demousername=rootpassword=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 connectionPool;    /**     * 標記對應的連接是否被使用,是為 true,否為 false     **/    private static List 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 SQLExceptionConnection getConnection(String username, String password) throws SQLException

    以上,就是所謂的數據源。

    數據源和連接池的關系

    有的人會把數據源等同于連接池,那到底是不是呢?從概念上看,明顯不是一個東西,數據源是數據來源,連接池則是連接的緩存池,用于存儲和管理數據庫連接。

    我認為,出現這種看法是因為我們在配置數據源的時候,把連接池也進行了相關的配置。所以才會把數據源等同于連接池。

    不過,雖然不是同個東西,但是數據源和連接池是緊密相關的,它們一起協同工作來管理數據庫連接并提供訪問數據庫的功能。

    在日常開發中,數據源除了指數據來自哪里,還可以有其他信息!對于數據源對象來說,它定義了數據庫連接參數以及連接數據庫所需的所有信息,例如數據庫服務器的地址、用戶名和密碼等。

    有的連接池,會從數據源對象中獲取連接參數并使用它們來創建和管理數據庫連接,就比如當我們在項目中使用開源的數據庫連接池的時候,就需要進行相關的配置。對于開源的數據庫連接池,它們都具有實現 Java 的標準數據源接口 javax.sql.DataSource的類,可以直接使用。

    以 Druid 為例,這個類是 com.alibaba.druid.pool.DruidDataSource,可以用于創建和管理數據庫連接。

    在我看來,Druid 是一個既包含數據源功能又包含連接池功能的開源項目。它可以用作數據源,通過配置和管理連接池來提供訪問數據庫的功能。Druid 提供了一組高效的連接池和監控工具,可用于管理和監控數據庫連接。

    https://github.com/alibaba/druid/wiki/DruidDataSource配置

    這里給出 Druid 的一些配置:

                                                                                 

    從上面的配置中可以看到,我們在這里進行了數據源配置,這里不僅僅配置了連接對象連接的是哪一個數據庫,它的用戶名和用戶密碼是多少,還配置了數據庫連接池的初始化大小、最小和最大連接數等屬性。

    總結

    一開始,我主要說明了數據庫和數據庫管理系統(DBMS)的區別。雖然我們通常會將 MySQL、Oracle、PostgreSQL 等軟件稱為數據庫,但它們實際上是一種可以操作和管理數據庫的軟件,而真正的數據庫是指存儲數據的倉庫。

    接著,講了什么是數據庫連接池,數據庫連接池是一種池化技術,它可以提前準備好一定數量的連接對象并將它們放在一個「池子」中。這些連接對象可以被重復使用,當需要進行數據庫操作時,可以從連接池中獲取連接對象并執行相關操作。執行完畢后,連接對象會被放回到池子中,以供后續的操作使用。這種技術可以避免頻繁地創建和銷毀連接對象,從而提高應用程序的性能和效率。

    最后,講了什么是數據源以及它與連接池的關系,它們兩者是不同的,或者說有這么三個詞:「數據源」、「數據庫連接池」、「數據源對象」。單獨說數據源,那么就顧名思義,數據的來源。數據庫連接池,用于管理連接的,方便連接的復用,提升效率。數據源對象,它包含了連接池的配置,也配置了數據源,即數據從哪里來。

    以上,也不知道我又沒有說清楚,歡迎大家評論!

    最后的最后

    希望各位屏幕前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心里世界增添一顆明亮而耀眼的星!

    咱們下期再見!

    關鍵詞:
    責任編輯: 梅長蘇