@Cacheable注解使用sync引起死锁问题解决

  |   0 评论   |   0 浏览

发现问题

在使用Cacheable注解的时候,为了解决并发查的问题,使用了sync=true,但是出现了下面的问题:
image.png
导致整个服务都不可用了。

分析问题

spring-data-redis的版本为1.8.12.RELEASE
初步怀疑是不是线程卡住了,使用jstack命令,输出日志:
image.png
从日志可以看出是redis的操作被什么锁住了,从日志的类一步一步看出,终于看到了真凶。接下来让我们一步一步来看:
找到RedisTemplate类,定位到
image.png
这个方法会找到RedisCache类下的AbstractRedisCacheCallback类,
image.png
这个方法的具体代码如下:

protected boolean waitForLock(RedisConnection connection) {
	boolean retry;
	boolean foundLock = false;
	do {
		retry = false;
		//判断是否存在具体key~lock,如果存在,那么休眠300毫秒,
		if (connection.exists(cacheMetadata.getCacheLockKey())) {
			foundLock = true;
			try {
				Thread.sleep(WAIT_FOR_LOCK_TIMEOUT);
			} catch (InterruptedException ex) {
				Thread.currentThread().interrupt();
			}
			retry = true;
		}
	} while (retry);
	return foundLock;
}

此时这个线程就一直在等待,等这个key失效,但是这个key在redis中的永久有效的,所以就进入死循环了。
image.png
这个AbstractRedisCacheCallback类有以下几个子类,
image.png
但是只有RedisWriteThroughCallback类调用了unlock方法,
image.png
其他几个类就没有调用unlock方法
image.png
如果在调用过程中,服务重启或者出现假死,那么unlock没有执行到,就会出现服务无响应的问题。

解决问题

可以写个定时任务,每隔几分钟扫一下redis,如果出现该key就进行删除,需要判断一定时间内都存在的情况才能删除,防止误删。

也可以关注我的公众号:程序之声
图片
关注公众号,领取更多资源

本文为博主原创文章,未经博主允许不得转载。