由JDBC事务引起的锁状态以及内存数据无法写入数据库的问题

填坑之路 专栏收录该内容
0 篇文章 0 订阅

最近在做项目的时候,踩了一个大坑,为了排查BUG所在,花费了大量的时间。所以觉得应该来一个“填坑之路”系列,自己踩过的坑,希望各位看官能及时绕开。得亏看到了,要不然踩着了!

事情是这样的,开发完成的一个项目,在本地测试表现正常,没有问题。但在发布到服务器上之后,问题就出现了,一开始会出现用户添加的数据无法写入数据库的情况,重启数据库之后,一段时间内数据可以及时写入,但一段时间之后又会出现内存中数据无法写入数据库的情况。具体表现为:在Web页面写入数据后,可以在页面看到数据的变化,但进入数据库查询却看不到任何改变。另外,在整个系统访问人数增多之后,会不时地出现事务锁的情况,后台报错为:数据库等待超时或数据库出现死锁。

之后就是分析问题,然后各种猜测:数据库版本不对?数据库配置缓存区设置过大?数据库自动提交默认关闭?数据库引擎问题?数据源连接池采用了单例模式?在排除了以上所有可能之后,我看到了一行代码

conn.setAutoCommit(false);

会不会是这里我要开启事务因而关闭自动提交的原因?但是下面还有一行呢

if (conn != null) {
    conn.close();
}

按理说,我关闭了当前连接,下次建立的又是一个新的连接,数据库又是默认开启自动提交的,所以这样写应该是完全合理的啊。但目前能想到的也只剩这么一个可能了,我不确定是不是这个原因所致,因此我做了一个实验:

从网页上登录系统,首先进行一项需要开启事务的sql语句相关的操作,然后观察数据库是否能及时写入数据,发现写入正常。接下来,无论执行怎样的操作,发现数据库都已经无法及时地写入数据。这下困扰我许久的问题应该水落石出了:在执行了conn.setAutoCommit(false);这句代码之后,连接即使关闭了,之后建立的新连接的自动提交都为关闭状态。但是为什么呢?网上查阅资料后发现,数据源采用连接池执行conn.close();语句,并不是真正关闭连接,而是将连接归还连接池,供下次使用。真相大白之后,接下来的修改就简单啦,在finally语句块中添加conn.setAutoCommit(true);将自动提交手动开启。另外,需要特别注意的是:如果采用了事务,那么必须在catch语句块中添加conn.rollback();进行数据库回滚。否则,一旦sql执行过程中出现异常,而在catch异常处理语句中没有进行rollback回滚操作,事务会一直处于等待状态,造成事务锁。

总结一下,如果数据库操作要用到事务的话(批量操作需要用事务大家都知道的吧?不然每一条数据改动都提交一次会严重影响效率,采用事务在所有数据更改完毕之后一次性提交),以下代码必不可少,且必须按顺序执行:

try {
    ...
    conn.setAutoCommit(false);
    ...
    conn.commit();
    ...
} catch (SQLException e) {
    conn.rollback();
    ...
} finally {
    conn.setAutoCommit(true);
    ...
}
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值