记一次死锁问题的处理

总想写点技术积累。因为最近工作比较忙,一忙一懒,找足了借口 :(

今天想简单记录一下前段时间发现在做数据清洗时遇到的问题,在处理这件事情上,感受到了许多。

大概需求是这样的:有两组数据库,每组都是分库分表的结构。从两组库中查出数据,根据规则再进行入库、清除缓存、发送消息队列。数据有几千万。

遇到这样的问题,首先想到的就是在保证数据准确的情况下做并行处理,其次想到的是尽量降低数据库的压力,再次就是可重复执行。

大概是这么写的。伪代码:

staic Connection connA = new LongDbConnectionA();   // 不做自动关闭,只做查询操作

static Connection connB = new LongDbConnectinoB();   // 不做自动关闭,只做查询操作

CountDownLatch latch = new CountDownLatch(dbACount + dbBCount);

ExecutorService service = Executors.newFixedThreadPool(MAX_THREAD_NUM);

for(a;b;c) {

     service.submit(new BizRunnable()); // 具体的业务逻辑

}

latch.await();

service.shutdown();

log.info(result);


我使用了4个线程在本地做运行测试,在测试过程中偶尔发生程序hung住的问题。正好其他同学在这段时间反馈研发环境的数据库访问特别慢,所以我认为hung住也是数据库搞的鬼,也没去深究。

于是后续去线上执行了3次预跑,程序运行的很好,很快,根据日志分析也很ok,40个线程几分钟就跑完了。然后信心满满的去正式跑。

刚运行一会发现在服务器上神奇的hung住了!开始不相信是代码的问题,kill -9 pid,重来,因为可重复跑嘛。这次终于执行完成了,验证后数据正常。完事后手动clear了cache,发送消息队列。一番周章,有惊无险的搞定了事情。

但是这次在服务器上hung住绝对不可能认为是数据库的问题,那自然而然。于是跟我的头一起看代码,看了半天也没发现哪里出现了问题,感觉似乎都很正常,中规中矩的代码。

既然代码看不出来了,只能想别的办法了。回到研发环境我反复跑了几次,问题终于复现了,又见hung住。我确认还是代码的问题。于是再多次运行直到hung状态。用stack打印当前堆栈。果然,发现了deadlock。原来是最外层那个想节省数据库链接的主意导致了Connection的死锁:

Found one Java-level deadlock:
=============================
"pool-1-thread-4":
  waiting to lock monitor 0x000000000d8fb7d8 (object 0x000000078176ea50, a com.mysql.jdbc.JDBC4Connection),
  which is held by "pool-1-thread-1"
"pool-1-thread-1":
  waiting to lock monitor 0x000000000d8f8918 (object 0x00000007d75838b0, a com.mysql.jdbc.JDBC4ResultSet),
  which is held by "pool-1-thread-3"
"pool-1-thread-3":
  waiting to lock monitor 0x000000000d8f89c8 (object 0x00000007d7581ea0, a com.mysql.jdbc.JDBC4PreparedStatement),
  which is held by "pool-1-thread-1"




今天翻文件的时候


工作完成后,才定位到问题,这事是第一次用到。也有一些感悟:

1、不要太相信自己的代码;

2、遇到异常不要想当然,要真正的去定位问题,而不是凭借主观意识去决断;多做一步,消灭BUG;

3、不容易定位的死锁问题使用jstack可以很方便的找到根源;

4、别有侥幸心理,多测试不会死,测试不足会崩。崩了,丢人。。


版权声明:本文为博主原创文章,未经博主允许不得转载。

 

posted @ 2015-07-10 00:13  土豆条  阅读(265)  评论(0编辑  收藏  举报