最近学习测试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的时候:

  1. mysql> START TRANSACTION;
  2. Query OK, 0 rows affected (0.00 sec)
  3.  
  4. mysql> SELECT * FROM blog WHERE id = 12 LOCK IN SHARE MODE;
  5. +----+-------+-----------+
  6. | id | name | author_id |
  7. +----+-------+-----------+
  8. | 12 | testA | 50 |
  9. +----+-------+-----------+
  10. 1 row in set (0.00 sec)

客户端B:

开启事务,尝试删除id=12:

  1. mysql> START TRANSACTION;
  2. Query OK, 0 rows affected (0.00 sec)
  3.  
  4. mysql> DELETE FROM blog WHERE id = 12;

删除操作需要互斥锁 (X),但是互斥锁X和共享锁S是不能相容的。所以删除事务被放到锁请求队列中,客户B阻塞。

这时候客户端A也想要删除12:

  1. mysql> DELETE FROM blog WHERE id = 12;
  2. Query OK, 1 row affected (0.00 sec)

和参考文章不同的是,居然删除成功了,但客户端B出错了:

  1. ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

于是,我尝试删除13,这下都阻塞了:

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

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

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

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

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

  1. <environment id="development">
  2. <transactionManager type="JDBC"/>
  3. <dataSource type="POOLED">
  4. <property name="driver" value="${jdbc.driver}"/>
  5. <property name="url" value="${jdbc.url}"/>
  6. <property name="username" value="${jdbc.username}"/>
  7. <property name="password" value="${jdbc.password}"/>
  8. </dataSource>
  9. </environment>

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

  1. while (conn == null) {
  2. synchronized (state) {
  3. if (!state.idleConnections.isEmpty()) {
  4. // Pool has available connection
  5. conn = state.idleConnections.remove(0);
  6. if (log.isDebugEnabled()) {
  7. log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
  8. }
  9. } else {
  10. // Pool does not have available connection
  11. if (state.activeConnections.size() < poolMaximumActiveConnections) {
  12. // Can create new connection
  13. conn = new PooledConnection(dataSource.getConnection(), this);
  14. if (log.isDebugEnabled()) {
  15. log.debug("Created connection " + conn.getRealHashCode() + ".");
  16. }
  17. } else {
  18. // Cannot create new connection
  19. PooledConnection oldestActiveConnection = state.activeConnections.get(0);
  20. long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
  21. if (longestCheckoutTime > poolMaximumCheckoutTime) {
  22. // Can claim overdue connection
  23. state.claimedOverdueConnectionCount++;
  24. state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
  25. state.accumulatedCheckoutTime += longestCheckoutTime;
  26. state.activeConnections.remove(oldestActiveConnection);
  27. if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
  28. try {
  29. oldestActiveConnection.getRealConnection().rollback();
  30. } catch (SQLException e) {
  31. log.debug("Bad connection. Could not roll back");
  32. }
  33. }
  34. conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
  35. oldestActiveConnection.invalidate();
  36. if (log.isDebugEnabled()) {
  37. log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
  38. }
  39. } else {
  40. // Must wait
  41. try {
  42. if (!countedWait) {
  43. state.hadToWaitCount++;
  44. countedWait = true;
  45. }
  46. if (log.isDebugEnabled()) {
  47. log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
  48. }
  49. long wt = System.currentTimeMillis();
  50. state.wait(poolTimeToWait);
  51. state.accumulatedWaitTime += System.currentTimeMillis() - wt;
  52. } catch (InterruptedException e) {
  53. break;
  54. }
  55. }
  56. }
  57. }
  58. if (conn != null) {
  59. if (conn.isValid()) {
  60. if (!conn.getRealConnection().getAutoCommit()) {
  61. conn.getRealConnection().rollback();
  62. }
  63. conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
  64. conn.setCheckoutTimestamp(System.currentTimeMillis());
  65. conn.setLastUsedTimestamp(System.currentTimeMillis());
  66. state.activeConnections.add(conn);
  67. state.requestCount++;
  68. state.accumulatedRequestTime += System.currentTimeMillis() - t;
  69. } else {
  70. if (log.isDebugEnabled()) {
  71. log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
  72. }
  73. state.badConnectionCount++;
  74. localBadConnectionCount++;
  75. conn = null;
  76. if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
  77. if (log.isDebugEnabled()) {
  78. log.debug("PooledDataSource: Could not get a good connection to the database.");
  79. }
  80. throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
  81. }
  82. }
  83. }
  84. }
  85.  
  86. }

当连接数少于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. 使用PowerShell找出具体某个站点所使用的模板(Web Template)名称?

    $web = get-spweb –identity http://servername/sites/site/web #得到站点的对象 $web.WebTemplate #得到WebTemplate ...

  2. 通过jquery js 实现幻灯片切换轮播效果

    观察各个电商网址轮播图的效果,总结了一下主要突破点与难点 1.->封装函数的步骤与具体实现 2->this关键字的指向 3->jquery js函数熟练运用 如animate 4-& ...

  3. java多线程系类:JUC原子类:01之框架

    本系列内容全部来自于http://www.cnblogs.com/skywang12345/p/3514589.html 特在此说明!!!!! 根据修改的数据类型,可以将JUC包中的原子操作类可以分为 ...

  4. Linux 部署ASP.NET SQLite 应用 的坎坷之旅 附demo及源码

    Linux 部署ASP.NET SQLite 应用 的坎坷之旅.文章底部 附示例代码. 有一台闲置的Linux VPS,尝试着部署一下.NET 程序,结果就踏上了坑之路,不过最后算是完美解决问题,遂记 ...

  5. ASP.NET MVC 5 入门指南汇总

    经过前一段时间的翻译和编辑,我们陆续发出12篇ASP.NET MVC 5的入门文章.其中大部分翻译自ASP.NET MVC 5 官方教程,由于本系列文章言简意赅,篇幅适中,从一个web网站示例开始讲解 ...

  6. Redis分布式锁服务(八)

    阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...

  7. 共享文件夹:The user has not been granted the requested logon type at this computer

    场景重现 今天做一个项目测试,要用到虚拟机,于是在虚拟机(XP 32)上新建了一个共享的文件夹.然后我在Win7 机器上访问它得到如下的error 消息:

  8. jQuery编程的最佳实践

    好像是feedly订阅里看到的文章,读完后觉得非常不错,译之备用,多看受益. 加载jQuery 1.坚持使用CDN来加载jQuery,这种别人服务器免费帮你托管文件的便宜干嘛不占呢.点击查看使用CDN ...

  9. Java this的一两点使用

    Java this的一两点使用 之前的文章都是关于Android的使用,这次想写一些关于Java的知识,总结一下Java的使用.这次写的是关于Java this的使用,介绍以下内容: this的概念 ...

  10. Can't use Subversion command line client: svn Probably the path to Subversion executable is wrong. Fix it.

    1.最近使用SVN工具时,Checkout出项目到本地后后,然后将其导入到Intellij idea中开发,在提交svn代码的时候,出现这样的错误:Can't use Subversion comma ...