什么是连接池?如何确认连接池的大小?

对于我们编写的几乎每个网络或移动应用程序来说,其底层的关键组件之一就是数据库。对于编写使用数据库且高性能且资源高效的应用程序,必须处理一项关键资源,但与 CPU、内存等不同,它通常不是很明显。该资源是数据库连接。

什么是数据库连接?

使用MySQL作为DB的数据库连接

数据库连接是由数据库驱动程序创建的对象,数据库驱动程序是一个软件,用于管理与数据库通信的细节,并使我们的应用程序代码能够轻松地使用数据库。例如,MySQL连接可以通过com.mysql.jdbc.Driver驱动程序创建。连接维护许多东西,其中包括用于数据交换的套接字 (套接字是两台机器之间连接的松散逻辑表示)以及与数据库的会话,与典型的 Web 会话非常相似。

需要连接池

连接管理不善的陷阱

未正确关闭数据库连接是难以检测的错误来源。最常见的错误是:

  1. 'Too many connections'错误,数据库不接受客户端连接进行查询处理。
  2. 某种形式的内存溢出(例如:'Heap OutOfMemoryError'):由于打开的连接在内存中累积而发生。

即使 Web 应用程序中服务请求的单个位置没有正确关闭连接,随着时间的推移,也可能会因上述错误而导致应用程序崩溃。

使用连接池通常可以减少或消除此类错误。

引入连接池的效果

考虑以下简单程序,它在数据库上运行 1000 个查询:

ini 复制代码
for i = 1 to 1000
    connection = Driver.getConnection()
    result = connection.executeQuery(query)
    connection.close()

上面代码中我们创建和关闭连接的次数随着执行的查询数量线性增加。

该程序可以通过使用一个非常简单的连接池来高度优化,连接池只是活动数据库连接的缓存。​​​​​​​

ini 复制代码
Pool = CreateConnectionPool(size = 2)
for i = 1 to 1000
    connection = Pool.getConnection()
    result = executeQuery(query, connection)
    Pool.returnConnection(connection)

连接池的简化表示

在这里,创建连接被从池中获取连接所取代,而关闭连接则被返回到池中所取代,后者速度更快。

无池(~34 秒)

java 复制代码
// Query Execution without pool ---> ~34 sec
public static void main(String[] args) throws SQLException {
    long start = System.currentTimeMillis();
    for(int i = 0 ;i < 1000; i++){
        // 1. Application will load the suitable MySQL Driver, eg: com.mysql.jdbc.Driver, com.mysql.cj.jdbc.Driver
        // 2. Driver will connect to the MySQL DB using the provided URL for the DB and credentials
        // 3. Driver will return a connection object corresponding to created DB connection
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/my_db", "root", "");
        
        connection.createStatement().execute("select count(*) from places");
        
        // release connection
        connection.close();
    }
    System.out.println(System.currentTimeMillis() - start);

带池(非常幼稚的实现)(~16 秒)

ini 复制代码
//Query execution using a very simplistic Connection Pool ---> ~16 sec
public static void main(String[] args) throws SQLException {
    long start = System.currentTimeMillis();

    // create pool
    List<Connection> pool = new ArrayList<>();
    for(int i = 0; i < 10;i++){
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/my_db", "root", "");
        pool.add(connection);
    }

    Random r = new Random();
    for(int i = 0 ;i < 1000; i++){
        // get connection
        int randomIdx = r.nextInt(10);
        Connection connection = pool.get(randomIdx);
        pool.remove(randomIdx);

        connection.createStatement().execute("select count(*) from places");
        
        // release connection
        pool.add(randomIdx, connection);

    }
    System.out.println(System.currentTimeMillis() - start);
}

连接池虽然以非常简单的方式实现,但却导致程序执行时间减少了50% 以上(执行时间从 34 秒至 16 秒)。

复杂的连接池库,例如HikariCP、C3P0等,可以带来巨大的性能提升并为您的应用程序带来资源效率。

如何确定连接池的大小

假设您有一个 Web 应用程序,其中处理每个请求都需要对数据库记录执行操作,并且您在阅读本文后决定使用连接池。

如果您的 Web 应用程序要处理 100 个并发请求的负载,那么连接池的大小应该是多少?100,对吗(每个请求 1 个)?

答案是,这取决于几个因素,而且大多数情况下,必须通过实验得出适当的值。需要考虑的一些因素如下:

  1. 您的网络应用程序处理的并发请求数
  2. 平均查询执行时间
  3. DB资源(CPU核心、磁盘速度)

但它通常比人们通常想象的要低。例如,对于上例中的 100 个并发请求,好的池大小大多不会大于 10。最后,这里有一个图可以直观地考虑大小调整:

作者:Ajay Joshi

更多技术干货请关注公号【云原生数据库

squids.cn,云数据库RDS,迁移工具DBMotion,云备份DBTwin等数据库生态工具。

irds.cn,多数据库管理平台(私有云)。

相关推荐
Elastic 中国社区官方博客5 小时前
在 Elasticsearch 中使用 Mistral Chat completions 进行上下文工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
编程爱好者熊浪6 小时前
两次连接池泄露的BUG
java·数据库
南宫乘风8 小时前
基于 Flask + APScheduler + MySQL 的自动报表系统设计
python·mysql·flask
TDengine (老段)8 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
qq7422349848 小时前
Python操作数据库之pyodbc
开发语言·数据库·python
姚远Oracle ACE9 小时前
Oracle 如何计算 AWR 报告中的 Sessions 数量
数据库·oracle
Dxy12393102169 小时前
MySQL的SUBSTRING函数详解与应用
数据库·mysql
码力引擎9 小时前
【零基础学MySQL】第十二章:DCL详解
数据库·mysql·1024程序员节
杨云龙UP9 小时前
【MySQL迁移】MySQL数据库迁移实战(利用mysqldump从Windows 5.7迁至Linux 8.0)
linux·运维·数据库·mysql·mssql
l1t9 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb