1 前言
连接池主要解决的问题就是,服务器对数据库的后半段的连接问题,这个过程连接对象是固定的,常规的连接完直接断开在连接建立时间浪费过长,同时连接需要多个连接同时工作来保证数据库的高效操作,提高服务器的响应时间。
2 连接池设计
2.1 连接池原理
连接池的主要是是完成服务器后半段的操作,对数据库进行操作的过程。一般场景下为了提高响应的速度,利用线程池处理多个连接同时进行连接操作。下面是连接池的整体示意图:
连接池的工作原理就是初始化的时候创建好连接,并加入到空闲队列;当有任务来的时候从空闲队列取出任务;利用连接池进行数据操作;最后使用完成后将连接归还队列,从而达到重复利用的功能。
这里涉及线程池和连接池操作,为了更好的理解,这里进行对比:
连接池:
(1)连接池本身并没有执行能力,需要配合线程池一起使用;
(2)连接池的数量与执行线程数量相关,一般1比1;
(3)连接池被动操作,池对象被任务获取,执行任务后归还。
线程池:
(1)线程池有自己的工作线程,可以自行消化任务;
(2)线程池数量与任务内容相关;
(3)线程池主动执行任务,执行完任务就销毁。
2.2 数据结构设计
连接池包含:一个空闲连接队列、锁和信号量;连接对象节点包括:数据库连接初始化函数和释放函数;连接对象本身。
typedef struct connecter {
db_init init;
db_denit denit;
void * conn;
struct connecter *prev;
struct connecter *next;
}connecter;
typedef struct con_pool {
connecter *cons;
int terminate;
pthread_cond_t cond;
pthread_mutex_t mtx;
}con_pool;
3 mysql连接过程
mysql连接操作是一个耗时的操作,分为四个步骤:一个是三次握手建立TCP连接;第二个是账号密码的人数在过程;第三是执行mysql语句;最后是关闭连接。连接池的作用就是把连接、认证和断开操作屏蔽,只保留mysql操作是时间,大大的缩短交互时间。
- 三次握手;
- 账号密码认证;
- 执行sql语句;
- 关闭四次挥手;
4 连接池的实现
4.1 连接池创建
连接池创建过程,主要是创建信号量和锁,同时创建连接。需要在连接前传入连接函数和数据库释放函数,包括完成连接的创建和连接好数据库。
int con_pool_create(con_pool *pool, int num_cons, db_init init, void *init_data, db_denit denit) {
//
if (pool == NULL) return -1;
if (num_cons < 1) num_cons = 1;
memset(pool, 0, sizeof(con_pool));
// cond
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));
// mutex
pthread_mutex_t blank_mtx = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->mtx, &blank_mtx, sizeof(pthread_mutex_t));
int idx = 0;
for (idx = 0;idx < num_cons;idx ++) {
connecter *con = (connecter *)malloc(sizeof(connecter));
if (con == NULL) {
perror("malloc");
return idx;
}
memset(con, 0, sizeof(connecter));
con->init = init;
con->denit = denit;
if(init(&con->conn, init_data))
{
free(con);
return -1;
}
LL_ADD(con, pool->cons);
}
return idx;
}
4.2 连接池销毁
连接池销毁过程,先要通知连接池暂停服务,关闭连接获取,同时释放连接,利用回调释放对应的数据库连接。
int con_pool_destroy(con_pool *pool) {
pool->terminate = 1;
pthread_mutex_lock(&pool->mtx);
pthread_cond_broadcast(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
connecter *con = pool->cons;
connecter *tmp = NULL;
while(con)
{
tmp = con;
con = con->next;
tmp->denit(tmp);
free(tmp);
}
}
4.3 连接获取
连接的获取是一个多线程操作,需要上锁,对连接队列没有可用连接时,目前采用的是等待到有连接再操作。后续也可以根据需要增加超时操作等等。连接获取完成后需要移除空闲队列,防止其他任务使用。
connecter *con_pool_get_con(con_pool *pool) {
pthread_mutex_lock(&pool->mtx);
while (pool->cons == NULL) {
if (pool->terminate) break;
pthread_cond_wait(&pool->cond, &pool->mtx);
}
if (pool->terminate) {
pthread_mutex_unlock(&pool->mtx);
return NULL;
}
connecter *con = pool->cons;
if (con) {
LL_REMOVE(con, pool->cons);
}
pthread_mutex_unlock(&pool->mtx);
if (con == NULL) return NULL;
return con;
}
4.4 连接归还
连接归还操作是在任务处理完成后归还连接,这里需要重新加入空闲队列,并且通知连接队列有数据加入,方便取出连接操作。
int con_pool_back_con(con_pool *pool, connecter *con) {
if(!pool || !con) return -1;
pthread_mutex_lock(&pool->mtx);
LL_ADD(con, pool->cons);
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
return 0;
}
5 测试
连接测试,采用10个线程和10个连接同时操作mysql插入1000条数据,具体用时1416ms