zoukankan      html  css  js  c++  java
  • Mybatis-update

    最近学习测试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.");
                }
              }
            }
          }
    
        }
    View Code

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

  • 相关阅读:
    runtest.sh
    写代码:简单的三元运算
    写代码:用户交互显示类似省市县三级联动的选择
    写代码:列举布尔值是False的所有值
    写代码:求集合
    写代码:利用For循环和while输出1-100
    写代码:利用for循环和range输出9*9乘法表
    写代码:查找列表中元素,移除每个元素的空格,并查找以a或A开头并且以c结尾的元素。
    写代码:利用下划线将列表的每个元素拼接成字符串,li=["alex","eric","rain"]
    写代码:购物车程序
  • 原文地址:https://www.cnblogs.com/woshimrf/p/5693673.html
Copyright © 2011-2022 走看看