数据库连接池——TinyWebServer: CGImysql文件夹下的sql_connection_pool.h和sql_connection_pool.cpp

五、数据库连接池:CGImysql文件夹下的sql_connection_pool.h和sql_connection_pool.cpp

基础知识

1. 数据连接池:

池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化。通俗来说,池是资源的容器,本质上是对资源的复用。

顾名思义,连接池中的资源为一组数据库连接,由程序动态地对池中的连接进行使用,释放。

当系统开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配;当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源。

2. 数据库访问的一般流程

创建数据库连接
完成数据库操作
断开数据库连接

3. 为什么创建连接池

如果需要频繁访问数据库,不创建连接池就只能反复的创建数据库连接和断开连接,而这是很耗时的,而且容易造成据库安全隐患。

在程序创建初期,集中创建数据连接池,需要使用数据库的时候就从连接池拿出,不需要就返还给连接池,这样便于集中管理,还能保证较快的数据库读写速度,更加安全可靠

整体概述

  • 使用单例模式和链表创建数据库连接池,实现对数据库连接资源的复用。

  • 项目中的数据库模块分为两部分,其一是数据库连接池的定义,其二是利用连接池完成登录和注册的校验功能。 【具体的,工作线程从数据库连接池取得一个连接,访问数据库中的数据,访问完毕后将连接交还连接池。】

代码实现

mysql基本函数

  1. #include<mysql.h>, 编译时要在后边链接库 -lmysqlclient
  2. MYSQL *con = NULL; //初始化MYSQL变量,用于标记数据库
  3. MYSQL * mysql_init(MYSQL *mysql);//初始化一个MYSQL 连接的实例对象,初始化失败会返回NULL
  4. void mysql_close(MYSQL *sock); // 释放一个MYSQL 连接实例
  5. 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)
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果你不想使用 `pool.size()` 和 `pool._used` 属性来查看当前连接池的连接数,你可以通过以下方式来实现: ```python import psycopg2 from psycopg2 import pool pool = psycopg2.pool.SimpleConnectionPool(minconn=1, maxconn=10, user="your_username", password="your_password", host="your_host", port=your_port, database="your_database") # 使用 getconn() 获取一个连接 conn = pool.getconn() # 使用 getconn() 方法获取连接时,连接池会自动管理连接数,因此无法直接获取当前连接数 # 但是可以通过连接的状态来判断连接数是否达到最大值 try: # 使用 with 语句来自动管理连接的生命周期 with conn: # 如果连接数已经达到最大值,这里会抛出异常 # 因为 SimpleConnectionPool 是一个线程安全的连接池,所以在使用连接之前需要对连接进行加锁 # 这里使用 conn.lock.acquire() 来获取锁 conn.lock.acquire() conn.lock.release() except pool.PoolError: # 如果连接数已经达到最大值,这里会抛出异常 print("连接数已经达到最大值") # 使用 putconn() 将连接放回连接池中 pool.putconn(conn) ``` 在上面的代码中,我们通过 `try...except` 语句来判断连接数是否已经达到最大值。使用 `getconn()` 方法获取连接时,连接池会自动管理连接数。但是在使用连接之前,我们需要对连接进行加锁,这样才能保证连接的线程安全。使用 `putconn()` 方法将连接放回连接池时,连接池会自动管理连接数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值