当程序中频繁访问数据库时,我们可以采用向内存池、线程池那样的池化技术,引入数据库连接池,减少申请和释放数据库连接的操作,从而提高性能。
在很多C++库中都集成了连接池,下面以Poco的mysql连接池为例进行简要的介绍,下面是代码示例,其他数据库连接池的使用也是类似。需要指定数据库连接的参数、最大最小连接数还有空闲时间。
#include <iostream>
#include "Poco/Data/MySQL/Connector.h"
#include "Poco/Data/MySQL/MySQLException.h"
#include "Poco/Data/SessionPool.h"
#include "Poco/Exception.h"
using namespace std;
using namespace Poco::Data::MySQL;
using namespace Poco::Data;
using namespace Poco;
using namespace Poco::Data::Keywords;
int main(int argc, char const* argv[]) {
Poco::Data::MySQL::Connector::registerConnector();
// 数据库配置参数
std::string g_db_info =
"host=localhost;port=3306;user=root;password=111111;db=testdb;"
"compress=true;auto-reconnect=true";
// 数据库连接池
// 这里设置连接池最小回话数量为8,最大回话数量为128
// 空闲时间为10秒,如果在连接池中空闲时间超过10秒则被回收
Poco::Data::SessionPool* session_pool = new Poco::Data::SessionPool(
Poco::Data::MySQL::Connector::KEY, g_db_info, 8, 128, 10);
int count = 100;
while (count--) {
try {
// Session session(Poco::Data::MySQL::Connector::KEY, g_db_info);
Session session(session_pool->get()); // 从连接池中获取一个session
std::string student_id("001");
std::string name;
session << "SELECT student_name from student where student_id = ?",
use(student_id), into(name), now;
std::cout << "name:" << name << std::endl;
session.close(); // <推荐> 显式关闭连接
} catch (Exception& e) {
std::cerr << e.displayText() << std::endl;
}
}
delete session_pool;
Poco::Data::MySQL::Connector::unregisterConnector();
return 0;
}
一般我们会把数据库连接池封装成一个全局的单例类来使用,如下
MysqlSessionPool.h
#pragma once
#include <cassert>
#include <string>
#include "Poco/Data/MySQL/Connector.h"
#include "Poco/Data/MySQL/MySQLException.h"
#include "Poco/Data/SessionPool.h"
#include "Poco/Exception.h"
using namespace Poco::Data::MySQL;
using namespace Poco::Data;
using namespace Poco;
using namespace std;
class MysqlSessionPool {
private:
Poco::Data::SessionPool* _session_pool = nullptr;
const std::string _db_info;
static const int _min_sessions = 1;
static const int _max_sessions = 32;
static const int _idle_time = 128;
public:
MysqlSessionPool()
: _db_info(
"host=localhost;port=3306;user=root;password=111111;db=testdb;"
"compress=true;auto-reconnect=true") {
Poco::Data::MySQL::Connector::registerConnector();
_session_pool = new Poco::Data::SessionPool(
Poco::Data::MySQL::Connector::KEY, _db_info, _min_sessions,
_max_sessions, _idle_time);
assert(_session_pool != nullptr);
}
~MysqlSessionPool() {
delete _session_pool;
// Poco::Data::MySQL::Connector::unregisterConnector();
}
Session get() { return _session_pool->get(); }
static MysqlSessionPool* GetInst(); //单例
};
MysqlSessionPool.cpp 这里用到poco的单例模板,用法比较简单,普通类成员增加一个静态函数即可。 然后用模板SingletonHolder定义一个holder即可。
#include "MysqlSessionPool.h"
#include "Poco/SingletonHolder.h"
using Poco::SingletonHolder;
static SingletonHolder<MysqlSessionPool> holder;
MysqlSessionPool* MysqlSessionPool::GetInst() { return holder.get(); }
调用
int main(int argc, char const* argv[]) {
int count = 100;
while (count--) {
try {
// 从连接池中获取一个session
Session session(MysqlSessionPool::GetInst()->get());
std::string student_id("001");
std::string name;
session << "SELECT student_name from student where student_id = ?",
use(student_id), into(name), now;
std::cout << "name:" << name << std::endl;
session.close(); // <推荐> 显式关闭连接
} catch (Exception& e) {
std::cerr << e.displayText() << std::endl;
}
}
return 0;
}
如果业务频繁访问数据库,而连接得不到及时的释放,那对数据库而言可能是一种灾难,有大量的数据库连接处于timewait状态,而这些连接资源是有限的。引入连接池之后,就可以避免这种问题,不过尽管我们设置了空闲时间,但是还是建议使用完毕之后,立刻显示的关闭数据库连接,这样才是最高效的。