首页 > 编程知识 正文

数据库连接池的原理和作用,数据库连接池工作原理

时间:2023-05-06 13:05:21 阅读:175682 作者:926

前言pooleddatasourcepoolstatepooledconnection总结

前言

如在上文的《MyBatis原理——传统JDBC操作数据库》中讨论的,在MyBatis中,将非池化版本3358www.Sina.com/和池化版本3358www.Sina.com/发送到数据源http://www.Sina /

非池化版本相对简单,类似于使用传统的DataSource获取数据库连接,但增加了对MyBatis的一些打包和验证。 下面记录池化版本的实现。

让我们快速查看pooled data source 3358 www.Sina.com /的源代码。 这里只列出了主要的字段和构造函数。

publicclasspooleddatasourceimplementsdatasource//所有活动连接、空闲连接和一些相关参数privatefinalpoolstatestate=new poool //针对非池化版本的privatefinalunpooleddatasourcedatasource; 公共轮询数据源()数据源=newunpooleddatasource ); }其实我知道我有UnpooledDataSource内部没有池化的PooledDataSource,本身什么也没做。 具体真正获得连接等细节工作还是交给DriverManager吧。 但是,数据库连接池的主要作用是:

1 .获取连接时,如果连接池中存在空闲连接,则不获取新的物理连接;

2 .释放连接时,将其放回连接池,而不是关闭物理连接。

让我们看看MyBatis是如何解决这两个问题的:

PoolState如上所述,PooledDataSource具有PooledDataSource字段。 这才是真正保存连接的地方。 这个类的主要字段如下。

用public class PoolState { //连接的池化数据源protectedpooleddatasourcedatasource; //空闲连接protectedfinallistpooledconnectionidleconnections=new ArrayList (; //活动连接(工作中) protectedfinallistpooledconnectionactiveconnections=new ArrayList; 3358www.Sina.com/主要包括这两个DataSource,一个存储空闲连接,另一个存储活动连接。 它还包含相关的基本统计字段,如记录请求数和累计请求时间。 3358www.Sina.com/拥有它,您就可以根据现有配置和相关字段的统计信息,对这两个UnpooledDataSource进行操作,以实现连接池的功能。 列表中的PooledDataSource是MyBatis对基本数据库连接的封装。 首先,让我们简单看看PoolState是如何获得连接的。

@ overridepublicconnectiongetconnection (throws sqlexception ) returnpopconnection (data source.getusername ),data source . } privatepooledconnectionpopconnection (string username,String password ) throwssqlexception ) pooledconnectionn=null; while(conn==null )同步(state ) /如果存在空闲连接,则直接从空闲连接列表中选择if (! state.idleConnections.isEmpty ()//poolhasavailableconnectionconn=state.idle connections.remove ) 0; } else { //空闲连接if不存在(state.activeco

nnections.size() < poolMaximumActiveConnections) { // 创建新连接 conn = new PooledConnection(dataSource.getConnection(), this); } else { // Cannot create new connection// ... } } if (conn != null) {// 对连接做一些校验和配置// ...state.activeConnections.add(conn); state.requestCount++; } } } if (conn == null) { throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection."); } return conn; }

这个方法本身很长,删去了一些复杂情况,仅保留最简单的内容。可以看到其实就是对PoolState中两个列表的操作:

如果存在空闲连接,则直接从列表上取下来一个。如果不存在空闲连接,但是当前活跃连接数小于配置的最大连接数,那么新建一个连接,新建连接从本类持有的非池化DataSource获取:dataSource.getConnection()。并放到池中(state.activeConnections.add(conn);)。这一点类似JDK的线程池,有常驻线程数,最大线程数等配置信息。

当然还有其他情况,比如达到最大连接数,不能再新建了,那么就需要根据PooledDataSourcePoolState中的相关配置、统计信息采取策略了,比如替换最老的连接。这里不再赘述。

可以看到,这里popConnection 返回的是PooledConnection对象,getConnection()返回的,则是PooledConnection..getProxyConnection();一个代理连接。所以最后的奥妙就在PooledConnection中。

PooledConnection

惯例先看下PooledConnection类的主要字段和构造方法:

class PooledConnection implements InvocationHandler { private static final String CLOSE = "close"; private static final Class<?>[] IFACES = new Class<?>[] { Connection.class }; // 绑定的数据源 private final PooledDataSource dataSource; // 真实数据库连接 private final Connection realConnection; // 代理数据库连接 private final Connection proxyConnection; public PooledConnection(Connection connection, PooledDataSource dataSource) { this.hashCode = connection.hashCode(); this.realConnection = connection; this.dataSource = dataSource; this.createdTimestamp = System.currentTimeMillis(); this.lastUsedTimestamp = System.currentTimeMillis(); this.valid = true; this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this); } public Connection getProxyConnection() { return proxyConnection; }

可以看到PooledConnection其实持有“两个数据库连接”。在构造方法里,一个是真实的数据库连接,由构造方法的参数传递而来,该参数由PooledDataSource持有的UnpooledDataSource中获取。另一个则是对真实连接的包装(JDK动态代理),并将自己作为代理的实现者传递进去。紧接上边得知:最终是这个代理连接被从数据库连接池中取出,它实际承担了连接对象的责任。

PooledConnection本身实现了InvocationHandler接口,所以它对连接对象搞的鬼就都在实现的invoke方法中了:

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } try { if (!Object.class.equals(method.getDeclaringClass())) { // issue #579 toString() should never fail // throw an SQLException instead of a Runtime checkConnection(); } return method.invoke(realConnection, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }

代理方法意外的简单,大部分任务都直接委托给了真实的数据库连接(realConnection),仅在开头对close()方法做了拦截,如果当前调用的是close()方法,那么仅仅将本连接放入数据库连接池:dataSource.pushConnection(this);该push方法最终会将 连接再次放入到PoolState对象中。至此终于实现了连接池的功能。

总结 MyBatis的池化数据源PooledDataSource持有真实数据源UnpooledDataSource和连接池容器PoolState对象,获取连接时从连接池容器中调度获取,而当需要获取真实物理连接时,仍然是委托UnpooledDataSource来做。PoolState持有空闲连接和活跃连接两个列表,根据配置参数和统计信息来供PooledDataSource使用。PooledConnection借助动态代理创建一个代理连接,并同时持有代理连接和真实连接两者,返回代理连接来执行具体数据库任务。代理连接把大部分任务直接委托给了真实连接来做,而仅仅拦截close()方法,把自身放回到连接池容器(因为真实连接的close()方法是断开连接)。来达到”不断开,仅放回“的功能。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。