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

yizhihongxing

前言

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

在某次工作中,同事 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日

相关文章

  • 缓存与数据库双写一致性几种策略分析

    作者:京东零售 于泷 一、背景 在高并发场景中,为防止大量请求直接访问数据库,缓解数据库压力,常用的方式一般会增加缓存层起到缓冲作用,减少数据库压力。引入缓存,就会涉及到缓存与数据库中数据如何保持一致性问题,本文将对几种缓存与数据库保证数据一致性的使用方式进行分析。为保证高并发性能,以下分析场景不考虑执行的原子性及加锁等强一致性要求的场景,仅追求最终一致性。…

    MySQL 2023年4月19日
    00
  • MySQL分区表管理命令汇总

    MySQL分区表管理命令汇总 什么是MySQL分区表 MySQL分区表是MySQL数据库中一种特殊的表,它将数据分散到多个物理分区中,每个分区只包含表的一部分行,从而能够更高效地查询和管理大量数据。 创建MySQL分区表 创建MySQL分区表的关键是在创建表时使用PARTITION BY子句,其常见的分区方式有以下几种: RANGE分区:按照范围对数据进行分…

    MySQL 2023年5月19日
    00
  • mysql limit分页优化方法分享

    一下是「mysql limit分页优化方法分享」的完整攻略。 一、问题概述 在处理大量数据时,往往需要进行分页处理。而mysql中的limit语句便是用于分页的一个重要方法。但在数据量较大的情况下,使用limit语句进行分页容易导致性能问题。因此,本文将介绍mysql limit分页优化的方法。 二、基础知识 在介绍mysql limit分页优化的方法之前,…

    MySQL 2023年5月19日
    00
  • MySQL中因字段字符集不同导致索引不能命中的解决方法

    在MySQL中,如果表中的某个字段使用的字符集与索引使用的字符集不同,则在进行索引查询时可能会出现无法命中索引的情况,这时需要进行相应的解决措施。 下面是解决因字段字符集不同导致索引不能命中的方法: 确认表和字段的字符集 在MySQL中,可以使用如下命令查看表和字段的字符集: SHOW CREATE TABLE table_name; 其中table_nam…

    MySQL 2023年5月19日
    00
  • Can’t connect to MySQL server on ‘localhost’ (10048)问题解决方法

    当我们访问本地MySQL服务器时,有时候会遇到 “Can’t connect to MySQL server on ‘localhost’ (10048)” 的错误提示,这个错误提示通常是由于MySQL服务器无法连接导致的。以下是一些可能的原因和解决方法: 原因 出现这个错误的原因可能是因为以下一些原因: MySQL服务未启动。 MySQL配置出现错误。 端…

    MySQL 2023年5月18日
    00
  • mysql 排重查询

    GROUP BY 语句可以实现某一列的去重查询。 直接上语句: select io_dev_id from io_info where (TID=1 AND host_name=’yang1′) GROUP BY 1; 按照io_dev_id去重查询。   p:顺手加上与ORDER BY 和 distinct的区分使用 GROUP BY 是根据列捡选 ORD…

    MySQL 2023年4月12日
    00
  • MySQL优化GROUP BY(松散索引扫描与紧凑索引扫描)

    MySQL中的GROUP BY语句可以用于将相似的数据分组并计算其汇总值。但是,当数据集很大时,GROUP BY语句对性能的影响也会很大。本文将详细讲解如何通过松散索引扫描和紧凑索引扫描来优化MySQL中的GROUP BY语句。 松散索引扫描优化 在MySQL中,使用GROUP BY语句分组时,如果不指定排序(ORDER BY)的话,MySQL会随机选择一个…

    MySQL 2023年5月19日
    00
  • Mysql占用CPU过高如何优化,如何解决 批量 kill mysql 中运行时间长的sql

    2017-02-28 15:13 331人阅读 评论(0) 举报   MySQL占用CPU过高如何优化   一次生产DB服务器的 超负荷运行问题解决: 1.查看生产DB服务器top列表, 执行 top 命令 查看Cpu(s) 参数一直处于 98% 状态 ,load average达到了 5  (4核服务器)   可见DB已经超负荷运行了   2.使用root…

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