MySQL与Redis数据库连接池介绍(图示+源码+代码演示)

MySQL与Redis数据库连接池介绍(图示+源码+代码演示),第1张

数据库连接池(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

}


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/sjk/6760008.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-03-27
下一篇2023-03-27

发表评论

登录后才能评论

评论列表(0条)

    保存