
数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
简单的说:创建数据库连接是一个很耗时的 *** 作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。
不使用数据库连接池
如果不使用数据库连接池,对于每一次SQL *** 作,都要走一遍下面完整的流程:
1.TCP建立连接的三次握手(客户端与 MySQL服务器的连接基于TCP协议)
2.MySQL认证的三次我收
3.真正的SQL执行
4.MySQL的关闭
5.TCP的四次握手关闭
可以看出来,为了执行一条SQL,需要进行大量的初始化与关闭 *** 作
使用数据库连接池
如果使用数据库连接池,那么会 事先申请(初始化)好 相关的数据库连接,然后在之后的SQL *** 作中会复用这些数据库连接, *** 作结束之后数据库也不会断开连接,而是将数据库对象放回到数据库连接池中
资源重用:由于数据库连接得到重用,避免了频繁的创建、释放连接引起的性能开销,在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
更快的系统响应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。 此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了从数据库连接初始化和释放过程的开销,从而缩减了系统整体响应时间。
统一的连接管理,避免数据库连接泄露:在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接 *** 作中可能出现的资源泄露。
如果说你的服务器CPU是4核i7的,连接池大小应该为((4*2)+1)=9
相关视频推荐
90分钟搞懂数据库连接池技术|linux后台开发
《tcp/ip详解卷一》: 150行代码拉开协议栈实现的篇章
学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
需要C/C++ Linux服务器架构师学习资料加qun 812855908 获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享
源码下载
下载方式:https://github.com/dongyusheng/csdn-code/tree/master/db_pool(Github中下载)
db_pool目录下有两个目录,mysql_pool目录为MySQL连接池代码,redis_pool为redis连接池代码
下面介绍mysql_pool
CDBConn解析
概念: 代表一个数据连接对象实例
相关成员:
m_pDBPool:该数据库连接对象所属的数据库连接池
构造函数: 绑定自己所属于哪个数据库连接池
Init()函数: 创建数据库连接句柄
CDBPool解析
概念:代表一个数据库连接池
相关成员:
Init()函数:常见指定数量的数据库实例句柄,然后添加到m_free_list中,供后面使用
GetDBConn()函数: 用于从空闲队列中返回可以使用的数据库连接句柄
RelDBConn()函数: 程序使用完该数据库句柄之后,将句柄放回到空闲队列中
测试之前,将代码中的数据库地址、端口、账号密码等改为自己的(代码中有好几处)
进入MySQL, 创建mysql_pool_test数据库
进入到mysql_pool目录下, 创建一个build目录并进入 :
然后输入如下的命令进行编译
之后就会在目录下生成如下的可执行文件
输入如下两条命令进行测试: 可以看到不使用数据库连接池,整个 *** 作耗时4秒左右;使用连接池之后,整个 *** 作耗时2秒左右,提升了一倍
源码下载
下面介绍redis_pool
测试
进入到redis_pool目录下, 创建一个build目录并进入 :
然后输入如下的命令进行编译
之后就会在目录下生成如下的可执行文件
输入如下的命令进行测试: 可以看到不使用数据库连接池,整个 *** 作耗时182ms;使用连接池之后,整个 *** 作耗时21ms,提升了很多
进入redis,可以看到我们新建的key:
数据库连接是最常用的提高性能的方法,总结了一下:1 定义数据库访问接口public interface IDatabaseConnection{
public abstract Connection createConnection()
public abstract void closeConnection(Connection conn)
}2 实现该接口2.1 直接连接,连接速度较慢,用户量较大时资源占用很大public Connection createConnection()
{
try
{
SystemParameter SP = new SystemParameter()
Class.forName(DBDRIVER).newInstance()
Connection con =
DriverManager.getConnection(
DBDRIVER_URL,
DBUSER,
DBPASSWORD)
return con
}
catch (ClassNotFoundException e)
{
e.printStackTrace()
return null
}
catch (SQLException e)
{ e.printStackTrace()
return null
}
catch (InstantiationException e)
{ e.printStackTrace()
return null
}
catch (IllegalAccessException e)
{ e.printStackTrace()
return null
}
}
public void closeConnection(Connection conn)
{
if(conn!=null) try
{
conn.close()
}
catch (SQLException e)
{
e.printStackTrace()
} }2.2 DBConnectionManager、DBConnectionPool方式这个连接池是网上比较多的连接池代码public class DBConnectionManager
{
static private DBConnectionManager instance/*唯一实例*/
static private int clients
/*该计数代表引用DBConnectionManager唯一实例的客户程序总数,
它将被用于控制连接池的关闭 *** 作 */
//Added by leo on 2001-5-23
static private String DEFAULTCONNECTIONPOOL = "homepage"private Vector drivers = new Vector()
private PrintWriter logPrint
private Hashtable pools = new Hashtable()
private SystemParameter sp = new SystemParameter()
/**
返回唯一实例。如果是第一次调用此方法,则创建实例
*/
static synchronized public DBConnectionManager getInstance()
{
if (instance == null)
{
instance = new DBConnectionManager()
}
clients++
return instance
} /**
建构函数私有以防止其它对象创建本类实例
*/
public DBConnectionManager()
{
init()
} /** Added by leo on 2001-5-23
使用默认的连接池名,创建并返回新连接
*
* @return Connection 可用连接或null
*/
public Connection getConnection()
{
return getConnection(DEFAULTCONNECTIONPOOL)
} /**
获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
限制,则创建并返回新连接
*
* @param name 在属性文件中定义的连接池名字
* @return Connection 可用连接或null
*/
public Connection getConnection(String name)
{
DBConnectionPool pool = (DBConnectionPool) pools.get(name)
if (pool != null)
{
return pool.getConnection()
}
return null
} /**
获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,
则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.
*
* @param name 连接池名字
* @param time 以毫秒计的等待时间
* @return Connection 可用连接或null
*/
public Connection getConnection(String name, long time)
{
DBConnectionPool pool = (DBConnectionPool) pools.get(name)
if (pool != null)
{
return pool.getConnection(time)
}
return null
} /** Added by leo on 2001-5-23
将连接对象返回给默认的连接池
*
* @param con 连接对象
*/
public void freeConnection(Connection con)
{
String name = DEFAULTCONNECTIONPOOL
freeConnection(name, con)
} /**
将连接对象返回给由名字指定的连接池
*
* @param name 在属性文件中定义的连接池名字
* @param con 连接对象
*/
public void freeConnection(String name, Connection con)
{
DBConnectionPool pool = (DBConnectionPool) pools.get(name)
if (pool != null)
{
pool.freeConnection(con)
}
} /**
关闭所有连接,撤销驱动程序的注册
*/
public synchronized void release()
{ // 等待直到最后一个客户程序调用
if (--clients != 0)
{
return
}
Enumeration allPools = pools.elements()
while (allPools.hasMoreElements())
{
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement()
pool.release()
}
Enumeration allDrivers = drivers.elements()
while (allDrivers.hasMoreElements())
{
Driver driver = (Driver) allDrivers.nextElement()
try
{
DriverManager.deregisterDriver(driver)
log("撤消JDBC驱动程序" + driver.getClass().getName() + "的注册")
}
catch (SQLException e)
{
log(e, "无法撤消下列JDBC驱动程序的注册" + driver.getClass().getName())
}
}
} /**
根据指定属性创建连接池实例.
*
* @param props 连接池属性
*/
private void createPools()
{
String poolName = sp.DBPOOLNAME
String url = sp.DBDRIVER_URL
String user = sp.DBUSER
String password = sp.DBPASSWORD
int max = sp.DBMAXCONN
DBConnectionPool pool =
new DBConnectionPool(poolName, url, user, password, max)
pools.put(poolName, pool)
log("成功创建连接池" + poolName)} /**
读取属性完成初始化
*/
private void init()
{ logPrint=new PrintWriter(System.out)
loadDrivers()
createPools()
} /**
装载和注册所有JDBC驱动程序
*
* @param props 属性
*/
private void loadDrivers()
{
String driverClassName = sp.DBDRIVER
try
{
Driver driver =
(Driver) Class.forName(driverClassName).newInstance()
DriverManager.registerDriver(driver)
drivers.addElement(driver)
log("成功JDBC注册驱动程序" + driverClassName)
}
catch (Exception e)
{
log("无法注册JDBC驱动程序;" + driverClassName + ",错误:" + e)
}
} /**
将文本信息写入日志文件
缺省为packaged
*/
void log(String msg)
{
logPrint.println(new java.util.Date() + ": " + msg)
} /**
将文本信息与异常写入日志文件
*/
void log(Throwable e, String msg)
{
logPrint.println(new java.util.Date() + ": " + msg)
e.printStackTrace(logPrint)
}}------------------------------------------------------class DBConnectionPool
{
private int checkedOut
private PrintWriter logPrint
private Vector freeConnections = new Vector()
private int maxConn
private String name
private String password
private String URL
private String user/**
创建新的连接池
*@param name 连接池名字
*@param URL 数据库的JDBC URL
*@param user 数据库帐号,或null
*@param password 密码,或null
*@param maxConn 此连接池允许建立的最大连接数
*/
public DBConnectionPool(String name, String URL, String user, String password, int maxConn)
{
this.name = name
this.URL = URL
this.user = user
this.password = password
this.maxConn = maxConn
logPrint = new PrintWriter(System.out)
} /**
将不再使用的连接返回给连接池
*@param con 客户程序释放的连接
*/
public synchronized void freeConnection(Connection con)
{
freeConnections.addElement(con)
checkedOut--
notifyAll()
} /**
从连接池获得一个可用连接,如没有空闲的连接且当前连接数小于最大连接数,则创建新连接,
如原来登记为可用的连接不再有效,则从向量删除之,然后递归调用自己以尝试新的可用连接
*/
public synchronized Connection getConnection()
{
Connection con = null
if(freeConnections.size() >0)
{// 获取向量中第一个可用连接
con = (Connection)freeConnections.firstElement()
freeConnections.removeElementAt(0)
try
{
if(con.isClosed())
{
log("从连接池" + name + "删除一个无效连接")
con = getConnection()
}
}
catch(SQLException e)
{
log("从连接池" + name + "删除一个无效连接")
con = getConnection()
}
}
else if(maxConn == 0 || checkedOut <maxConn)
{
con = newConnection()
}
if(con != null)
{
checkedOut++
}
return con
} /**
从连接池获取可用连接,可以指定客户程序能够等待的最长时间
*@param timeout 以毫秒计的等待时间限制
*/
public synchronized Connection getConnection(long timeout)
{
long startTime = new Date().getTime()
Connection con
while((con = getConnection()) == null)
{
try
{
wait(timeout)
}
catch(InterruptedException e){}
if((new Date().getTime() - startTime) >= timeout)
{
return null
}
}
return con
} /**
关闭所有连接
*/
public synchronized void release()
{
Enumeration allConnections = freeConnections.elements()
while(allConnections.hasMoreElements())
{
Connection con = (Connection)allConnections.nextElement()
try
{
con.close()
log("关闭连接池" + name + "中的一个连接")
}
catch(SQLException e)
{
log(e, "无法关闭连接池" + name + "中的连接")
}
}
freeConnections.removeAllElements()
} /**
创建新的连接
*/
private Connection newConnection()
{
Connection con = null
try
{
if(user == null)
{
con = DriverManager.getConnection(URL)
}
else
{
con = DriverManager.getConnection(URL, user, password)
}
log("连接池" + name + "创建一个新的连接")
}
catch(SQLException e)
{
log(e, "无法创建下列URL的连接" + URL)
return null
}
return con
} /**
将文本信息写入日志文件
缺省为packaged
*/
void log(String msg)
{
logPrint.println(new Date() + ": " + msg)
} /**
将文本信息与异常写入日志文件
*/
void log(Throwable e, String msg)
{
logPrint.println(new Date() + ": " + msg)
e.printStackTrace(logPrint)
}
}
---------------------------------DBConnectionManager connMgr = DBConnectionManager.getInstance()
public Connection createConnection()
{ Connection dBConn = connMgr.getConnection(new SystemParameter().DBPOOLNAME)
return dBConn
} public void closeConnection(Connection conn)
{
connMgr.freeConnection(new SystemParameter().DBPOOLNAME, conn)
connMgr.release()}
经过测试,使用这种方法,会造成数据库大量死锁,证明该连接池实现方法存在隐患也可能是因为它不属于webserver容器自身的,不具有可管理性。2.3 tomcat连接池public Connection createConnection()
{ try
{
Context initCtx = new InitialContext()
DataSource ds
ds = (DataSource) initCtx.lookup("java:comp/env/"+new SystemParameter().JDBC_SOURCE)
return ds.getConnection()
}
catch (NamingException e)
{ e.printStackTrace()
return null
}
catch (SQLException e)
{ e.printStackTrace()
return null
} } public void closeConnection(Connection conn)
{
if(conn!=null)try
{
conn.close()
conn=null
}
catch (SQLException e)
{
e.printStackTrace()
}
}2.4 weblogic连接池public Connection createConnection()
{
Context ctx = null
Hashtable ht = new Hashtable()
ht.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY)
ht.put(Context.PROVIDER_URL, URL)
// ht.put(Context.SECURITY_PRINCIPAL, "admin")
// ht.put(Context.SECURITY_CREDENTIALS, "11111111") Connection conn = null try
{
ctx = new InitialContext(ht)
DataSource ds = (DataSource) ctx.lookup(POOL_NAME)
return ds.getConnection()
}
catch (NamingException e)
{ e.printStackTrace()
}
catch (SQLException e)
{ e.printStackTrace()
}
return null} public void closeConnection(Connection conn)
{
if (conn != null)
try
{
conn.close()
conn = null
}
catch (SQLException e)
{e.printStackTrace()
} }3,4的方法比较好
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {// 超时重试次数,达到这个值就报错
int notFullTimeoutRetryCnt = 0
for () {
// handle notFullTimeoutRetry
DruidPooledConnection poolableConnection
try {
// 核心逻辑
poolableConnection = getConnectionInternal(maxWaitMillis)
} catch (GetConnectionTimeoutException ex) {
if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount &&!isFull()) {
notFullTimeoutRetryCnt++
if (LOG.isWarnEnabled()) {
LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt)
}
continue
}
throw ex
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)