Qt 数据库连接池

一、核心流程拆解(5步)
  1. 初始化连接池(init

    • 接收数据库配置(类型、IP、端口、账号密码等)及连接池参数(最大连接数、最小空闲连接数)。
    • 预先创建minIdleConnections个空闲连接,存入队列m_connections,并记录总连接数m_totalConnections
    • 标记连接池为“已初始化”(m_isInitialized = true)。
  2. 获取连接(acquireConnection

    • 若连接池未初始化,直接返回无效连接。
    • 优先从队列中取空闲连接,通过isConnectionValid验证有效性(执行SELECT 1检查连接是否存活):
      • 有效则直接返回;
      • 无效则销毁该连接(总连接数减1),继续尝试取其他连接。
    • 若队列无可用连接且总连接数未达maxConnections,调用createConnection创建新连接,总连接数加1并返回。
    • 若连接数达上限,通过QWaitCondition阻塞等待(超时退出),直到有连接被释放。
  3. 释放连接(releaseConnection

    • 若连接无效(未打开),直接销毁(总连接数减1)。
    • 若总连接数超上限或空闲连接数已达minIdleConnections,销毁该连接(总连接数减1)。
    • 否则将连接放回队列m_connections,并通过QWaitCondition::wakeOne唤醒一个等待连接的线程。
  4. 关闭所有连接(closeAllConnections

    • 遍历队列中所有空闲连接,关闭连接并通过QSqlDatabase::removeDatabase彻底移除。
    • 重置总连接数为0,标记连接池为“未初始化”。
  5. 辅助功能

    • createConnection:生成唯一连接名,创建QSqlDatabase实例并打开连接(失败则清理无效连接)。
    • isConnectionValid:通过执行SELECT 1检查连接是否存活(避免使用已断开的连接)。
二、核心知识点
  1. 线程同步机制

    • QMutex:保证连接池操作(获取/释放连接、初始化)的线程安全,避免多线程并发冲突。
    • QWaitCondition:当连接池无可用连接时,阻塞等待线程(wait);当连接被释放时,唤醒等待线程(wakeOne),实现连接的高效复用。
  2. 数据库连接管理

    • QSqlDatabase:封装数据库连接,支持多类型数据库(MySQL、SQLite、PostgreSQL),通过唯一连接名(conn_%1_%2)区分不同连接。
    • QSqlQuery:执行SELECT 1验证连接有效性,确保返回的连接可正常使用。
  3. 单例模式

    • 通过getInstance实现单例(static DbConnectionPool instance),保证全局只有一个连接池实例,统一管理所有数据库连接。
  4. 资源复用与控制

    • 预先创建空闲连接,避免频繁创建/销毁连接的开销(连接创建是重量级操作,尤其对远程数据库)。
    • 通过maxConnections限制并发连接数,防止数据库因连接过多崩溃。
三、使用示例(Qt环境)
// 1. 初始化连接池(程序启动时调用)
DbConnectionPool::getInstance().init(
    databaseType::QMYSQL,    // 数据库类型
    "127.0.0.1",             // IP
    3306,                    // 端口
    "mydb",                  // 数据库名
    "root",                  // 用户名
    "123456",                // 密码
    10,                      // 最大连接数
    3                        // 最小空闲连接数
);

// 2. 获取连接并执行查询(业务逻辑中)
QSqlDatabase db = DbConnectionPool::getInstance().acquireConnection(3000); // 超时3秒
if (db.isValid() && db.isOpen()) {
    QSqlQuery query(db);
    if (query.exec("SELECT * FROM user")) {
        while (query.next()) {
            // 处理查询结果
            QString name = query.value("name").toString();
        }
    } else {
        qDebug() << "查询失败:" << query.lastError().text();
    }
    // 3. 释放连接(必须调用,否则连接池会耗尽)
    DbConnectionPool::getInstance().releaseConnection(db);
}

// 4. 程序退出时关闭所有连接
DbConnectionPool::getInstance().closeAllConnections();
四、使用连接池的核心优点
  1. 提高性能
    避免频繁创建/销毁数据库连接(连接创建需握手、认证等耗时操作),通过复用空闲连接减少资源开销,尤其适合高并发场景。

  2. 控制资源占用
    通过maxConnections限制最大并发连接数,防止因连接过多导致数据库负载过高或崩溃。

  3. 统一连接管理
    集中处理连接的创建、验证、销毁,简化业务代码(无需手动管理连接生命周期),且便于监控连接状态(如空闲数、总连接数)。

  4. 增强稳定性
    内置连接有效性检查(isConnectionValid),自动剔除无效连接,避免使用已断开的连接导致业务失败。

  5. 线程安全
    通过QMutexQWaitCondition保证多线程环境下连接的安全获取与释放,支持多线程并发访问数据库。

总结

该连接池通过“预创建+复用+监控”连接,结合Qt的线程同步机制,实现了数据库连接的高效管理,适用于需要频繁操作数据库的Qt多线程程序,可显著提升性能并降低数据库压力。

// DbConnectionPool.h
#pragma once
#include "databasesdklib_global.h"
#include "struct_def.h"
class DbConnectionPool {
public:
    // 单例模式(全局唯一连接池)
    static DbConnectionPool& getInstance();
    // 禁止拷贝
    DbConnectionPool(const DbConnectionPool&) = delete;
    DbConnectionPool& operator=(const DbConnectionPool&) = delete;

    // 初始化连接池(需在使用前调用)
    void init(databaseType type, const QString& ip, int port, const QString& dbName,
        const QString& username, const QString& password,
        int maxConnections = 10, int minIdleConnections = 2);

    // 获取连接(若池为空且未达最大连接数,则创建新连接;否则等待)
    QSqlDatabase acquireConnection(int timeoutMs = 3000);

    // 归还连接(将连接放回池,需确保连接未关闭)
    void releaseConnection(QSqlDatabase db);

    // 关闭所有连接(程序退出时调用)
    void closeAllConnections();

private:
    DbConnectionPool() = default;
    ~DbConnectionPool() { closeAllConnections(); }

    // 创建新连接
    QSqlDatabase createConnection();
    // 检查连接是否有效
    bool isConnectionValid(const QSqlDatabase& db);

private:
    QQueue<QSqlDatabase> m_connections; // 可用连接队列
    QMutex m_mutex; // 保护队列的线程安全
    QWaitCondition m_waitCondition; // 等待连接可用的条件变量

    // 数据库配置
    databaseType m_type;
    QString m_ip;
    int m_port;
    QString m_dbName;
    QString m_username;
    QString m_password;

    // 连接池参数
    int m_maxConnections = 10; // 最大连接数
    int m_minIdleConnections = 2; // 最小空闲连接数
    int m_totalConnections = 0; // 当前总连接数(包括已借出和空闲)
    bool m_isInitialized = false; // 是否已初始化
public:
    int m_count=0;
};
// DbConnectionPool.cpp
#include "DbConnectionPool.h"

DbConnectionPool& DbConnectionPool::getInstance() {
    static DbConnectionPool instance;
    return instance;
}

void DbConnectionPool::init(databaseType type, const QString& ip, int port, const QString& dbName,
    const QString& username, const QString& password,
    int maxConnections, int minIdleConnections) {
    QMutexLocker locker(&m_mutex);
    if (m_isInitialized) {
        qWarning() << "连接池已初始化,无需重复调用";
        return;
    }

    // 保存配置
    m_type = type;
    m_ip = ip;
    m_port = port;
    m_dbName = dbName;
    m_username = username;
    m_password = password;
    m_maxConnections = maxConnections;
    m_minIdleConnections = minIdleConnections;

    // 预先创建最小空闲连接
    for (int i = 0; i < m_minIdleConnections; ++i) {
        QSqlDatabase db = createConnection();
        if (db.isOpen()) {
            m_connections.enqueue(db);
            m_totalConnections++;
        }
    }

    m_isInitialized = true;
}

QSqlDatabase DbConnectionPool::acquireConnection(int timeoutMs) {
    QMutexLocker locker(&m_mutex);
    if (!m_isInitialized) {
        qCritical() << "连接池未初始化,请先调用init()";
        return QSqlDatabase();
    }

    // 循环等待可用连接(超时退出)
    while (true) {
        // 1. 检查队列中是否有可用连接
        if (!m_connections.isEmpty()) {
            QSqlDatabase db = m_connections.dequeue();
            // 验证连接有效性(避免连接已断开)
            if (isConnectionValid(db)) {

                return db;
            }
            else {
                // 连接无效,销毁并减少总连接数
                m_totalConnections--;
                continue;
            }
        }

        // 2. 若未达最大连接数,创建新连接
        if (m_totalConnections < m_maxConnections) {
            QSqlDatabase db = createConnection();
            if (db.isOpen()) {
                m_totalConnections++;
                m_count++;
                return db;
            }
            else {
                qWarning() << "创建新连接失败,将重试";
                continue;
            }
        }

        // 3. 无可用连接且已达最大连接数,等待超时
        if (!m_waitCondition.wait(&m_mutex, timeoutMs)) {
            qWarning() << "获取连接超时(" << timeoutMs << "ms),当前总连接数:" << m_totalConnections;
            return QSqlDatabase();
        }
    }
}

void DbConnectionPool::releaseConnection(QSqlDatabase db) {
    if (!db.isValid()) {
        qWarning() << "归还无效连接,忽略";
        return;
    }

    QMutexLocker locker(&m_mutex);
    // 若连接已关闭,直接销毁
    if (!db.isOpen()) {
        m_totalConnections--;
        m_count--;
        return;
    }

    // 检查是否需要销毁(若总连接数超过最大,且空闲连接数超过最小)
    if (m_totalConnections > m_maxConnections || m_connections.size() >= m_minIdleConnections) {
        m_totalConnections--;
        m_count--;
        return;
    }

    // 否则放回队列
    m_connections.enqueue(db);
    m_waitCondition.wakeOne(); // 唤醒一个等待连接的线程
}

void DbConnectionPool::closeAllConnections() {
    QMutexLocker locker(&m_mutex);
    while (!m_connections.isEmpty()) {
        QSqlDatabase db = m_connections.dequeue();
        if (db.isOpen()) {
            db.close();
        }
        QString connName = db.connectionName();
        QSqlDatabase::removeDatabase(connName); // 彻底移除连接
    }
    m_totalConnections = 0;
    m_isInitialized = false;
}

QSqlDatabase DbConnectionPool::createConnection() {
    // 生成唯一连接名(避免冲突)
    QString connName = QString("conn_%1_%2").arg(int(QThread::currentThreadId())).arg(qrand());

    // 根据数据库类型创建连接
    QString driver;
    if (m_type == databaseType::QMYSQL) {
        driver = "QMYSQL";
    }
    else if (m_type == databaseType::QSQLITE) {
        driver = "QSQLITE";
    }
    else if (m_type == databaseType::QPSQL) {
        driver = "QPSQL";
    }
    else {
        qCritical() << "不支持的数据库类型";
        return QSqlDatabase();
    }

    QSqlDatabase db = QSqlDatabase::addDatabase(driver, connName);
    db.setHostName(m_ip);
    db.setPort(m_port);
    db.setDatabaseName(m_dbName);
    db.setUserName(m_username);
    db.setPassword(m_password);

    // 打开连接
    if (!db.open()) {
        qWarning() << "创建连接失败:" << db.lastError().text() << "(连接名:" << connName << ")";
        QSqlDatabase::removeDatabase(connName); // 清理失败的连接
        return QSqlDatabase();
    }

    return db;
}

bool DbConnectionPool::isConnectionValid(const QSqlDatabase& db) {
    if (!db.isOpen()) {
        return false;
    }
    // 执行简单查询验证连接(如MySQL可用"SELECT 1",SQLite可用"SELECT 1")
    QSqlQuery query(db);
    return query.exec("SELECT 1");
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐