MySQL连接池
什么是数据库连接池?
定义:数据库连接池(Connection pooling)是程序启动时建立一定数量的数据库连接,并将这些连接组成 一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
**个人理解:**创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度, 还更加安全可靠。 这里讲的数据库,不单只是指Mysql,也同样适用于Redis。
为什么使用数据库连接池
- 资源复用:由于数据库的连接得到的复用,避免创建和销毁连接的性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
- 更快的系统响应速度:数据库连接池在初始化后,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了从数据库连接初始化和释放过程的开销,从而缩减了系统整体响应时间。
- 统一的连接管理,避免数据库连接泄露:在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。
不使用连接池执行一条sql语句的过程
- TCP建立连接的三次握手(客户端与MySQL服务器的连接基于TCP协议)
- MySQL认证的三次握手
- 真正的SQL执行
- MySQL的关闭
- TCP的四次挥手关闭
优点:实现简单
缺点: 网络IO较多 带宽利用率低 QPS较低 应用频繁低创建连接和关闭连接,导致临时对象较多,带来更多的内存碎片 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭)
使用连接池执行一条sql语句的过程
第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。
数据库连接池运行机制
从连接池获取或创建可用连接; 使用完毕之后,把连接返回给连接池; 在系统关闭前,断开所有连接并释放连接占用的系统资源;
线程池设计要点
使用连接池需要预先建立数据库连接。
线程池设计思路:
-
连接到数据库,涉及到数据库ip、端口、用户名、密码、数据库名字等;
-
配置最小连接数和最大连接数
-
需要一个队列管理他的连接,比如使用list;
-
-
连接的操作,每个连接对象都是独立的连接通道,它们是独立的
-
获取连接对象:
-
归还连接对象;
-
连接池的名字
连接池设计逻辑
- 构造函数
- 初始化
- 请求获取连接
- 归还连接
- 析构函数
- 连接池名
#include <list>
#include <string>
#include <mutex>
#include <mysql/mysql.h>
#include <queue>
#include <iostream>
#include <condition_variable>
using namespace std;
class MySqlPool
{
public:
MySqlPool(string pool_name, string db_server_ip, uint16_t db_server_port, string db_name,
string db_user, string db_password, int maxSize = 10)
: m_pool_name(pool_name), m_db_server_ip(db_server_ip), m_db_server_port(db_server_port), m_db_name(db_name),
m_db_user(db_user), m_db_password(db_password), maxSize(maxSize) {}
void Init() {
for (int i = 0; i < maxSize; ++i) {
m_free_pool.emplace_back(createConnection());
}
}
std::shared_ptr<MYSQL> getConn(size_t time_out_time) {
std::unique_lock<std::mutex> lock(m_mutex);
// 没有可用连接时
if (m_free_pool.empty()) {
// 根据当前连接数选择创建或等待
if (curSize == maxSize) {
// 等待且未设置超时时间
if (time_out_time <= 0) {
m_cond.wait(lock, [this] {
return (!m_free_pool.empty());
});
} else {
m_cond.wait_for(lock, std::chrono::milliseconds(time_out_time), [this] {
return (!m_free_pool.empty());
});
// 超时后再次检查
if (m_free_pool.empty()) {
return nullptr;
}
}
} else {
// 未达到最大连接数
std::shared_ptr<MYSQL> conn = createConnection();
m_free_pool.push_back(conn);
curSize++;
}
}
std::shared_ptr<MYSQL> conn = m_free_pool.front();
m_free_pool.pop_front();
m_busy_pool.push_back(conn);
return conn;
}
void returnConn(std::shared_ptr<MYSQL> conn) {
// 是否已归还
std::unique_lock<std::mutex> lock(m_mutex);
list<std::shared_ptr<MYSQL>>::iterator it = m_free_pool.begin();
for (; it != m_free_pool.end(); it++) {
if (*it == conn) {
cout << "Return failed." << endl;
return;
}
}
m_busy_pool.remove(conn);
m_free_pool.push_back(conn);
m_cond.notify_one();
}
private:
string m_pool_name;
string m_db_server_ip;
uint16_t m_db_server_port;
string m_db_name;
string m_db_user;
string m_db_password;
int maxSize;
int curSize;
list<std::shared_ptr<MYSQL>> m_free_pool;
list<std::shared_ptr<MYSQL>> m_busy_pool;
std::mutex m_mutex;
std::condition_variable m_cond;
std::shared_ptr<MYSQL> createConnection() {
MYSQL* conn = mysql_init(nullptr);
if (!mysql_real_connect(conn, m_db_server_ip.c_str(), m_db_user.c_str(), m_db_password.c_str(), m_db_name.c_str(),
m_db_server_port, nullptr, 0))
{
std::cerr << "mysql connection error: " << mysql_error(conn) << endl;
mysql_close(conn);
return nullptr;
}
// 自定义删除器
return std::shared_ptr<MYSQL>(conn, [](MYSQL* p) {
mysql_close(p);
});
}
};
int main() {
MySqlPool pool("mysql_pool", "127.0.0.1", 3306, "char", "ningao", "ning1020", 10);
auto conn = pool.getConn(100);
if (conn) {
// 执行你的MySQL操作
}
pool.returnConn(conn);
return 0;
}