想做设计师需要学什么/长沙seo免费诊断
文章目录
- 前言
- 一、Redisson是什么?
- 二、使用Redisson
- 1.引入pom依赖
- 2.配置注入对象
- 三、使用Redisson实现可重入锁(Reentrant Lock)
- 1.原因
- 2.redisson使用
- 3.redisson看门狗机制
- 4.redisson看门狗源码分析
- 四、使用Redisson实现读写锁(ReadWrite Lock)
- 五、使用Redisson实现信号量(Semaphore)
- 六、使用Redisson实现分布式闭锁(CountDownLatch)
- 七、缓存和数据库一致性解决方案
- 1.出现的问题
- 2.解决方案
- 3.总结
前言
使用redisson进行redis客户端操作
一、Redisson是什么?
Redisson是一个在Redis的基础上实现的Java驻内存数据网络(In-Memory Data Grid)。不仅提供了一些列的分布式java常用对象,还提供了许多分布式服务。
Redission提供了Redis最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离,集中精力在业务处理上。
二、使用Redisson
1.引入pom依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.4</version>
</dependency>
2.配置注入对象
@Configuration
public class MyRedisConfig {@Value("${spring.redis.host}")private String redisIp;@Value("${spring.redis.port}")private String redisPort;//注入RedissonClient@Bean(destroyMethod = "shutdown")RedissonClient redisson(){//编写配置Config config = new Config();//单节点模式config.useSingleServer().setAddress("redis://"+redisIp +":"+redisPort);return Redisson.create(config);}
}
三、使用Redisson实现可重入锁(Reentrant Lock)
1.原因
如果A调用B。A、B都需要同一把锁,此时使用可重入锁(Reetrant Lock)就能实现可重入,A调用B。否则如果是不可重入,调用B的前提是A释放锁,A释放锁的前提是调用B,此时就形成死锁。
2.redisson使用
@ResponseBody
@RequestMapping("/hello")
public String hello(){//获取一把锁 只要锁的名字一样就说是同一把锁RLock lock = redisson.getLock("my-lock");lock.lock();try {System.out.println("走业务代码");}finally {lock.unlock();}return "hello";
}
3.redisson看门狗机制
- 锁的续期:如果没有锁的续期,存储分布式锁的redisson节点宕机,正好这个锁处于锁住状态时,这个锁就处于死锁状态。为了避免这种情况,redisson内部提供了一种监控锁的看门狗机制。
- 看门狗的作用:在redisson实例被关闭之前,不断延长锁的有效时间。默认情况看门狗检查锁的时间是30秒。
4.redisson看门狗源码分析
- 如果传递了锁的超时时间,就执行脚本,进行占锁;
- 如果没传递锁时间,使用看门狗的时间,占锁。如果返回占锁成功future,调用future.onComplete();
没异常的话调用scheduleExpirationRenewal(threadId);
重新设置过期时间,定时任务;
看门狗的原理是定时任务:重新给锁设置过期时间,新的过期时间就是看门狗的默认时间;锁时间/3是定时任务周期;
四、使用Redisson实现读写锁(ReadWrite Lock)
分布式可重入读写锁允许同时又多个读锁和写锁处于加锁状态。
读时处于无锁状态。
五、使用Redisson实现信号量(Semaphore)
@GetMapping("/park")
@ResponseBody
public String park() {RSemaphore park = redisson.getSemaphore("park");try {park.acquire(2);} catch (InterruptedException e) {e.printStackTrace();}return "park";
}@GetMapping("/go")
@ResponseBody
public String go() {RSemaphore park = redisson.getSemaphore("park");park.release(2);return "go";
}
信号量可以做限流等应用
六、使用Redisson实现分布式闭锁(CountDownLatch)
七、缓存和数据库一致性解决方案
- 双写模式。写完数据库后,写缓存。
- 失效模式。写完数据库后,删缓存。
1.出现的问题
双写模式:
并发时,2号线程进入,写完DB后写缓存。而此时1号线程还没有写缓存,造成短时间内缓存有脏数据。
失效模式:
并发时,
1号线程先写数据库,删缓存,
2号线程此时依然还在写数据库,
但是3号线程读缓存发现没有缓存,此时去读取数据库,读数据库完成之后,2号线程才写完
在3号线程写缓存之前,2号线程写完数据库,也删完了缓存,
此时3号线程更新缓存。但是更新的依然是1号线程写的数据,2号线程写的数据没有加入到缓存。
2.解决方案
-
如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。
-
如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式
缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
-
通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略);
3.总结
-
我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。
-
我们不应该过度设计,增加系统的复杂性
-
遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。