最近学习测试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,这下都阻塞了:

我的mybatis测试代码中,因为上一个测试没有commit导致死锁,commit后就ok了。在这里,我想说,数据库的东西全还给老师了,关于锁以及事务需要重新温习一下了。

2.Mybatis中datasource的数据库连接数

当我mvn test的时候,我发现有个查询的test打印日志:

2016-07-21 23:43:53,356 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection
2016-07-21 23:43:53,356 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Waiting as long as 20000 milliseconds for connection.

于是,果然等了一段时间后才执行成功。跟踪源码,找到这处日志就明白了。首先,我这里使用的数据库连接配置是mybatis默认的:

<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>

当数据库连接池的连接数用光了之后就要等20s再去获取:

while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) {
// Pool has available connection
conn = 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 connection
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
log.debug("Bad connection. Could not roll back");
}
}
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
try {
if (!countedWait) {
state.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);
state.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;
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.");
}
}
}
} }

当连接数少于10个的时候回创建,超过10个就会等待,不然就报错。

Mybatis-update - 数据库死锁 - 获取数据库连接池等待的更多相关文章

  1. Java操作数据库——手动实现数据库连接池

    Java操作数据库——手动实现数据库连接池 摘要:本文主要学习了如何手动实现一个数据库连接池,以及在这基础上的一些改进. 部分内容来自以下博客: https://blog.csdn.net/soonf ...

  2. mybatis学习三 数据库连接池技术

    1.在内存中开辟一块空间,存放多个数据库连接对象.就是Connection的多个实例2. 连接池技术有很多,c3p0,dbcp,druid,以及JDBC Tomcat Pool, JDBC Tomca ...

  3. Spring Boot 集成 Mybatis(druid 数据库连接池 以及 分页配置)

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射,目前很大一部分互联网.软件公司都在使用这套框架 关于Mybatis-Generator的下载可以到这个地址:http ...

  4. Java开发工程师(Web方向) - 03.数据库开发 - 第2章.数据库连接池

    第2章--数据库连接池 数据库连接池 一般而言,在实际开发中,往往不是直接使用JDBC访问后端数据库,而是使用数据库连接池的机制去管理数据库连接,来实现对后端数据库的访问. 建立Java应用程序到后端 ...

  5. JDBC02 利用JDBC连接数据库【使用数据库连接池】

    目录 1/2/3  Statement 和 Preparedstatement 的区别 4 读取properties配置文件 5 数据库连接池 6 利用数据库连接池连接数据库 1 使用Statemen ...

  6. Java学习:数据库连接池技术

    本节内容 数据库连接池 Spring JDBC : JDBC Template 数据库连接池 1.概念:其实就是一个容器(集合),存放数据库连接的容器 当系统初始化好后,容器中会申请一些连接对象,当用 ...

  7. Tomcat6.0数据库连接池配置

    http://blog.163.com/magicc_love/blog/static/185853662201111101130969/ oracle驱动包Tomcat 6.0配置oracle数据库 ...

  8. node+mysql 数据库连接池

    1. 什么是数据库连接池? 数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个池,由程序动态地对池中的连接进行申请,使用和释放. 2. 使用数据库连接池原理及优点是什么? 数据库连接池 ...

  9. Tomcat 动态数据库连接池

    package com.boguan.bte.util; import java.sql.Connection;import java.sql.SQLException;import java.uti ...

随机推荐

  1. BSBuDeJie_04

    一 段子的下拉 建立模型 数字类型的用assign /* 当前页码 */ @property (nonatomic, assign) NSInteger page; 二 下拉上拉细节处理 三 细节处理 ...

  2. 用javascript实现一个2048游戏

    早就想自己写一个2048游戏了,昨晚闲着没事,终于写了一个 如下图,按方向键开始玩吧. 如果觉得操作不方便,请直接打开链接玩吧: http://gujianbo.1kapp.com/2048/2048 ...

  3. java接口中定义成员变量

    //抽象类中可以定义如下成员变量:public abstract class People { public String name; public int age; public abstract ...

  4. review简历之感想和建议

    最近帮很多朋友review他们的简历,总结起来存在以下问题: 1,简历太多页了.请尽量不超过两页.一般地,每个hr阅读简历的时间大概在20s甚至更少,写那么多页不仅毫无必要,而且有害. 而且我怀疑一般 ...

  5. MongoDB 初见指南

    技术若只如初见,那么还会踩坑么? 在系统引入 MongoDB 也有几年了,一开始是因为 MySQL 中有单表记录增长太快(每天几千万条吧)容易拖慢 MySQL 的主从复制.而这类数据增长迅速的流水表, ...

  6. 大白话讲解Promise(一)

    去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...

  7. 架构设计(ASP.NET MVC+Knockout+Web API+SignalR)

    最近忙于重构项目的架构设计,没有时间发博客,也没有时间回复邮件及博文评论,忘各位见谅: 今天先发布架构设计图,同样没有时间写相关的介绍也没有时间回复评论,所以就不发在首页,希望给看到的朋友一些参考,同 ...

  8. SSH实战 · SSH项目开发环境搭建

    一:SSH整合 创建一个新的WEB项目 引入struts2.3.15.3: jar包:                 struts-2.3.15.3\apps\struts2-blank.war\W ...

  9. sleep和wait区别

    1. sleep和wait都是用来进行线程控制,他们最大本质的区别是: sleep()不释放同步锁,wait()释放同步锁.               sleep(milliseconds)可以用时 ...

  10. c++头文件 #include<iostream>

    cout<<"C1="<<setiosflags(ios::fixed)<<setprecision(2)<<3.14*r*2< ...