mybatis数据库连接池分析介绍

一、数据库连接池

1、定义

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。 (百度百科)

2、核心原理

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

2、java常用连接池

c3p0、dbcp、druid 等

二、mybatis数据库连接池

1、核心工作流程

mybatis所有的数据库操作都是通过执行器去执行的,在基础执行器BaseExecutor获取连接时最终会调用PooledDataSource的popConnection方法获取一个数据库连接。当然使用mybatis自带的数据库连接池的前提是mybatis配置的数据源类型配置的是POOLED类型,如果是别的类型或者换成了别的数据源是走不到PooledDataSource的popConnection方法的。

<dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="root"/></dataSource>
【1】获取数据库连接的核心类和方法

从连接池获取连接的方法

private PooledConnection popConnection(String username, String password) throws SQLException {boolean countedWait = false;PooledConnection conn = null;long t = System.currentTimeMillis();int localBadConnectionCount = 0;while (conn == null) {synchronized (state) {// 执行完,connection是何时挪到idleConnections集合中的--在连接关闭的时候// 如果空闲连接列表中存在空闲的连接,直接拿出来使用if (state.idleConnections.size() > 0) {// Pool has available connectionconn = state.idleConnections.remove(0);if (log.isDebugEnabled()) {log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");}} else {// Pool does not have available connection// 连接池中没有空闲的连接可用,且当前活动的连接数<池子最大的连接数,创建一个新的连接if (state.activeConnections.size() < poolMaximumActiveConnections) {// Can create new connectionconn = new PooledConnection(dataSource.getConnection(), this);@SuppressWarnings("unused")// used in logging, if enabledConnection realConn = conn.getRealConnection();if (log.isDebugEnabled()) {log.debug("Created connection " + conn.getRealHashCode() + ".");}} else {// Cannot create new connection// 且当前活动的连接数>池子最大的连接数, 不能继续创建连接PooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();// 最早的活动连接当前以执行的时间 > 最大检出时间(20S)if (longestCheckoutTime > poolMaximumCheckoutTime) {// Can claim overdue connection// 池子里面超时的连接个数+1state.claimedOverdueConnectionCount++;// 累计所有超时连接的所有超时时间,用来算超时连接的平均超时时间state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;// 用来算所有请求的平均超时时间state.accumulatedCheckoutTime += longestCheckoutTime;// 将这个执行超时的连接移除state.activeConnections.remove(oldestActiveConnection);// 手动提交进行回滚if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {oldestActiveConnection.getRealConnection().rollback();}// 将连接重新包装conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);// 老的超时的连接让其失效oldestActiveConnection.invalidate();if (log.isDebugEnabled()) {log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");}} else {// Must wait// 如果当前活跃的连接都没有执行超时,那么创建新连接的请求必须等待try {if (!countedWait) {// 池子中阻塞的数量+1state.hadToWaitCount++;countedWait = true;}if (log.isDebugEnabled()) {log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");}long wt = System.currentTimeMillis();// 等待阻塞时间(期间请求都会被阻塞),如果阻塞时间过了重新尝试获取连接state.wait(poolTimeToWait);// 累计阻塞时间state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}// 成功拿到连接if (conn != null) {if (conn.isValid()) {// 手动提交前先回滚先前的操作if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));// 设置当前连接的创建时间conn.setCheckoutTimestamp(System.currentTimeMillis());conn.setLastUsedTimestamp(System.currentTimeMillis());// 加入到活跃的连接集合中state.activeConnections.add(conn);// 请求数量+1state.requestCount++;// 累计请求时间state.accumulatedRequestTime += System.currentTimeMillis() - t;} else {// 连接失效if (log.isDebugEnabled()) {log.debug("A bad connection (" + conn.getRealHashCode()+ ") was returned from the pool, getting another connection.");}state.badConnectionCount++;localBadConnectionCount++;conn = null;// 本次请求连续8次都连接失效抛异常if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {if (log.isDebugEnabled()) {log.debug("PooledDataSource: Could not get a good connection to the database.");}throw new SQLException("PooledDataSource: Could not get a good connection to the database.");}}}}}// 获取不到连接抛异常if (conn == null) {if (log.isDebugEnabled()) {log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");}throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");}return conn;}
【2】关闭数据库连接时的操作

mybatis采用了jdk动态代理的方式在connection.close方法执行的时候实际上是调用的PooledDataSource的pushConnection方法,该方法没有直接关闭连接而是将连接加入到空闲连接的集合中以便后面复用。

/*** 功能描述: sql操作完后,关闭连接时的操作** @param conn* @return void* @see [相关类/方法](可选)* @since [产品/模块版本](可选)**/protected void pushConnection(PooledConnection conn) throws SQLException {synchronized (state) {// 从活跃的连接列表中移除state.activeConnections.remove(conn);// 判断连接是否有效if (conn.isValid()) {// 空闲的连接数量 < 最大空闲数量if (state.idleConnections.size() < poolMaximumIdleConnections&& conn.getConnectionTypeCode() == expectedConnectionTypeCode) {// 累计sql执行检出时间state.accumulatedCheckoutTime += conn.getCheckoutTime();if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}// 重新包装连接PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);// 加入到池子的空闲列表中state.idleConnections.add(newConn);// 设置新连接的创建时间newConn.setCreatedTimestamp(conn.getCreatedTimestamp());newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());conn.invalidate();if (log.isDebugEnabled()) {log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");}// 唤醒等待池子锁的所有线程state.notifyAll();} else {// 超过了最大的空闲连接数量,则关闭连接state.accumulatedCheckoutTime += conn.getCheckoutTime();if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}// 关闭连接conn.getRealConnection().close();if (log.isDebugEnabled()) {log.debug("Closed connection " + conn.getRealHashCode() + ".");}// 设置连接失效conn.invalidate();}} else {if (log.isDebugEnabled()) {log.debug("A bad connection (" + conn.getRealHashCode()+ ") attempted to return to the pool, discarding connection.");}// 失效的连接数累加state.badConnectionCount++;}}}

怎么看它采用的是动态代理呢?PooledDataSource中getConnection方法拿到的是连接的代理对象,代理对象去执行connection接口的close方法的时候并不是真正执行的close方法而是执行的PooledConnection的invoke方法

public Connection getConnection() throws SQLException {// 获取的是proxyConnectionreturn popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();}

invoke方法中针对Connection接口中的方法进行代理

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {// 如果是关闭连接没有直接closedataSource.pushConnection(this);return null;} else {// 不是关闭连接而是connection的其他操作try {if (!Object.class.equals(method.getDeclaringClass())) {// issue #579 toString() should never fail// throw an SQLException instead of a RuntimecheckConnection();}return method.invoke(realConnection, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}}

2、图解连接池工作流程

今天没时间了,后面再画下更新

三、总结

mybatis数据库连接池我们可以看到每次获取连接时一上来就把整个池子给锁了,这样的性能肯定有所降低,可以用常用的主流数据库连接池方案替换。

mybatis数据库连接池介绍和源码剖析相关推荐

  1. mybatis数据库连接池配置

    配置可修改参数db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/*XXX(数据库 ...

  2. mybatis数据库连接池

    mybatis作为一个ORM框架,在进行数据库操作时需要和数据库连接池连接,支持基于数据库连接池的连接创建方式. 目录 1.使用自带连接池 2.使用第三方数据库连接池 2.1添加依赖 2.2在util ...

  3. Mybatis连接池介绍与分类 Mybatis使用POOLED UNPOOLED配置连接池的原理分析

    一.连接池 1.概念:其实就是一个容器(集合),存放数据库连接的容器. 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象 ...

  4. mysql 连接池的作用,数据库连接池介绍、主要参数设置、作用

    数据库连接池 dataSource 1.平时用的数据库类型?? dbcp  c3p0 2.连接池需要那些配置?? 初始化连接数量initialSize 最大连接数maxActive 最大空闲连接数ma ...

  5. mysql连接池源码_一个JAVA数据库连接池实现源码

    原文链接:http://www.open-open.com/lib/view/open1410875608164.html // // 一个效果非常不错的JAVA数据库连接池. // from:htt ...

  6. hystrix 源码 线程池隔离_“池”的思想:从java线程池到数据库连接池的源码解读(1)...

    一. java线程池 带着问题: 线程是什么时候被创建的? 线程会一直循环取任务任务吗?怎么做的? 线程取不到任务会怎么样? 线程会被Runnable和Callable的异常干掉吗? 线程怎么干掉自己 ...

  7. docker container DNS配置介绍和源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 本文主要介绍了docker容器的DNS配置及其注意点,重点对docker 1.10发布的embedded DNS server ...

  8. Android开源框架PowerfulViewLibrary——PowerfulEditText的介绍和源码解析

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请注明出处:http://blog.csdn.net/chay_chan/article/details/63685905 An ...

  9. SLIC超像素分割的算法介绍和源码分析

    前述 最近在看显著性检测,发现很多算法的基础是超像素分割,而正在看的Saliency Optimization from Robust Background Detection算法的预处理是SLIC算 ...

最新文章

  1. BZOJ2831(小强的金字塔系列问题--区域整点数求法)
  2. html任务清单源码,JavaScript jQuery 任务清单 ToDoList
  3. 《Java程序设计》 第五周学习总结
  4. 三星集团和华为集团,哪个更厉害?
  5. Matrix(二维hash)
  6. Java开发学习必须了解的基础知识点
  7. zemax验证高斯公式_ZEMAX 实验讲义
  8. 1b8c语言,C语言 - 王朝网络 - wangchao.net.cn
  9. 基础知识 | 对目标检测认识及理解
  10. node html响应头,nodejs 中http请求头,响应头
  11. 2022届秋招笔试题小结:图
  12. 微信小程序 满意度调查问卷
  13. 微软发布了最新的Sync Framework 2.0 CTP2
  14. iOS 高德室内地图导航功能的简单实现
  15. 【XAMPP】phpMyAdmin安装和配置_解决修改密码后错误问题
  16. 求两个圆公切线的模板
  17. 三分钟集成 TapTap 防沉迷 SDK(Unity 版)
  18. jQuery添加单选多选题的代码
  19. CCF试题 201609-3 炉石传说
  20. Xxl Job Helloworld

热门文章

  1. composer:php的依赖管理工具
  2. C#获取%AppData%路径的方法
  3. java NIO 复习
  4. windows server 2003 32位支持8G内存
  5. 通过ISA发布服务器(二)
  6. Q91:真实地模拟透明材质(Realistic Transparency)
  7. 大数据架构由哪些模块组成
  8. linux常用ipc技术,LINUX系统编程之IPC
  9. goldendb mysql_golden数据库
  10. python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)