数据库连接池原理:
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:
外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
1. 资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。
连接池(ConnectionPool)与资源管理
ConnectionPool以缓冲池的机制,在一定数量上限范围内,控制管理Connection,Statement和ResultSet。任何数据库的资源是有限的,如果被耗尽,则无法获得更多的数据服务。
在大多数情况下,资源的耗尽不是由于应用的正常负载过高,而是程序原因。
在实际工作中,数据资源往往是瓶颈资源,不同的应用都会访问同一数据源。其中某个应用耗尽了数据库资源后,意味其他的应用也无法正常运行。因此,ConnectionPool的第一个任务是限制:每个应用或系统可以拥有的最大资源。也就是确定连接池的大小(PoolSize)。
ConnectionPool的第二个任务:在连接池的大小(PoolSize)范围内,最大限度地使用资源,缩短数据库访问的使用周期。许多数据库中,连接(Connection)并不是资源的最小单元,控制Statement资源比Connection更重要。以Oracle为例:
每申请一个连接(Connection)会在物理网络(如 TCP/IP网络)上建立一个用于通讯的连接,在此连接上还可以申请一定数量的Statement。同一连接可提供的活跃Statement数量可以达到几百。在节约网络资源的同时,缩短了每次会话周期(物理连接的建立是个费时的操作)。但在一般的应用中,多数按照2.1范例操作,这样有10个程序调用,则会产生10次物理连接,每个Statement单独占用一个物理连接,这是极大的资源浪费。 ConnectionPool可以解决这个问题,让几十、几百个Statement只占用同一个物理连接, 发挥数据库原有的优点。
通过ConnectionPool对资源的有效管理,应用可以获得的Statement总数到达 :
(并发物理连接数)×(每个连接可提供的Statement数量)
例如某种数据库可同时建立的物理连接数为 200个,每个连接可同时提供250个Statement,那么ConnectionPool最终为应用提供的并发Statement总数为: 200 × 250 = 50,000个。这是个并发数字,很少有系统会突破这个量级。所以在本节的开始,指出资源的耗尽与应用程序直接管理有关。
对资源的优化管理,很大程度上依靠数据库自身的JDBC Driver是否具备。有些数据库的JDBC Driver并不支持Connection与Statement之间的逻辑连接功能,如SQLServer,我们只能等待她自身的更新版本了。
对资源的申请、释放、回收、共享和同步,这些管理是复杂精密的。所以,ConnectionPool另一个功能就是,封装这些操作,为应用提供简单的,甚至是不改变应用风格的调用接口。
无论是Snap-ConnectionPool还是其他的数据库连接池,都应当具备一下基本功能:
-对源数据库资源的保护
-充分利用发挥数据库的有效资源
-简化应用的数据库接口,封闭资源管理。
-对应用遗留资源的自动回收和整理,提高资源的再次利用率。
在这个前提下,应用程序才能投入更多的精力于各自的业务逻辑中。数据库资源也不再成为系统的瓶颈。
连接池为了解决频繁的创建、销毁所带来的系统开销。
简而言之,就是 自己先创建一定量的连接,然后在需要的时候取出一条连接使用。
当然如果你只有一个线程连接数据库,而且不是实时返回结果,那么你完全不必用连接池。
想一下网络大型游戏服务器,你就明白为什么需要连接池了。
自己敲代码写了一个简单的类,实现连接池,虽然没有mysql++那么强大,但是还是自己有收获。
#pragma once
#include <WinSock2.h>
#include <mysql.h>
#include <list>
#pragma comment( lib , "libmysql.lib" )
using namespace std;
class Csqlpool
{
public:
~Csqlpool(void);
static Csqlpool *GetSqlPool();
bool IniSqlPool( const char *host , const char *name , const char *pwd , unsigned int port , unsigned int conMax ); //初始化连接池
bool SelectDB( MYSQL *sql, const char *DB); //选择数据库
MYSQL *GetConnect(); // 获取连接
void RelConnect(MYSQL *sql) ; // 释放连接
MYSQL_RES* GetQuery( MYSQL *sql , const char *query); //mysql操作 增删查改
void RelQuery(MYSQL_RES *res); //释放MYSQL_RES资源
bool Query(MYSQL *sql , const char *query); //增、删、改操作
protected:
Csqlpool(void);
private:
list<MYSQL *> m_sql_free; //空闲连接
static Csqlpool *pSqlPool;
CRITICAL_SECTION m_session; //获取空闲线程
};
#include "StdAfx.h"
#include "Csqlpool.h"
Csqlpool *Csqlpool::pSqlPool = NULL;
Csqlpool::Csqlpool(void)
{
InitializeCriticalSection( &m_session );
}
Csqlpool::~Csqlpool(void)
{
while ( m_sql_free.size() )
{
mysql_close( m_sql_free.front() );
m_sql_free.pop_front();
}
DeleteCriticalSection(&m_session);
}
Csqlpool* Csqlpool::GetSqlPool()
{
if ( pSqlPool == NULL )
{
return new Csqlpool;
}
return pSqlPool;
}
bool Csqlpool::IniSqlPool( const char *host ,const char *name , const char *pwd , unsigned int port , unsigned int conMax ) //初始化连接池
{
int nsum = 0 ;
for (unsigned int i = 0 ; i < conMax ;++i )
{
MYSQL *pmysql;
pmysql = mysql_init( (MYSQL*)NULL );
if ( pmysql != NULL )
{
if ( mysql_real_connect( pmysql , host , name , pwd , NULL , 3306 , NULL , 0 ) )
{
m_sql_free.push_back(pmysql);
}
else
{
if ( nsum++ == 100 )
{
return false;
}
continue;
}
}
continue;
}
return true;
}
bool Csqlpool::SelectDB( MYSQL *sql, const char *DB) //选择数据库
{
if(mysql_select_db(sql , DB))
{
return false;
}
return true;
}
MYSQL* Csqlpool::GetConnect() // 获取连接
{
if ( m_sql_free.size() )
{
EnterCriticalSection(&m_session);
MYSQL *mysql = m_sql_free.front();
m_sql_free.pop_front();
LeaveCriticalSection(&m_session);
return mysql;
}
else
return NULL;
}
void Csqlpool::RelConnect(MYSQL *sql) // 释放连接
{
EnterCriticalSection(&m_session);
m_sql_free.push_back(sql);
LeaveCriticalSection(&m_session);
}
MYSQL_RES* Csqlpool::GetQuery( MYSQL *sql , const char *query) //查询操作
{
if ( mysql_query( sql , query ) == 0 )
{
return mysql_store_result( sql );
}
else
return NULL;
}
void Csqlpool::RelQuery(MYSQL_RES *res) //mysql_res release
{
mysql_free_result(res);
}
bool Csqlpool::Query(MYSQL *sql , const char *query) //增、删、改操作
{
if ( mysql_query( sql , query ) )
{
return false;
}
return true;
}
// testsqlpool.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "Csqlpool.h"
#include <iostream>
using namespace std;
Csqlpool *psql = Csqlpool::GetSqlPool();
DWORD WINAPI ThreadProc( LPVOID lpParameter);
int _tmain(int argc, _TCHAR* argv[])
{
if(!psql->IniSqlPool("127.0.0.1" , "root" ,"123",3306,10))
{
cout<<"连接错误"<<endl;
}
HANDLE phan[2] ;
DWORD threadid[2];
int n1 = 0, n2 = 100;;
phan[0] = CreateThread( NULL , 0 , ThreadProc , &n1 , 0 , &threadid[0] );
phan[1] = CreateThread( NULL , 0 , ThreadProc , &n2 , 0 , &threadid[1] );
WaitForMultipleObjects( 2 , phan , true , INFINITE );
CloseHandle(phan[0]);
CloseHandle(phan[1]);
return 0;
}
DWORD WINAPI ThreadProc( LPVOID lpParameter)
{
int index = *(int *)lpParameter ;
int i = 1;
MYSQL *sql = psql->GetConnect();
string stemp = "insert into actor( actor_id , first_name , last_name,last_update )values(\"";
string strsql;
char str[10];
if ( psql->SelectDB(sql , "sakila") )
{
while ( i != 100 )
{
sprintf( str , "%d" , i+index );
strsql = stemp ;
strsql += str;
strsql += "\",\"0\",\"0\",\"0\")";
if(!sql)
return 0;
if(!psql->Query( sql ,strsql.c_str() ))
{
cout<<"add false"<<endl;
}
++i;
}
psql->RelConnect(sql);
}
return 0;
}