记录一次@Cacheable导致死锁问题排查过程

6 篇文章 0 订阅
4 篇文章 0 订阅

情景再现

最近同事的一个项目在测试环境出来了一个奇怪的问题,说项目一登录就卡死,接口一直没响应,也不报错,正式环境也没有这个问题,感觉很奇怪,他找了很长时间找不出问题,让我帮忙排查一下是什么原因,排查了一上午的时间终于找到原因了,代码逻辑其实很简单,大致逻辑如下模拟代码:

    @Cacheable(cacheNames = {"user","user"},key = "#id",sync =true)
    public String getById(String id) {
        System.out.println("get from db");
        return "user:" + id;
    }

重要线索

他说只要把 @Cacheable去掉就不卡,加上就卡,这是一条非常重要的线索,肯定是这个导致的卡卡死,首先可以确定,不是数据库查询问题。

排查步骤

  • 查看缓存有没有数据
    我在想这肯定是走缓存了,那就打开redis 客户端看看有没有这个缓存,发现有一个user~lock这个Key 它的值为locked,看来是Redis线程死锁了,如果他看一下Redis数据库估计也就找到原因。
    在这里插入图片描述

  • 找到CacheAspectSupport
    有个execute()方法,有个 if (contexts.isSynchronized())判断,如果设置了sync=true的话,设置Redis缓存时会加锁
    在这里插入图片描述

  • 找到死锁代码 RedisCache waitForLock()方法
    这里就是个死循环,每隔300毫秒去Redis查询一下有没有 user~lock这个key,而且这个 key的有效期是永久的,所以就是卡在这里一直循环出不去了。
    在这里插入图片描述

解决方法

redis中删除 lock这个Key 就OK了

问题出现的原因

在RedisWriteThroughCallback 的 doInRedis中会先lock一个,在 finally会unlock(connection);
我猜测应该是测试环境停用服务使用的是 kill -9 导致finally中的unlock方法没有执行, 而且这个key没有设置过期时间,所以就永久死循环了,如果不加sync =true 也不会加锁了。

	static class RedisWriteThroughCallback extends AbstractRedisCacheCallback<byte[]> {

		public RedisWriteThroughCallback(BinaryRedisCacheElement element, RedisCacheMetadata metadata) {
			super(element, metadata);
		}

		@Override
		public byte[] doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
			try {
				lock(connection);
				try {
					byte[] value = connection.get(element.getKeyBytes());
					if (value != null) {
						return value;
					}
					.........
					return value;
				} catch (RuntimeException e) {
					if (!isClusterConnection(connection)) {
						connection.discard();
					}
					throw e;
				}
			} finally {
				unlock(connection);
			}
		}
	};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵侠客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值