总想写点技术积累。因为最近工作比较忙,一忙一懒,找足了借口 :(
今天想简单记录一下前段时间发现在做数据清洗时遇到的问题,在处理这件事情上,感受到了许多。
大概需求是这样的:有两组数据库,每组都是分库分表的结构。从两组库中查出数据,根据规则再进行入库、清除缓存、发送消息队列。数据有几千万。
遇到这样的问题,首先想到的就是在保证数据准确的情况下做并行处理,其次想到的是尽量降低数据库的压力,再次就是可重复执行。
大概是这么写的。伪代码:
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、别有侥幸心理,多测试不会死,测试不足会崩。崩了,丢人。。
版权声明:本文为博主原创文章,未经博主允许不得转载。