「数据库、数据库连接池、数据源」这些概念你真的理解了吗?

前言

我学习的过程中,对于连接池和数据源分得不是很清楚,而且我发现有的人将数据库等同于数据源,或者将数据源等同于连接池,实际上这些说法并不准确。

在某次工作中,同事 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 提供了一组高效的连接池和监控工具,可用于管理和监控数据库连接。

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

这里给出 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 等软件称为数据库,但它们实际上是一种可以操作和管理数据库的软件,而真正的数据库是指存储数据的仓库。

接着,讲了什么是数据库连接池,数据库连接池是一种池化技术,它可以提前准备好一定数量的连接对象并将它们放在一个「池子」中。这些连接对象可以被重复使用,当需要进行数据库操作时,可以从连接池中获取连接对象并执行相关操作。执行完毕后,连接对象会被放回到池子中,以供后续的操作使用。这种技术可以避免频繁地创建和销毁连接对象,从而提高应用程序的性能和效率。

最后,讲了什么是数据源以及它与连接池的关系,它们两者是不同的,或者说有这么三个词:「数据源」、「数据库连接池」、「数据源对象」。单独说数据源,那么就顾名思义,数据的来源。数据库连接池,用于管理连接的,方便连接的复用,提升效率。数据源对象,它包含了连接池的配置,也配置了数据源,即数据从哪里来。

以上,也不知道我又没有说清楚,欢迎大家评论!

最后的最后

希望各位屏幕前的靓仔靓女们给个三连!你轻轻地点了个赞,那将在我的心里世界增添一颗明亮而耀眼的星!

咱们下期再见!

原文链接:https://www.cnblogs.com/god23bin/p/concepts-that-you-need-to-understand.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:「数据库、数据库连接池、数据源」这些概念你真的理解了吗? - Python技术站

(0)
上一篇 2023年4月19日
下一篇 2023年4月22日

相关文章

  • 配置ogg异构oracle-mysql 双向同步注意事项

    双向同步需要考虑的是怎么解决循环复制,以及同时更新一张表以谁为基准。 配置过程就不写了,大致和oracle到mysql的单向+mysql到oracle的单向差不多。 需要注意的有如下几点: 1.oracle和mysql的2端,抽取(extract)和应用(replication)应该使用不同的用户 2.为解决禁止循环复制,应该在ext进程配置3个参数,如下:…

    MySQL 2023年4月12日
    00
  • mysql数据库查询基础命令详解

    MySQL数据库查询基础命令详解 MySQL是目前最流行的关系型数据库之一,它具有易用性和高扩展性等优点,并且可用于各种类型的应用开发。本文将介绍MySQL数据库查询基础命令,以帮助初学者更好地了解和使用MySQL。 1. 基础查询语句 1.1 SELECT语句 SELECT语句是MySQL中最常用的查询语句,它可以从指定的表中检索出数据,使用基本的语法格式…

    MySQL 2023年5月18日
    00
  • MySQL中一条update语句是如何执行的

    MySQL中的一条update语句的执行过程可以分为以下步骤: 1.分析语句: MySQL服务器首先会对update语句进行解析,并检查语法和语义是否正确。如果有语法错误或缺少操作权限,服务器将拒绝执行该语句。 2.查询优化: 一旦语句被分析,MySQL服务器会进行查询优化,以确定执行查询的最佳方式。这个过程包括选择索引,重新排列关联表的顺序等。为了提高查询…

    MySQL 2023年5月19日
    00
  • MySQL GRANT:用户授权方法详解

    MySQL GRANT 命令是 MySQL 数据库中最重要的命令之一,用于授权用户相关数据库操作的权限。通过 GRANT 命令,可以实现对数据库对象(如数据库、数据表、视图等)的不同级别的访问控制和权限分配。 在 MySQL 中,我们可以使用 GRANT 命令将权限授予一个用户,这个用户可以是本地用户,也可以是远程用户。下面对 MySQL GRANT 命令进…

    MySQL 2023年3月10日
    00
  • MySQL 主从延迟的常见原因及解决方法

    承蒙大家的支持,刚上市的《MySQL实战》已经跃居京东自营数据库图书热卖榜第 1 名,收到的反馈也普遍不错。对该书感兴趣的童鞋可通过右边的链接购买。目前,京东自营有活动,只需 5 折。 主从延迟作为 MySQL 的痛点已经存在很多年了,以至于大家都有一种错觉:有 MySQL 复制的地方就有主从延迟。 对于主从延迟的原因,很多人将之归结为从库的单线程重放。 但…

    MySQL 2023年4月19日
    00
  • 转 Swoole】用swoole简单实现MySQL连接池

    在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再根据当前情况去调用其worker进程然后处理PHP,如果需要MySQL,在与MySQL建立连接,这个时候,如果有1000个请求打过来,就需要与MySQL建立1000个连接。如果请…

    2023年4月13日
    00
  • 初步认知MySQL metadata lock(MDL)

    概述 随着5.5.3引入MDL,更多的Query被“Waiting for table metadata lock”给’炕’了SHOW PROCESSLIST的输出也有之前的”Locked”变得粒度更加细的’Waiting for table metadata lock’引入MDL,当需要访问、修改表结构时,都需要对元数据上锁(读/写)MDL在Server层…

    MySQL 2023年4月13日
    00
  • MySQL Threads_running飙升与慢查询的相关问题解决

    MySQL Threads_running飙升与慢查询的相关问题解决 问题描述 在使用MySQL数据库时,我们可能会遇到Threads_running飙升的情况,同时也可能会出现慢查询的问题。这些问题可能会导致服务器性能下降,影响用户使用体验。下面是一些可能导致这些问题的原因: 大量的并发请求导致了Threads_running飙升 未优化的SQL查询导致了…

    MySQL 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部