Mybatis的连接池
先总结一个原则:
mytatis的连接池最大值poolMaximumActiveConnections尽量跟服务器的并发访问量持平以至于大于并发访问量。
原因:在org.apache.ibatis.datasource.pooled.PooledDataSource中,popConnection函数(获取连接)会锁住一个PoolState对象,pushConnection函数(把连接回收到池中,在关闭连接的时候,会调用PooledConnection的invoke函数<使用的代理模式,invoke是一个回调函数>,触发close函数时调用)也会锁住这个对象。
在popConnection的时候:
1.如果池中有idle的,返回之
2.如果没有,并且池中的active连接数小于配置的最大连接数,新建一个连接返回
3.如果没有idle并且连接数已经创建到最大,就不创建新连接。从acitve connection列表中返回一个最老的值state.activeConnections.get(0),看这个连接被取出的时间(check out时间,表示从连接开始使用到目前还未close)是不是超过poolMaximumCheckoutTime(配置项,默认是20秒),如果超过,使这个连接失效,并且使用这个连接返回做下一个操作
4.如果这个连接check out时间还未到poolMaximumCheckoutTime,调用state对象的wait函数:state.wait(poolTimeToWait);等待被唤醒(在连接close的时候会调用pushConnection函数,这里会调用state对象的notifyAll,唤醒之后重新进入循环取连接)
源代码比较长就不贴了,有兴趣的同学自己下下来看!
在并发数比连接池的数量大很多的情况下,会导致大量的排除竞争来同步state对象,开销比较大!会直接导致延时大大增加。
http://www.xuebuyuan.com/1676551.html
最近学习测试mybatis,单个增删改查都没问题,最后使用mvn test的时候发现了几个问题:
1.update失败,原因是数据库死锁
2.select等待,原因是connection连接池被用光了,需要等待
get:
1.要勇于探索,坚持就是胜利。刚看到错误的时候直接懵逼,因为错误完全看不出来,属于框架内部报错,在犹豫是不是直接睡觉得了,毕竟也快12点了。最后还是给我一点点找到问题所在了。
2.同上,要敢于去深入你不了解的代码,敢于研究不懂的代码。
3.距离一个合格的码农越来越远了,因为越学越觉得漏洞百出,自己的代码到处都是坑。所以,一定要记录下来。
下面记录这两个问题。
1.mysql数据库死锁
这里,感谢http://www.cnblogs.com/lin-xuan/p/5280614.html,我找到了答案。在这里,我还是重现一下:
数据库死锁是事务性数据库 (如SQL Server, MySql等)经常遇到的问题。除非数据库死锁问题频繁出现导致用户无法操作,一般情况下数据库死锁问题不严重。在应用程序中进行try-catch就可以。那么数据死锁是如何产生的呢?
InnoDB实现的是行锁 (row level lock),分为共享锁 (S) 和 互斥锁 (X)。
•共享锁用于事务read一行。
•互斥锁用于事务update或delete一行。
当客户A持有共享锁S,并请求互斥锁X;同时客户B持有互斥锁X,并请求共享锁S。以上情况,会发生数据库死锁。
如果还不够清楚,请看下面的例子。
双开两个mysql客户端
客户端A:
开启事务,并锁定共享锁S 在id=12的时候:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM blog WHERE id = 12 LOCK IN SHARE MODE;
+----+-------+-----------+
| id | name | author_id |
+----+-------+-----------+
| 12 | testA | 50 |
+----+-------+-----------+
1 row in set (0.00 sec)
客户端B:
开启事务,尝试删除id=12:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM blog WHERE id = 12;
删除操作需要互斥锁 (X),但是互斥锁X和共享锁S是不能相容的。所以删除事务被放到锁请求队列中,客户B阻塞。
这时候客户端A也想要删除12:
mysql> DELETE FROM blog WHERE id = 12;
Query OK, 1 row affected (0.00 sec)
和参考文章不同的是,居然删除成功了,但客户端B出错了:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
于是,我尝试删除13,带锁的客户端A可以正常删除,客户端B就报错:
我的mybatis测试代码中,因为上一个测试没有commit导致死锁,commit后就ok了。在这里,我想说,数据库的东西全还给老师了,关于锁以及事务需要重新温习一下了。
http://www.cnblogs.com/woshimrf/p/5693673.html
SET [GLOBAL | SESSION] TRANSACTION
transaction_characteristic [, transaction_characteristic] ... transaction_characteristic:
ISOLATION LEVEL level
| READ WRITE
| READ ONLY level:
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
附带一些mysql相关事务级别的相关信息:
In addition, SET TRANSACTION
can include an optional GLOBAL
or SESSION
keyword to indicate the scope of the statement.
Scope of Transaction Characteristics
You can set transaction characteristics globally, for the current session, or for the next transaction:
With the
GLOBAL
keyword, the statement applies globally for all subsequent sessions. Existing sessions are unaffected.With the
SESSION
keyword, the statement applies to all subsequent transactions performed within the current session.Without any
SESSION
orGLOBAL
keyword, the statement applies to the next (not started) transaction performed within the current session. Subsequent transactions revert to using theSESSION
isolation level.
A global change to transaction characteristics requires the SUPER
privilege. Any session is free to change its session characteristics (even in the middle of a transaction), or the characteristics for its next transaction.
SET TRANSACTION
without GLOBAL
or SESSION
is not permitted while there is an active transaction:
mysql>START TRANSACTION;
Query OK, 0 rows affected (0.02 sec) mysql>SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
ERROR 1568 (25001): Transaction characteristics can't be changed
while a transaction is in progress
To set the global default isolation level at server startup, use the --transaction-isolation=
option to mysqld on the command line or in an option file. Values of level
level
for this option use dashes rather than spaces, so the permissible values are READ-UNCOMMITTED
, READ-COMMITTED
,REPEATABLE-READ
, or SERIALIZABLE
. For example, to set the default isolation level to REPEATABLE READ
, use these lines in the [mysqld]
section of an option file:
[mysqld]
transaction-isolation = REPEATABLE-READ
It is possible to check or set the global and session transaction isolation levels at runtime by using the tx_isolation
system variable:
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
SET GLOBAL tx_isolation='REPEATABLE-READ';
SET SESSION tx_isolation='SERIALIZABLE';
Similarly, to set the transaction access mode at server startup or at runtime, use the --transaction-read-only
option or tx_read_only
system variable. By default, these are OFF
(the mode is read/write) but can be set to ON
for a default mode of read only.
Setting the global or session value of tx_isolation
or tx_read_only
is equivalent to setting the isolation level or access mode with SET GLOBAL TRANSACTION
or SET SESSION TRANSACTION
.
https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
MyISAM不支持
START TRANSACTION | BEGIN [WORK]
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
SET AUTOCOMMIT = {0 | 1}
START TRANSACTION或BEGIN语句可以开始一项新的事务。
COMMIT可以提交当前事务,是变更成为永久变更。
ROLLBACK可以 回滚当前事务,取消其变更。
SET AUTOCOMMIT语句可以禁用或启用默认的autocommit模式,用于当前连接。
自选的WORK关键词被支持,用于COMMIT和RELEASE,与CHAIN和RELEASE子句。CHAIN和RELEASE可以被用于对事务完成进行附加控制。Completion_type系统变量的值决定了默认完成的性质。
AND CHAIN子句会在当前事务结束时,立刻启动一个新事务,并且新事务与刚结束的事务有相同的隔离等级。RELEASE子句在终止了当前事务后,会让服务器断开与当前客户端的连接。包含NO关键词可以抑制CHAIN或RELEASE完成。如果completion_type系统变量被设置为一定的值,使连锁或释放完成可以默认进行,此时NO关键词有用。
默认情况下,MySQL采用autocommit模式运行。这意味着,当您执行一个用于更新(修改)表的语句之后,MySQL立刻把更新存储到磁盘中。
如果您正在使用一个事务安全型的存储引擎(如InnoDB, BDB或NDB簇),则您可以使用以下语句禁用autocommit模式:
SET AUTOCOMMIT=0;
通过把AUTOCOMMIT变量设置为零,禁用autocommit模式之后,您必须使用COMMIT把变更存储到磁盘中,或着如果您想要忽略从事务开始进行以来做出的变更,使用ROLLBACK。
如果您想要对于一个单一系列的语句禁用autocommit模式,则您可以使用START TRANSACTION语句:
START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summary=@A WHERE type=1;
COMMIT;
使用START TRANSACTION,autocommit仍然被禁用,直到使用COMMIT或ROLLBACK结束事务为止。
然后autocommit模式恢复到原来的状态。
BEGIN和BEGIN WORK被作为START TRANSACTION的别名受到支持,用于对事务进行初始化。
找来Jmeter做压力测试,结果这一测,暴露问题了,mysql返回"too many connections"这个error,这个error的具体解释参照这个链接http://dev.mysql.com/doc//refman/5.5/en/too-many-connections.html。
一般持久层与数据库连接都会通过一个连接池(pooled datasource)管理,方便复用连接,控制并发,比较有名的有DBCP,C3P0,BONECP等等。Mybatis3自己实现了一个连接池,在配置文件中指定datasource的type属性为POOLED即可使用。与并发关系较大的两个Mybatis连接池参数是poolMaximumActiveConnections和poolMaximumIdleConnections。
好了,出了问题,自然得找文档(官方手册,中英文皆有),poolMaximumActiveConnections是最大的活动连接数,活动连接,顾名思义,就是正在与数据库交互的连接,默认是10,poolMaximumIdleConnections是空闲连接数,就是没有处理请求的连接,默认是5。Mysql的max_connections我设置的是200,既最大连接数。这样一看,好像找不到问题所在,连接池最大的活动连接也就是10,跟200比还差很远,Mysql怎么会返回"too many connections"呢?在查阅文档无果后,我请教周围的一位同事,他虽然没用过Mybatis,但是他说是不是请求数超过poolMaximumActiveConnections后mybatis还会去获取连接,是不是有这样的参数控制,然后让我看看源码,说开源的东西嘛,搞不清楚就看源码。
我一向对源码抱有恐惧的心理,感觉那都是大神写的,我等屌丝怎能看得懂,不过被逼无奈,翻出Mybatis的源码看了一看,结果豁然开朗。找到org.apache.ibatis.datasource.pooled包下面的PooledDataSource类,这个就是连接池的实现类。可以看到里面定义了几个参数,其中就包括poolMaximumActiveConnections和poolMaximumIdleConnections,找到pushConnection方法,这个方法里会判断当前空闲连接数和poolMaximumIdleConnections的大小,如果小于他,会new PooledConnection并放进队列中,这就导致一个问题,当所有的连接被占满后,Mybatis为了保持一定的空闲连接,会不断获取新的连接,然后这些新连接被占用后,就会再去new PooledConnection,结果就是超过了mysql设置的最大连接数,然后数据库返回该错误。不知道这算不算是Mybatis的一个"坑"吧,总之在使用时要小心了,并发量大的时候就会爆掉你的数据库,解决办法很简单,将poolMaximumIdleConnections设置为0即可,果然改掉后压力测试不会爆掉数据库。
现在回想起来,官方文档对poolMaximumIdleConnections的定义是:在任意时间存在的空闲连接数,完全就解释了这个参数的含义,只不过当时没有仔细想,那这个参数是不是该改名字叫poolPermanentIdleConnections比较好呢,呵呵。
问题解决了,很开心,晚上回去再仔细读下里面的源码,看看还有没有别的没发现的问题。看来源码也不是想象中的那么神秘和高深啊。其实为什么那个同事一下就能看出问题的大概,一方面是经验丰富,另一方面可能与他理解数据库连接池机制有关,归根到底,基础的东西还是最重要的。
http://my.oschina.net/sniperLi/blog/550680
1.如何初始化连接池
在openSession的时候,mybatis才会new第一个连接,并放入连接池。
2.mybatis是如何返回一个连接的,先上代码
2.1.首先判断是否存在空闲的连接,存在则返回一个空闲的连接(mybatis用了很多if else,感觉可读性不是很好)。
2.2.接着判断连接数是否小于最大连接数,小于则new一个新的连接返回 。
2.3 .首先得到一个非空闲时间最长的连接,判断该连接用了多久,如果超过了一个连接的最大使用时间,则返回这个连接.
2.4 上面的都不成立,就等待一个时间(默认为poolTimeToWait=20000),然后继续上面的判断。
3. mybatis是如何将一个连接放回连接池的
是通过动态代理的方式,每次调用PooledConnection的时候,就会判断方法名是否为:close,如果是就将连接放入连接池。
3.mybatis会在程序结束的时候释放连接池里面的连接吗
不会的,不过还好并发数不搞的时候创建的连接很少。
如果做高并发的测试,那就要小心数据库爆掉..
http://www.xuebuyuan.com/1487139.html
org.apache.ibatis.datasource.pooled.PooledConnection
/*
* Required for InvocationHandler implementation.
*
* @param proxy - not used
* @param method - the method to be executed
* @param args - the parameters to be passed to the method
* @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
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;
} else {
try {
if (method.getDeclaringClass() != Object.class) {
// issue #578.
// toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
mybatis-3-mybatis-3.2.3
org.apache.ibatis.datasource.pooled.PooledDataSource
protected void pushConnection(PooledConnection conn) throws SQLException { synchronized (state) {
state.activeConnections.remove(conn);
if (conn.isValid()) {
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
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++;
}
}
}
jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
2016-08-16 10:12:10 [http-bio-8080-exec-8:57930] org.apache.ibatis.logging.LogFactory - [DEBUG] Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2016-08-16 10:12:10 [http-bio-8080-exec-8:57938] org.apache.ibatis.datasource.pooled.PooledDataSource - [DEBUG] PooledDataSource forcefully closed/removed all connections.
2016-08-16 10:12:10 [http-bio-8080-exec-8:57938] org.apache.ibatis.datasource.pooled.PooledDataSource - [DEBUG] PooledDataSource forcefully closed/removed all connections.
2016-08-16 10:12:10 [http-bio-8080-exec-8:57938] org.apache.ibatis.datasource.pooled.PooledDataSource - [DEBUG] PooledDataSource forcefully closed/removed all connections.
2016-08-16 10:12:10 [http-bio-8080-exec-8:57938] org.apache.ibatis.datasource.pooled.PooledDataSource - [DEBUG] PooledDataSource forcefully closed/removed all connections.
2016-08-16 10:12:10 [http-bio-8080-exec-8:58771] org.springframework.beans.factory.support.DefaultListableBeanFactory - [DEBUG] Eagerly caching bean 'sqlSessionFactory' to allow for resolving potential circular references
2016-08-16 10:12:10 [http-bio-8080-exec-8:58777] org.springframework.beans.factory.support.DefaultListableBeanFactory - [DEBUG] Finished creating instance of bean 'sqlSessionFactory'
2016-08-16 10:12:10 [http-bio-8080-exec-8:58833] org.apache.ibatis.transaction.jdbc.JdbcTransaction - [DEBUG] Opening JDBC Connection
2016-08-16 10:12:11 [http-bio-8080-exec-8:58973] org.apache.ibatis.datasource.pooled.PooledDataSource - [DEBUG] Created connection 863970198.
2016-08-16 10:12:11 [http-bio-8080-exec-8:58974] org.apache.ibatis.transaction.jdbc.JdbcTransaction - [DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@337f2396]
2016-08-16 10:12:11 [http-bio-8080-exec-8:58975] com.selectMediaList - [DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@337f2396]
2016-08-16 10:12:11 [http-bio-8080-exec-8:58975] com.selectMediaList - [DEBUG] ==> Preparing: set names utf8mb4; select now();
http://rhodian.iteye.com/blog/1930891
Mybatis的连接池的更多相关文章
- Mybatis 的连接池技术
我们在前面的 WEB 课程中也学习过类似的连接池技术,而在 Mybatis 中也有连接池技术,但是它采用的是自 己的连接池技术.在 Mybatis 的 SqlMapConfig.xml 配置文件中,通 ...
- Mybatis 打开连接池和关闭连接池性能对比
1 创建数据库表 -- phpMyAdmin SQL Dump -- version 4.2.11 -- http://www.phpmyadmin.net -- -- Host: localhos ...
- spring 5.x 系列第6篇 —— 整合 mybatis + druid 连接池 (代码配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- spring 5.x 系列第5篇 —— 整合 mybatis + druid 连接池 (xml配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- 阶段3 1.Mybatis_07.Mybatis的连接池及事务_6 mybatis中的事务原理和自动提交设置
在实际的开发中,建议使用连接池的形式. JNDI的资料 H:\BaiDu\黑马传智JavaEE57期 2019最新基础+就业+在职加薪\讲义+笔记+资料\主流框架\31.会员版(2.0)-就业课(2. ...
- 阶段3 1.Mybatis_07.Mybatis的连接池及事务_3 mybatis连接池的分类
2.mybatis中的连接池 mybatis连接池提供了3种方式的配置: 配置的位置: 主配置文件SqlMapConfig.xml中的dataSourc ...
- Mybatis之连接池
一,前言 连接池有很多种,最为熟悉的比如c3p0,DBCP,druid等. mybatis支持三种内置的数据源类型: Pooled:实现dataSource接口,并且使用了池的思想. UNPo ...
- Mybatis数据连接池的配置---增删改查(以及遇见的问题)
1.首先创建项目和各个文件,如图所示: 2.配置相关数据库连接 在jdbc.properties中加入 1 db.driver=com.mysql.jdbc.Driver 2 db.url=jdbc: ...
- mybatis 配置连接池
<!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidData ...
随机推荐
- QProcess进程间双向通信
记得以前写过Linux的C程序, 里面用popen打开一个子进程, 这样可以用read/write和子进程通讯, 而在子进程里则是通过从stdin读和向stdout写实现对父进程的通讯. QProce ...
- java设计模式--创建型模式--抽象工厂
什么是抽象工厂,再次学习. 抽象工厂 概述 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 适用性 1.一个系统要独立于它的产品的创建.组合和表示时. 2.一个系统要由多个产品系 ...
- Linux服务器挂死案例分析
问题现象: 在linux服务器上运行一个指定的脚本时,就会出现无数个相同进程的,而且不停的产生,杀也杀不掉,最后系统就陷入死循环,无法登陆,只能人工去按机器的电源键才可以.这够崩溃的吧? 问题分析过程 ...
- Tomcat启动load action异常
近期将之前的项目移到另一个文件夹中(包的路径也更改了),启动Tomcat之后包错:无法加载action,看错误提示知道是路径错误,网上也有各种各样的解决方案,这里我的错误是因为项目移到了别的文件中,所 ...
- July 【补题】
A(zoj 3596) bfs,记忆搜都可以, 按余数来记录状态. B(zoj 3599) 博弈,跳过 C(zoj 3592) 简单dp,题意不好懂 D(zoj 3602) 子树哈希, 对根的左右儿子 ...
- jumpGate部署
preface statement: manageing OpenStack & SoftLayer resource with Jumpgate 1,forward; Imagine a w ...
- hdu 3711 Binary Number(暴力 模拟)
Problem Description For non-negative integers x and y, f(x, y) , )=,f(, )=, f(, )=. Now given sets o ...
- 格而知之15:我所理解的Block(1)
1.Block 本质上是一个struct结构体,在这个结构体中,最重要的成员是一个函数(当然除函数外还有其他重要的成员). 2.在开始解析Block之前,首先来回顾一下Block的格式.Block相关 ...
- JConsole 连接配置
远程监控配置 JDK配置 在%JAVA_HOME%/jre/lib/management目录下,jmxremote.password.template.jmxremote.password需要修改配置 ...
- Ubuntu14.04配置cuda-convnet
转载请注明:http://blog.csdn.net/stdcoutzyx/article/details/39722999 在上一个链接中,我配置了cuda,有强大的GPU,自然不能暴殄天物,让资源 ...