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
类实现了一个 数据库连接池,用于管理数据库连接。它采用了 单例模式 来确保连接池在整个程序运行期间只有一个实例。
设置数据库连接池的目的是为了提高性能和减少数据库连接建立和销毁的开销。
GetInstance()
:实现单例模式,通过静态成员函数获取唯一的连接池实例。- 使用信号量
reserve
控制连接的获取和释放,确保线程在连接不可用时会阻塞,直到有可用连接。 - 使用 互斥锁 用于多线程同步,保护对
connList
和连接计数器的操作,确保线程安全。 - 使用
std::list<std::shared_ptr<MYSQL>>
来保存连接池中的所有数据库连接。shared_ptr
用于自动管理连接的生命周期,确保资源在适当时被释放。 connectionRAII
实现了 RAII(资源获取即初始化) 的模式,用于自动管理数据库连接的获取和释放。
connectionRAII
类
connectionRAII
类是一个典型的 RAII 模式实现,用于管理数据库连接的生命周期。它的作用是确保从连接池中获取的数据库连接能够在使用结束后自动释放,从而防止资源泄漏。保证每次从连接池中获取的连接在使用后都被正确归还。通过 RAII 模式,数据库连接的生命周期与 connectionRAII
对象绑定,确保不会有连接被无意遗忘。
这样设计的目的
数据库连接池的目的是为了管理数据库连接的复用,避免数据库连接频繁创建和销毁带来的额外开销,在一个多线程的应用程序中,数据库操作非常频繁,每次都建立和销毁数据库连接会带来较大的性能开销。使用 connection_pool
可以有效提高连接复用率,减少连接的开销。
RAII 机制的目的就是确保数据库连接能够安全的获取和归还。
例如,在一个 HTTP 服务器中处理客户端请求时,每个请求可能都需要访问数据库。使用 connectionRAII
可以确保每个请求在需要时从连接池中获取一个连接并在请求处理完毕后自动归还,从而提升服务器的并发性能和资源利用率。
改进
- 使用
mysql_error()
提供的具体错误描述:
LOG_ERROR("MySQL init Error: %s", mysql_error(raw_con));
- 在创建多个连接的过程中,如果某个连接创建失败,原来的代码会直接退出
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); /* 没有连接被创建成功,退出程序 */
}
- 使用智能指针
std::shared_ptr
来管理 MYSQL 连接 。std::shared_ptr
可以被多个对象共享,同时它会在最后一个持有者销毁时释放资源。
改进:- 使用
std::shared_ptr<MYSQL>
替代原来的裸指针MYSQL*
。 - 将连接池的
connList
修改为存储std::shared_ptr<MYSQL>
的列表。
- 使用
- 原来的获取可用连接函数,如果数据库连接池为空,直接返回
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;
}