5. 数据库连接池实现

WebServer 类中的 sql_pool() 方法,用于初始化数据库连接池并设置用户数据。

void WebServer::sql_pool()
{
    /* 初始化数据库连接池 */
    m_connPool = connection_pool::GetInstance();
    m_connPool->init("localhost", m_user, m_passWord, m_databaseName, 3306, m_sql_num, m_close_log);

    /* 初始化数据库读取表 */
    users->initmysql_result(m_connPool);
}

数据库连接池

connection_pool

sql_connection_pool.h中的connection_pool 类实现了一个 数据库连接池,用于管理数据库连接。它采用了 单例模式 来确保连接池在整个程序运行期间只有一个实例。

设置数据库连接池的目的是为了提高性能和减少数据库连接建立和销毁的开销

  1. GetInstance():实现单例模式,通过静态成员函数获取唯一的连接池实例。
  2. 使用信号量 reserve 控制连接的获取和释放,确保线程在连接不可用时会阻塞,直到有可用连接。
  3. 使用 互斥锁 用于多线程同步,保护对 connList 和连接计数器的操作,确保线程安全。
  4. 使用 std::list<std::shared_ptr<MYSQL>> 来保存连接池中的所有数据库连接。shared_ptr 用于自动管理连接的生命周期,确保资源在适当时被释放。
  5. connectionRAII 实现了 RAII(资源获取即初始化) 的模式,用于自动管理数据库连接的获取和释放。

connectionRAII

connectionRAII 类是一个典型的 RAII 模式实现,用于管理数据库连接的生命周期。它的作用是确保从连接池中获取的数据库连接能够在使用结束后自动释放,从而防止资源泄漏。保证每次从连接池中获取的连接在使用后都被正确归还。通过 RAII 模式,数据库连接的生命周期与 connectionRAII 对象绑定,确保不会有连接被无意遗忘。

这样设计的目的

数据库连接池的目的是为了管理数据库连接的复用,避免数据库连接频繁创建和销毁带来的额外开销,在一个多线程的应用程序中,数据库操作非常频繁,每次都建立和销毁数据库连接会带来较大的性能开销。使用 connection_pool 可以有效提高连接复用率,减少连接的开销。

RAII 机制的目的就是确保数据库连接能够安全的获取和归还。

例如,在一个 HTTP 服务器中处理客户端请求时,每个请求可能都需要访问数据库。使用 connectionRAII 可以确保每个请求在需要时从连接池中获取一个连接并在请求处理完毕后自动归还,从而提升服务器的并发性能和资源利用率。

改进

  1. 使用mysql_error() 提供的具体错误描述:
LOG_ERROR("MySQL init Error: %s", mysql_error(raw_con));
  1. 在创建多个连接的过程中,如果某个连接创建失败,原来的代码会直接退出exit(1);,导致所有已成功创建的连接也会被释放。
    改进: 考虑使用逐步回滚的方式来处理这种情况。 如果一个连接创建失败,可以先释放已经创建的连接,但是不退出程序。
for (int i = 0; i < MaxConn; i++)
{
    MYSQL *con = NULL;
    con = mysql_init(con);		/* 初始化 MYSQL 连接句柄 */

    if (con == NULL)
    {
        LOG_ERROR("MySQL init Error: %s", mysql_error(con));

        continue;  				/* 记录错误但不退出程序 */ 
    }

    /* 连接到数据库 */
    con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);

    if (con == NULL)
    {
        LOG_ERROR("MySQL init Error: %s", mysql_error(con));
        mysql_close(con);  		/* 释放当前连接资源 */
        continue;  				/* 记录错误但不退出程序 */
    }

    connList.push_back(con);	/* 将连接加入连接池 */
    ++m_FreeConn;				/* 增加空闲连接计数 */
}

/* 如果没有任何连接被成功创建 */ 
if (m_FreeConn == 0)
{
    LOG_ERROR("All database connections failed to initialize.");
    exit(1);  					/* 没有连接被创建成功,退出程序 */
}  
  1. 使用智能指针std::shared_ptr 来管理 MYSQL 连接 。std::shared_ptr 可以被多个对象共享,同时它会在最后一个持有者销毁时释放资源。
    改进:
    • 使用 std::shared_ptr<MYSQL> 替代原来的裸指针 MYSQL*
    • 将连接池的 connList 修改为存储 std::shared_ptr<MYSQL> 的列表。
  2. 原来的获取可用连接函数,如果数据库连接池为空,直接返回 null
MYSQL *connection_pool::GetConnection()
{
	MYSQL *con = NULL;

	if (0 == connList.size())
		return NULL;

	// ...
}

这样会导致返回一个无效的数据库连接,改进:使用信号量阻塞等待,直到连接池中存在可用连接。

std::shared_ptr<MYSQL> connection_pool::GetConnection()
{	
	reserve.wait();   				/* 阻塞等待信号量,确保有可用连接 */

	lock.lock();

	if (connList.empty())
    {
        lock.unlock();
        return nullptr;
    }

	std::shared_ptr<MYSQL> con = connList.front();
	connList.pop_front();

	--m_FreeConn;
	++m_CurConn;

	lock.unlock();

	return con;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红茶川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值