1、引入maven依赖
<!--引入redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
2、config类
MyRedissonConfig
package com.ljs.gulimall.product.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class MyRedissonConfig {
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() throws IOException {
// 默认连接地址 127.0.0.1:6379
Config config = new Config();
//可以用"rediss://"来启用 SSL 连接
config.useSingleServer().setAddress("redis://xxx:6379").setPassword("xxxxx");
return Redisson.create(config);
}
}
3、可重入锁设计
redisson不存在死锁问题
@ResponseBody
@GetMapping("/hello")
public String hello(){
// 1、获取一把锁
RLock lock = redisson.getLock("my-lock");
// 2、加锁
lock.lock();
// 3、执行业务代码
try{
System.out.println("加锁成功,执行业务代码。。。"+Thread.currentThread().getId());
Thread.sleep(30000);
}catch (Exception exception) {
}finally {
// 4、解锁
System.out.println("释放锁。。。"+Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
无论程序是否异常。程序正常执行时看门狗机制会锁自动续期,程序异常时会30秒后释放锁。
4、读写锁
IndexController
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/write")
@ResponseBody
public String writeValue(){
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s = "";
RLock rLock = lock.writeLock();
try {
// 1、改数据加写锁,读数据加读锁
rLock.lock();
System.out.println("写锁加锁成功。。。"+Thread.currentThread().getId());
s = UUID.randomUUID().toString();
Thread.sleep(30000);
redisTemplate.opsForValue().set("writeValue",s);
}catch (Exception ex) {
}finally {
rLock.unlock();
System.out.println("写锁释放。。。"+Thread.currentThread().getId());
}
return s;
}
@GetMapping("/read")
@ResponseBody
public String readValue(){
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s = "";
RLock rLock = lock.readLock();
try {
// 1、改数据加写锁,读数据加读锁
rLock.lock();
System.out.println("读锁加锁成功。。。"+Thread.currentThread().getId());
s= redisTemplate.opsForValue().get("writeValue");
}catch (Exception ex) {
}finally {
rLock.unlock();
System.out.println("读锁释放。。。"+Thread.currentThread().getId());
}
return s;
}
当写锁存在、读锁必须等待
当写锁释放时。读锁全部都加锁
5、闭锁
@GetMapping("/lockDoor")
@ResponseBody
/**
* 5个部门走完才能关大门
*/
public String lockDoor() throws InterruptedException {
RCountDownLatch door = redisson.getCountDownLatch("door");
door.trySetCount(5);
door.await(); // 等待闭锁完成
return "关大门";
}
@GetMapping("/gogogo/{id}")
@ResponseBody
public String gogogo(@PathVariable("id") Integer id){
RCountDownLatch door = redisson.getCountDownLatch("door");
door.countDown(); //计数减一
return id + "号部门的人都下班走了";
}
/lockDoor方法会一直等待。闭锁数量减为0
当5个部门的人走光以后,关大门
6、信号量
@GetMapping("/park")
@ResponseBody
/**
* 车库停车
* 3个车位
*/
public String park () throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
park.acquire();
return "ok";
}
@GetMapping
@ResponseBody
/**
* 释放车位
*
*/
public String go() {
RSemaphore park = redisson.getSemaphore("park");
park.release();
return "释放一个车位";
}
redis里增加一个键值:车位:1
调用停车方法
返回ok
再调用一次发现阻塞等待
这时候需要释放一个车位这边才能停车、调用/go方法
7、缓存一致性问题解决
新增方法与业务结合。改造成redisson分布式锁
CategoryServiceImpl
/**
* 分布式锁
* 缓存和数据库的数据如何保持一致性
* 1、双写模式
* 2、失效模式
*
* @return
*/
public Map<String, List<Catelog2Vo>> getCatalogDbWithRedissonLock() {
// 分布式锁
RLock lock = redisson.getLock("CatalogJson-lock");
lock.lock();
System.out.println("获取分布式锁成功...");
Map<String, List<Catelog2Vo>> dataFromDb;
try {
dataFromDb = this.getDataFromDb();
} finally {
lock.unlock();
}
return dataFromDb;
}
@Override
public Map<String, List<Catelog2Vo>> getCatalog() {
// 1、获取缓存中的数据
String catalog = redisTemplate.opsForValue().get("catalog");
if (StrUtil.isBlank(catalog)) {
System.out.println("缓存不命中,准备要查询数据库");
// 2、从数据库中获取数据
return this.getCatalogDbWithRedissonLock();
}
// 将缓存中的数据返回
System.out.println("缓存命中,直接返回");
return JSON.parseObject(catalog,Map.class);
}
解决缓存和数据库不同步问题:
1、双写模式
双写模式可能会导致脏数据
2、失效模式
失效模式也可能有脏数据。
###对于经常修改。对数据实时性要求高的数据,最好不要加缓存,缓存只能保证最终一致性,而不是强一致性。应直接读数据库。对于数据实时性要求不高的数据。可以加缓存,如有业务需要可以加读写锁。
如果觉得加锁系统显得臃肿。可以集成阿里巴巴的canal。将数据一致性问题交给canal来做。它相当于mysql从库。我们对mysql数据进行更新时,打开binlog日志。canal将更新日志订阅。然后更新redis值。