您的位置:首页 > 数据库

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

2017-09-19 19:48 477 查看
最近在做项目的时候,踩了一个大坑,为了排查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);
...
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据库 事务 bug
相关文章推荐