Scala/Spark中Redisson调用Redis出现死锁问题求助
Redisson 3.9.1调用Redis死锁原因分析
问题场景
刚接触Scala与Spark技术栈,在Scala代码中通过Redisson 3.9.1调用Redis获取少量键数据时出现死锁,对应的线程栈信息如下:
"Attach Listener" #124 daemon prio=9 os_prio=0 tid=0x00007fbde8002800 nid=0x494e1 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Keep-Alive-Timer" #123 daemon prio=8 os_prio=0 tid=0x00007fbd68021000 nid=0x493d1 waiting on condition [0x00007fbb2fffe000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at sun.net.www.http.KeepAliveCache.run(KeepAliveCache.java:172) at java.lang.Thread.run(Thread.java:748) "ForkJoinPool-1-worker-5" #117 daemon prio=5 os_prio=0 tid=0x00007fbc9c87b000 nid=0x475ba waiting on condition [0x00007fbb1dbfc000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00007fc68d00ed50> (a java.util.concurrent.CountDownLatch$Sync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304) at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231) at org.redisson.command.CommandAsyncService.get(CommandAsyncService.java:182) at org.redisson.RedissonKeys$2.iterator(RedissonKeys.java:127) at org.redisson.RedissonKeys$2.iterator(RedissonKeys.java:123) at org.redisson.BaseIterator.hasNext(BaseIterator.java:54) at scala.collection.convert.Wrappers$JIteratorWrapper.hasNext(Wrappers.scala:42) at scala.collection.Iterator$class.foreach(Iterator.scala:893) at scala.collection.AbstractIterator.foreach(Iterator.scala:1336) at scala.collection.IterableLike$class.foreach(IterableLike.scala:72) at scala.collection.AbstractIterable.foreach(Iterable.scala:54) at scala.collection.TraversableLike$class.filterImpl(TraversableLike.scala:247) at scala.collection.TraversableLike$class.filterNot(TraversableLike.scala:267) at scala.collection.AbstractTraversable.filterNot(Traversable.scala:104) at com.mycomosi.eaa.common.infrastructure.topology.store.TopologyStoreEntityService$$anonfun$getTopologyInstanceIdsExcludingVersion$1$$anonfun$apply$7.apply(TopologyStoreEntityService.scala:73) at com.mycomosi.eaa.common.infrastructure.topology.store.TopologyStoreEntityService$$anonfun$getTopologyInstanceIdsExcludingVersion$1$$anonfun$apply$7.apply(TopologyStoreEntityService.scala:70) at scala.collection.immutable.Set$Set1.foreach(Set.scala:94) at com.mycomosi.eaa.common.infrastructure.topology.store.TopologyStoreEntityService$$anonfun$getTopologyInstanceIdsExcludingVersion$1.apply(TopologyStoreEntityService.scala:70) at com.mycomosi.eaa.common.infrastructure.topology.store.TopologyStoreEntityService$$anonfun$getTopologyInstanceIdsExcludingVersion$1.apply(TopologyStoreEntityService.scala:69) at scala.collection.Iterator$class.foreach(Iterator.scala:893) at scala.collection.AbstractIterator.foreach(Iterator.scala:1336)
原因分析
从线程栈核心调用链可定位问题根因:
- 线程阻塞本质:
ForkJoinPool-1-worker-5线程卡在CountDownLatch.await(),等待CommandAsyncService.get()中的计数门闩释放,但对应的Redis异步命令回调从未触发,导致线程永久等待。 - 版本缺陷:Redisson 3.9.1属于老旧版本,存在已知的异步回调异常问题——当Redis连接超时、网络波动或命令执行异常时,异步任务的回调逻辑无法正确触发,
CountDownLatch无法计数到0,线程陷入死锁。 - 线程模型冲突:Spark任务运行在
ForkJoinPool线程池中,Redisson异步操作依赖自身线程池。在ForkJoin线程中同步调用get()阻塞等待异步结果,可能因线程资源耗尽、上下文类加载冲突,导致Redisson回调线程无法正常执行。 - 迭代器异步加载bug:RedissonKeys的迭代器采用异步加载key的方式,老版本该逻辑在并发场景下存在线程阻塞问题,结合Scala的
JIteratorWrapper包装后,遍历操作触发的同步等待进一步放大了问题。
解决方案建议
- 升级Redisson版本:直接升级到3.17.x及以上稳定版本,该版本修复了大量线程阻塞和异步回调相关的bug。
- 避免同步阻塞调用:在Spark线程中不要直接调用Redisson异步方法的
get(),改用异步回调(如whenComplete)或将Redisson操作提交到独立线程池中执行。 - 优化Redis连接配置:检查Redisson连接池的最大连接数、超时时间设置,确保连接资源充足,同时确认Redis服务正常、网络无波动。
- 替换迭代器查询方式:改用Redisson的同步批量查询API(如
getKeysByPattern)先获取所有目标key,再进行遍历,避免迭代器异步加载带来的阻塞风险。
内容的提问来源于stack exchange,提问作者Chandan Gawri
相关产品推荐
相关产品推荐

