一、什么是数据库连接池?

官方:
数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请、使用、释放。

白话:
创建数据库连接是一个很耗时的操作,也容易对数据造成安全隐患。所以在程序初始化的时候,集中创建多个数据库连接,并把它们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。


二、为什么需要数据库连接池?

》》先来看两张图的比较

----->
不使用连接池情景下,执行一条SQL语句需要的网络交互

go mysql的连接池 mysql连接池_连接池

每次执行一条SQL语句的网络交互有:
1)TCP建立连接的三次握手(客户端与mysql服务器的连接基于TCP协议)。
2) MySQL认证的三次握手。
3) 真正的SQL执行。
4) MySQL的关闭。
5) TCP的四次挥手关闭。

----->
使用连接池情景

go mysql的连接池 mysql连接池_数据库连接_02


第一次访问的时候需要建立连接,但是之后的访问,均会复用之前的创建的连接,直接执行SQL语句即可。

》》使用连接池的好处
1)资源重用
由于数据库连接得到重用,避免了频繁的创建、释放连接引起的性能开销,在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

2)更快的系统相应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了从数据库连接初始化和释放过程的开销,从而缩减了系统整体响应时间。

3)统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。


三、数据库连接池运行机制

》》连接池的运行机制

go mysql的连接池 mysql连接池_mysql_03

  • 从连接池获取或创建可用连接。
  • 使用完毕后,把连接返回给连接池。
  • 在系统关闭前,断开所有连接并释放占用的系统资源。

》》连接池与线程池的区别
连接池:被动使用。
线程池:主动不断的从队列中去执行任务。

》》MySQL连接需要的要素
主机IP、主机端口、用户名、密码。


四、连接池连接数设置

供参考计算公式:连接数=(核心数*2)+有效磁盘数


五、连接池的设计思路

》》连接池的设计思路
1)连接数据库,涉及到主机IP、主机端口、用户名、密码、数据库名称;连接数据库的操作,每个连接都是独立的。
2)需要一个队列管理连接,比如list。
3)请求连接资源。
4)归还连接资源。

》》设计一个好的连接池需要考虑哪些因素
1)重连机制、重连次数统计。
2)总的连接次数统计。
3)峰值连接次数,比如间隔1、5、15秒统计一次,连接峰值数量供后续的性能评估。
4)超时机制,阻塞、非阻塞模式等。


六、部分关键源码

》》创建多个连接

int CDBPool::Init()
{
	for (int i = 0; i < conn_num; i++)
	{
		CDBConn *pDBConn = new CDBConn(this);
		int ret = pDBConn->Init();
		if (ret)
		{
			delete pDBConn;
			return ret;
		}

		m_conn_list.push_back(pDBConn);
	}
	return 0;
}

》》连接数据库

CDBConn::CDBConn(CDBPool *pPool)
{
	m_pDBPool = pPool;
	m_mysql = NULL;
}

int CDBConn::Init()
{
	m_mysql = mysql_init(NULL);
	if (!m_mysql)
	{
		return 1;
	}

	my_bool reconnect = true;
	// 配合mysql_ping实现自动重连 
	//从MySQL 5.0.3开始默认情况下禁止再连接;这是5.0.13中的新选项,提供了一种以显式方式设置再连接行为的方法
	mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect);	
	mysql_options(m_mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4");

	if (!mysql_real_connect(m_mysql, m_pDBPool->GetDBServerIP(), m_pDBPool->GetUsername(), m_pDBPool->GetPasswrod(),
							m_pDBPool->GetDBName(), m_pDBPool->GetDBServerPort(), NULL, 0))
	{
		return 2;
	}
	return 0;
}