五、数据库连接池:CGImysql文件夹下的sql_connection_pool.h和sql_connection_pool.cpp
基础知识
1. 数据连接池:
池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化。通俗来说,池是资源的容器,本质上是对资源的复用。
顾名思义,连接池中的资源为一组数据库连接,由程序动态地对池中的连接进行使用,释放。
当系统开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配;当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源。
2. 数据库访问的一般流程
3. 为什么创建连接池
如果需要频繁访问数据库,不创建连接池就只能反复的创建数据库连接和断开连接,而这是很耗时的,而且容易造成据库安全隐患。
在程序创建初期,集中创建数据连接池,需要使用数据库的时候就从连接池拿出,不需要就返还给连接池,这样便于集中管理,还能保证较快的数据库读写速度,更加安全可靠
整体概述
-
使用单例模式和链表创建数据库连接池,实现对数据库连接资源的复用。
-
项目中的数据库模块分为两部分,其一是数据库连接池的定义,其二是利用连接池完成登录和注册的校验功能。 【具体的,工作线程从数据库连接池取得一个连接,访问数据库中的数据,访问完毕后将连接交还连接池。】
代码实现
mysql基本函数
#include<mysql.h>
, 编译时要在后边链接库-lmysqlclient
MYSQL *con = NULL;
//初始化MYSQL变量,用于标记数据库MYSQL * mysql_init(MYSQL *mysql);
//初始化一个MYSQL 连接的实例对象,初始化失败会返回NULLvoid mysql_close(MYSQL *sock);
// 释放一个MYSQL 连接实例MYSQL *mysql_real_connect (MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)
MYSQL *为mysql_init函数返回的指针,
host为null或 localhost时链接的是本地的计算机,
当mysql默认安装在unix(或类unix)系统中,root账户是没有密码的,因此用户名使用root,密码为null,
当db为空的时候,函数链接到默认数据库,在进行 mysql安装时会存在默认的test数据库,因此此处可以使用 test数据库名称,
port端口为0,
使用 unix连接方式,unix_socket为null时,表明不使用socket或管道机制,最后一个参数经常设置为0例如:
int main() { MYSQL *con = NULL; con = mysql_init(con); if(con == NULL) { printf("MySQL error\n"); exit(1); } string url = "localhost"; string user = "zsz"; string password = "zsz"; string dbname = "test"; int port = 3306; con = mysql_real_connect(con,url.c_str(), user.c_str(), password.c_str(), dbname.c_str(), > port, NULL, 0); if (con == NULL) { printf("MySQL connect Error\n"); exit(1); } mysql_close(con); return 0; }
定义:
#ifndef _CONNECTION_POOL_
#define _CONNECTION_POOL_
#include <stdio.h>
#include <list>
#include <mysql/mysql.h>
#include <error.h>
#include <string.h>
#include <iostream>
#include <string>
#include "../lock/locker.h"
#include "../log/log.h"
class connect_pool
{
public:
MYSQL *GetConnection(); //获取数据库连接
bool ReleaseConnection((MYSQL *conn); //释放数据库连接
int GetFreeConn(); //获取连接
void DestoryPool(); //销毁所有连接
//静态单例模式
static connect_pool *GetInstance();
void init(std::string url, std::string user, std::string password, std::string DatabaseName, int port, int maxConn, int close_log);
private:
connect_pool();
~connect_pool();
int m_MaxConn; //最大连接数
int m_CurConn; //当前已使用的连接数
int m_FreeConn; //当前空闲的连接数
locker lock;
std::list<MYSQL *> connList; //连接池
sem reserve;
public:
string m_url; //主机地址
string m_Port; //数据库端口号
string m_User; //登陆数据库用户名
string m_PassWord; //登陆数据库密码
string m_DatabaseName; //使用数据库名
int m_close_log; //日志开关
};
//使用RAII的思想调用connect_pool
class connectionRAII
{
public:
connectionRAII(MYSQL **con, connection_pool *connPool);
~connectionRAII();
private:
//用于析构函数
MYSQL *conRAII;
connection_pool *poolRAII;
};
#endif _CONNECTION_POOL_
实现
#include <mysql/mysql.h> #include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <list> #include <pthread.h> #include <iostream> #include "sql_connection_pool.h" //构造函数 connection_pool::connection_pool() { m_CurConn = 0; m_FreeConn = 0; } ~connection_pool::connection_pool() { DestroyPool(); } void connection_pool::init(std::string url, std::string user, std::string password, > std::string DatabaseName, int port, int maxConn, int close_log) { m_url = url; //主机地址 m_Port = port; //数据库端口号 m_User = user; //登陆数据库用户名 m_PassWord = password; //登陆数据库密码 m_DatabaseName = DatabaseName; //使用数据库名 m_close_log = close_log; //日志开关 //建立maxConn个连接 for (int i = 0; i < maxConn; i++) { MYSQL *con = NULL; //初始化 con = mysql_init(con); if(con == NULL) { LOG_ERROR("MYSQL ERROR.") } con = mysql_real_connect(con, m_url.c_str(), m_User.c_str(), m_PassWord.c_str(), > m_DatabaseName.c_str(), m_Port, NULL, 0); //创建连接 if (con == NULL) { LOG_ERROR("MySQL Error"); exit(1); } //把数据库连接放入连接池 connList.push_back(con); ++m_FreeConn; } //初始化信号量,信号量的初始值为m_FreeConn,也就是数据库池连接池的容量 reserve = sem(m_FreeConn); //最大连接数就是申请到的个数 m_MaxConn = m_FreeConn; } //当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数 MYSQL *connection_pool::GetConnection() { MYSQL *con = NULL; //如果没有空闲的数据库连接,则会阻塞 reserve.wait(); //加锁的原因是防止右多个线程重复获取同样的MYSQL变量 lock.lock(); con = connList.front(); //弹出 connList.pop_front(); --m_FreeConn; ++m_CurConn; lock.unlock(); return con; } //释放当前使用的连接 bool connection_pool::ReleaseConnection(MYSQL *con) { if (NULL == con) return false; lock.lock(); connList.push_back(con); ++m_FreeConn; --m_CurConn; lock.unlock(); reserve.post(); return true; } //销毁数据库连接池 void connection_pool::DestroyPool() { lock.lock(); if (connList.size() > 0) { list<MYSQL *>::iterator it; //循环,一个一个关闭数据库连接 for (it = connList.begin(); it != connList.end(); ++it) { MYSQL *con = *it; mysql_close(con); } m_CurConn = 0; m_FreeConn = 0; connList.clear(); } lock.unlock(); } //当前空闲的连接数 int connection_pool::GetFreeConn() { return this->m_FreeConn; } connectionRAII::connectionRAII(MYSQL **con, connection_pool *connPool) { *con = connect_pool::GetInstance(); conRAII = *SQL; poolRAII = connPool; } ~connectionRAII::connectionRAII() { poolRAII->ReleaseConnection(conRAII) }