springboot+redisson初尝试

redisson为redis的分布式解决方案,对redis进行了封装,经常应用于分布式锁场景。
redis常见问题

  • 缓存穿透:程序中没有缓存x值,当大量请求获取一个不存在的x值时,由于缓存中没有,大量请求直接访问数据库,数据库压力陡增,从而出现穿透问题;
    解决:将查询结果为x值的数据缓存到redis中;
  • 缓存雪崩:大量缓存同一个时间内失效,这时来了一大波请求,都怼到数据库上,数据库处理不过来崩了;
    解决:在设置数据失效时间时,增加一个随机数;
  • 缓存击穿:大量请求同时访问同一个正好过期的缓存数据,导致原本能命中缓存数据的请求直接访问数据库,数据库崩了;
    解决:添加分布式锁;
    实例

            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.redisson
            redisson
            3.11.0
        
@Configuration
public class RedissonConfig {

    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        //创建配置
        Config config = new Config();
        //可以用"redis://"来启用SSL连接,useSingleServer表示单例模式
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        //根据config创建出RedissonClient实例
        return Redisson.create(config);
    }

}

分布式锁、写锁、读锁、CountDownLatch

@RestController
public class HelloController {

    @Autowired
    private RedissonClient redisson;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * lock.lock(10, TimeUnit.SECONDS); //10秒自动解锁;解锁时间一定要大于业务操作时间
     * 问题:如果指定解锁时间,在锁时间到了以后,不会自动续期
     * 1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
     * 2、如果我们未指定超时时间,就使用30*1000【lockWatchdogTimeout看门狗默认的时间】,只要占锁成功,就会
     *    启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒就会自动续期,续成30s
     */
    @RequestMapping("/hello")
    public String hello(){
        //1、获取一把锁,只要锁的名字一样,那就是同一把锁
        RLock lock = redisson.getLock("redisson-lock");
        //2、加锁,默认加的锁都是30s时间
        lock.lock(); //阻塞式等待
        //1)、锁的自动续期;如果业务超长,运行期间自动给锁续上新的30s;不用担心业务时间长,锁自动过期被删掉
        //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s后自动删除

        //lock.lock(30, TimeUnit.SECONDS); 指定时间,并手动解锁
        try {
            System.out.println("加锁成功,执行业务. Thread:"+Thread.currentThread().getId());
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("释放锁. Thread:"+Thread.currentThread().getId());
            lock.unlock();
        }
        return "hello";
    }

    /**
     * 写锁保证一定能读到最新数据,修改期间,写锁是一个排他锁(互诉锁,独享锁)。读锁是一个共享锁,写锁没释放,读就必须等待
     * 写 + 读 (写的时候进行读操作):等待写锁释放
     * 写 + 写 (写的时候进行写操作):阻塞方式
     * 读 + 写 (读的时候进行写操作):等待读锁释放
     * 读 + 读 :相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功。
     */
    @RequestMapping("/write")
    public String writeLock(){
        RReadWriteLock lock = redisson.getReadWriteLock("w-lock");
        String str = "";
        RLock rLock = lock.writeLock();
        try {
            // 改数据加写锁,读数据加读锁
            rLock.lock();
            str = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("writerValue",str);
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return str;
    }

    @RequestMapping("/read")
    public String readLock(){
        RReadWriteLock lock = redisson.getReadWriteLock("r-lock");
        RLock rLock = lock.readLock();
        String str = "";
        try {
            //加读锁
            rLock.lock();
            str = redisTemplate.opsForValue().get("writerValue").toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return str;
    }

    @RequestMapping("/lockDoor")
    public String lockDoor() throws InterruptedException {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        door.await(); // 等待闭锁都完成
        return "各部门已下班,关门了。";
    }

    @RequestMapping("/go/{id}")
    public String go(@PathVariable("id") String id){
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.countDown(); //计数减1
        return id + "部门下班了。";
    }

}

分布式锁的应用还需深入实践。

你可能感兴趣的:(springboot,redis,java,spring,boot)