[server component]——The implementation principle of mysql connection pool

Table of contents

1. Pooling technology

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. Design of CResultSet

 6.1Constructor

7. Design of CDBConn

6.1.Constructor 

6.2.init——Initialize connection

8. Design points of database connection pool

9.Interface design

9.1 Constructor

9.2 Request to obtain connection 

9.3 Release the connection

9.4 Destruction connection

9.5 mysql reconnection mechanism

10. Setting the number of connection pools

empirical formula

actual analysis

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.

Guess you like

Origin blog.csdn.net/sjp11/article/details/132074778