Table of contents
2. Definition of database connection pool
3. Why use connection pooling?
4. Operating mechanism of database connection pool
5. The relationship between connection pool and thread pool
6.2.init——Initialize connection
8. Design points of database connection pool
9.2 Request to obtain connection
9.5 mysql reconnection mechanism
10. Setting the number of connection pools
1. Pooling technology
Pooling technology can reduce the number of creations of resource objects and improve the response performance of the program , especially under high concurrency This improvement is more obvious . Resource objects cached using pooling technology have the following common characteristics:
1. Object creation takes a long time;
2. Object creation requires a lot of resources;
3. Objects can be reused after creation
Common thread pools, memory pools, connection pools, and object pools all have the above common characteristics.
2. Definition of database connection pool
Database connection pooling (Connection pooling) is to establish enough database connections when the program starts , and form these connections into a connection pool , and the program dynamically applies for .
3. Why use connection pooling?
1. Reuse of resources
Since the database connection is reused, the performance overhead caused by frequent creation and release of connections is avoided. On the basis of reducing system consumption, it also improves the stability of the system operating environment (reduces memory fragmentation and database temporary processes/threads). quantity).
2. Faster response speed
During the initialization process of the database connection pool, several database connections have been created and placed in the pool for standby. For business request processing, existing available connections are directly used to avoid the overhead of database connection initialization and release process , thereby reducing response time and improving response speed.
3. Unified connection management to avoid database connection leakage
In a relatively complete database connection pool implementation, occupied connections can be forcibly reclaimed based on the preset connection occupation timeout setting. This avoids possible resource leaks that may occur during regular database connection operations.
4. Operating mechanism of database connection pool
1. Obtain or create available connections from the connection pool ;
2. After use, return the connection to the connection pool ;
3. Before shutting down the system, disconnect all connections and release the system resources occupied by the connections ;
5. The relationship between connection pool and thread pool
When a thread in the thread pool needs to access the database when executing a task, it needs to obtain an idle connection from the connection pool. After the task is processed, the thread returns the connection to the connection pool.
The difference between thread pool and connection pool:
Thread pool: active operation, actively obtain tasks and execute tasks
Connection pool: Passive operation, the pool object is acquired by the task and returned after the task is completed.
6. Design of CResultSet
// 返回结果 select的时候用
class CResultSet {
public:
CResultSet(MYSQL_RES* res);
virtual ~CResultSet();
bool Next();//获取下一行数据
int GetInt(const char* key); //获取int类型的value值
char* GetString(const char* key);//获取字符串类型的value值
private:
int _GetIndex(const char* key);
MYSQL_RES* m_res;
MYSQL_ROW m_row;
map<string, int> m_key_map;
};
6.1Constructor
//将所有行数据存储到 m_row中,将字段名存储到 m_key_map,并记录其id
CResultSet::CResultSet(MYSQL_RES *res)
{
m_res = res;
// map table field key to index in the result array
int num_fields = mysql_num_fields(m_res);
MYSQL_FIELD *fields = mysql_fetch_fields(m_res);
for (int i = 0; i < num_fields; i++)
{
// 将表头数据存储到m_key_map中
m_key_map.insert(make_pair(fields[i].name, i));
}
}
Get subscript based on field name
int CResultSet::_GetIndex(const char *key)
{
map<string, int>::iterator it = m_key_map.find(key);
if (it == m_key_map.end())
{
return -1;
}
else
{
return it->second;
}
}
//根据key 获取 int 类型 value值
int CResultSet::GetInt(const char *key)
{
int idx = _GetIndex(key);
if (idx == -1)
{
return 0;
}
else
{
return atoi(m_row[idx]); // 有索引
}
}
//根据 key值获取 字符串类型 value值
char *CResultSet::GetString(const char *key)
{
int idx = _GetIndex(key);
if (idx == -1)
{
return NULL;
}
else
{
return m_row[idx]; // 列
}
}
//获取下一行数据
bool CResultSet::Next()
{
m_row = mysql_fetch_row(m_res);
if (m_row)
{
return true;
}
else
{
return false;
}
}
7. Design of CDBConn
CDBConn is an autonomously encapsulated MySQL connection. The connection obtained from the connection pool is a CDBConn object type.
class CDBConn {
public:
CDBConn(CDBPool* pDBPool);
virtual ~CDBConn();
int Init();
// 创建表
bool ExecuteCreate(const char* sql_query);
// 删除表
bool ExecuteDrop(const char* sql_query);
// 查询
CResultSet* ExecuteQuery(const char* sql_query);
bool ExecuteUpdate(const char* sql_query, bool care_affected_rows = true);
uint32_t GetInsertId();
// 开启事务
bool StartTransaction();
// 提交事务
bool Commit();
// 回滚事务
bool Rollback();
// 获取连接池名
const char* GetPoolName();
MYSQL* GetMysql() { return m_mysql; }
private:
CDBPool* m_pDBPool; // to get MySQL server information
MYSQL* m_mysql; // 对应一个连接
};
6.1.Constructor
CDBConn::CDBConn(CDBPool *pPool)
{
m_pDBPool = pPool;
m_mysql = NULL;
}
6.2.init——Initialize connection
int CDBConn::Init()
{
m_mysql = mysql_init(NULL); // mysql_标准的mysql c client对应的api
if (!m_mysql)
{
log_error("mysql_init failed\n");
return 1;
}
my_bool reconnect = true;
mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect); // 配合mysql_ping实现自动重连
mysql_options(m_mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4"); // utf8mb4和utf8区别
// ip 端口 用户名 密码 数据库名
if (!mysql_real_connect(m_mysql, m_pDBPool->GetDBServerIP(), m_pDBPool->GetUsername(), m_pDBPool->GetPasswrod(),
m_pDBPool->GetDBName(), m_pDBPool->GetDBServerPort(), NULL, 0))
{
log_error("mysql_real_connect failed: %s\n", mysql_error(m_mysql));
return 2;
}
return 0;
}
8. Design points of database connection pool
Connection pool design ideas:
1. Connect to the database, involving database IP, port, user name, password, database name, etc.;
- Configure the minimum and maximum number of connections
2. Define a queue management connection
3. Get the interface of the connection object
4. Return the interface of the connection object
5. Define the name of the connection pool
class CDBPool { // 只是负责管理连接CDBConn,真正干活的是CDBConn
public:
CDBPool() {}
CDBPool(const char* pool_name, const char* db_server_ip, uint16_t db_server_port,
const char* username, const char* password, const char* db_name,
int max_conn_cnt);
virtual ~CDBPool();
int Init(); // 连接数据库,创建连接
CDBConn* GetDBConn(const int timeout_ms = 0); // 获取连接资源
void RelDBConn(CDBConn* pConn); // 归还连接资源
const char* GetPoolName() { return m_pool_name.c_str(); }
const char* GetDBServerIP() { return m_db_server_ip.c_str(); }
uint16_t GetDBServerPort() { return m_db_server_port; }
const char* GetUsername() { return m_username.c_str(); }
const char* GetPasswrod() { return m_password.c_str(); }
const char* GetDBName() { return m_db_name.c_str(); }
private:
string m_pool_name; // 连接池名称
string m_db_server_ip; // 数据库ip
uint16_t m_db_server_port; // 数据库端口
string m_username; // 用户名
string m_password; // 用户密码
string m_db_name; // db名称
int m_db_cur_conn_cnt; // 当前启用的连接数量
int m_db_max_conn_cnt; // 最大连接数量
list<CDBConn*> m_free_list; // 空闲的连接
list<CDBConn*> m_used_list; // 记录已经被请求的连接
std::mutex m_mutex;
std::condition_variable m_cond_var;
bool m_abort_request = false;
// CThreadNotify m_free_notify; // 信号量
};
9.Interface design
9.1 Constructor
CDBPool::CDBPool(const char *pool_name, const char *db_server_ip, uint16_t db_server_port,
const char *username, const char *password, const char *db_name, int max_conn_cnt)
{
m_pool_name = pool_name;
m_db_server_ip = db_server_ip;
m_db_server_port = db_server_port;
m_username = username;
m_password = password;
m_db_name = db_name;
m_db_max_conn_cnt = max_conn_cnt; //
m_db_cur_conn_cnt = MIN_DB_CONN_CNT; // 最小连接数量
}
9.2 I nit initialization
Create a fixed minimum number of connections and store the connections in the idle queue.
int CDBPool::Init()
{
// 创建固定最小的连接数量
for (int i = 0; i < m_db_cur_conn_cnt; i++)
{
CDBConn *pDBConn = new CDBConn(this);
int ret = pDBConn->Init();
if (ret)
{
delete pDBConn;
return ret;
}
m_free_list.push_back(pDBConn);
}
// log_info("db pool: %s, size: %d\n", m_pool_name.c_str(), (int)m_free_list.size());
return 0;
}
9.2 Request to obtain connection
Please connect the process
CDBConn *CDBPool::GetDBConn(const int timeout_ms)
{
std::unique_lock<std::mutex> lock(m_mutex);
//m_abort_request 判断连接池是否被终止
if(m_abort_request)
{
log_warn("have aboort\n");
return NULL;
}
if (m_free_list.empty()) // 当没有连接可以用时
{
// 第一步先检测 当前连接数量是否达到最大的连接数量
if (m_db_cur_conn_cnt >= m_db_max_conn_cnt)
{
// 如果已经到达了,看看是否需要超时等待
if(timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出
{
log_info("wait ms:%d\n", timeout_ms);
m_cond_var.wait(lock, [this]
{
// log_info("wait:%d, size:%d\n", wait_cout++, m_free_list.size());
// 当前连接数量小于最大连接数量 或者请求释放连接池时退出
return (!m_free_list.empty()) | m_abort_request;
});
} else {
// return如果返回 false,继续wait(或者超时), 如果返回true退出wait
// 1.m_free_list不为空
// 2.超时退出
// 3. m_abort_request被置为true,要释放整个连接池
m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] {
// log_info("wait_for:%d, size:%d\n", wait_cout++, m_free_list.size());
return (!m_free_list.empty()) | m_abort_request;
});
// 带超时功能时还要判断是否为空
if(m_free_list.empty()) // 如果连接池还是没有空闲则退出
{
return NULL;
}
}
if(m_abort_request)
{
log_warn("have aboort\n");
return NULL;
}
}
else // 还没有到最大连接则创建连接
{
CDBConn *pDBConn = new CDBConn(this); //新建连接
int ret = pDBConn->Init();
if (ret)
{
log_error("Init DBConnecton failed\n\n");
delete pDBConn;
return NULL;
}
else
{
m_free_list.push_back(pDBConn);
m_db_cur_conn_cnt++;
// log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);
}
}
}
CDBConn *pConn = m_free_list.front(); // 获取连接
m_free_list.pop_front(); // STL 吐出连接,从空闲队列删除
m_used_list.push_back(pConn);//将连接存放到被使用的 链表中
return pConn;
}
9.3 Release the connection
void CDBPool::RelDBConn(CDBConn *pConn)
{
std::lock_guard<std::mutex> lock(m_mutex);
list<CDBConn *>::iterator it = m_free_list.begin();
for (; it != m_free_list.end(); it++) // 避免重复归还
{
if (*it == pConn)
{
break;
}
}
if (it == m_free_list.end())
{
m_used_list.remove(pConn);
m_free_list.push_back(pConn);
m_cond_var.notify_one(); // 通知取队列
} else
{
log_error("RelDBConn failed\n");
}
}
9.4 Destruction connection
CDBPool::~CDBPool()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_abort_request = true;
m_cond_var.notify_all(); // 通知所有在等待的线程
for (list<CDBConn *>::iterator it = m_free_list.begin(); it != m_free_list.end(); it++)
{
CDBConn *pConn = *it;
delete pConn;
}
m_free_list.clear();
}
9.5 mysql reconnection mechanism
1.Set the reconnection mechanism properties
//1. 设置启用(当发现连接断开时的)自动重连, 配合mysql_ping实现自动重连
my_bool reconnect = true;
mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect);
2. Check whether the connection is normal
int STDCALL mysql_ping(MYSQL *mysql);
describe:
Check whether the connection to the server is normal. When the connection is lost, if automatic reconnection is not disabled, attempts to reconnect to the server . This function can be used by the client to detect whether the connection to the server is closed after being idle for a long time, and reconnect if necessary.
return value:
If the connection is normal, 0 is returned; if an error occurs, a non-0 value is returned. Returning a non-zero value does not mean that the server itself is shut down. It may also be that the network is blocked due to network reasons.
10. Setting the number of connection pools
empirical formula
Number of connections = ((number of cores * 2) + number of valid disks)
According to this formula, that is to say, your server CPU is a 4-core i7, then the number of connection pool connections should be ((4*2)+1)=9
This is just an empirical formula. Should it be combined with the number of thread pools and services? According to the empirical formula, the connection number increases and decreases to test the connection pool performance bottleneck.
actual analysis
If the task as a whole is an IO-intensive task . In the process of processing a request (processing a task) , it takes a total of 100+5=105ms , of which only 5ms is used for calculation operations (consuming CPU) , and the other 100ms is waiting for io response, and the CPU utilization rate is 5/ (100+5).
The purpose of using the thread pool is to maximize the CPU utilization and reduce the waste of CPU resources . Assuming 100% CPU utilization, to achieve 100% CPU utilization , the reciprocal of its utilization must be set for a CPU. The number of threads is 1/(5/(100+5))=21. If there are 4 CPUs, multiply by 4. So calculated, it is... 84. At this time, the thread pool needs to set the number of threads to 84, and then the maximum number of connections in the connection pool is also set to 84 connections.