Redis的使用实例(一)

一、缓存实例

1.读数据

	public ProductSellDetailsModel getProductSellInfo(Long productId) {
        ProductSellDetailsModel productSellDetailsModel;
        // 查询缓存
        Object productSell =
                stringRedisTemplate.opsForHash().get("redis:productSellDetail", productId + "");
        if (StringUtils.isEmpty(productSell)) {
            // 缓存未找到 查db
            productSellDetailsModel = productSellDaoMapper.queryByProductId(productId);
            if(!StringUtils.isEmpty(productSellDetailsModel)){
                stringRedisTemplate.opsForHash().put("redis:productSellDetail",
                        productId + "", JSON.toJSONString(productSellDetailsModel));
            }
        } else {
            productSellDetailsModel = 
            	JSONObject.parseObject(String.valueOf(productSell),ProductSellDetailsModel.class);
        }
        return productSellDetailsModel;
    }

二、分布式锁

在部署了多台Tomcat服务的情况下,为了解决并发场景下的超卖问题,synchronized本地锁已经无法解决问题。在此情况下我们可以考虑Redis分布式锁。

方案一:

Boolean lock =stringRedisTemplate.opsForValue().setIfAbsent("redisLock"+productId,"lock");
if(lock){
	// 拿到锁后  执行逻辑
}else{
	// 未拿到锁
}
// 释放锁
stringRedisTemplate.delete("redisLock"+productId);

方案一,大致逻辑没有问题,但是方案还有很多缺点。
比如:在执行业务逻辑抛异常后、或者在释放锁操作时宕机,导致释放锁操作失败,从而产生死锁问题。
针对这两个问题做优化:
1.业务逻辑抛异常。可以使用try finally
2.在释放锁操作前宕机。可以给锁添加一个失效时间

方案二:

try{
    // 先设置锁,再设置超时时间,两条命令,没有保证命令的原子性
	//Boolean lock =stringRedisTemplate.opsForValue().setIfAbsent("redisLock"+productId,"lock");
	//stringRedisTemplate.expire("redisLock"+productId,10,TimeUnit.SECONDS);
	Boolean lock = stringRedisTemplate.opsForValue()
					.setIfAbsent("redisLock"+productId, "lock",10, TimeUnit.SECONDS);
	if(lock){
		// 拿到锁后  执行逻辑
	}else{
		// 未拿到锁
	}
}finally{
	// 释放锁
	stringRedisTemplate.delete("redisLock"+productId);
}

方案二相对于方案一而言,代码更加健壮一些,但还是存在一些问题。
例如:有三个线程同时执行这段代码
1.线程一先拿到锁,开始执行业务逻辑,假设业务逻辑执行时间需要12秒,超过了设置的锁过期时间10秒,导致锁失效,而此时线程一还在执行业务逻辑
2.线程二和三一直在等待锁释放,当锁失效后,线程二拿到了锁后,执行了2秒,而线程一已经执行12秒,逻辑已经执行完成,释放锁,但是此时线程二还在执行业务逻辑。
3.线程二加的锁被线程一释放掉,而线程二还在执行业务逻辑,线程三拿到锁后,也开始执行业务逻辑…
4.如此逻辑下去,不难发现分布式锁的效果并没有实现。

针对上面情景进行问题分析:
a.锁的失效时间不应该小于业务逻辑的执行时间。业务逻辑的执行时间受多重因素影响,是不确定的,因此锁的失效时间设置多少都是不合理的。那我们可不可以新开启一个线程,定时去获取锁的状态,如果有线程在使用,则更新一下过期时间,以此来延迟锁失效。
b.上面的场景可以看到,线程二加的锁被线程一给释放了。我们则可以进行限制,自己加的锁只能自己进行释放。

方案三:

// 生成唯一id
String uuid = UUID.randomUUID().toString();
try{
    // 先设置锁,再设置超时时间,两条命令,没有保证命令的原子性
	//Boolean lock =stringRedisTemplate.opsForValue().setIfAbsent("redisLock"+productId,"lock");
	//stringRedisTemplate.expire("redisLock"+productId,10,TimeUnit.SECONDS);
	Boolean lock = stringRedisTemplate.opsForValue()
					.setIfAbsent("redisLock"+productId, uuid,10, TimeUnit.SECONDS);
	if(lock){
		// 拿到锁后  执行逻辑
	}else{
		// 未拿到锁
	}
}finally{
			 // 释放锁
			 // 查询和删除没有原子性操作,故舍弃,采用Lua脚本的方式,保证操作的原子性
//            String redisLock = stringRedisTemplate.opsForValue().get("redisLock");
//            if(uuid.equals(redisLock)){
//                // 清理锁
//                log.info("清理锁==>"+redisLock);
//                stringRedisTemplate.delete("redisLock");
//            }
            // 脚本解锁
            String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            stringRedisTemplate.execute(
            	new DefaultRedisScript(script, Long.class),  Arrays.asList("redisLock"+productId), uuid);
}

上面的代码并没有加入锁续命的逻辑,当然代码还是有些地方需要优化。从上面的整个思维来看,除了业务逻辑外,还需要添加很多分布式锁相关代码,整个逻辑相对更加复杂。而redisson框架,帮我们解决了这些问题。

redisson的使用可浏览下个章节

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值