Redisson

官方帮助文档:https://github.com/redisson/redisson/wiki/Table-of-Content
springboot版本对应:https://blog.csdn.net/liuerchong/article/details/124942903
提供分布式对象和分布式服务,Redisson和lettuce、jedis一样都是操作redis的客户端
使用redisson作为分布式锁、分布式对象
_<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>

创建配置类

使用单机模式创建,其他配置查看官方文档修改

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 {
    /**
     * 所有对redisson的操作都是通过RedissonClient对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        //创建配置
        Config config = new Config();
        //可以用"rediss://"来启用SSL连接
        config.useSingleServer().setAddress("redis://47.251.1.35:6379")
                .setPassword("zax");
        //根据配置创建实例
        return Redisson.create(config);
    }
}

分布式锁

Redisson分布式可重入锁实现了JUC包下的Lock接口

//传入锁名,锁名相同就是同一把锁
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock(); //加锁,阻塞式等待,默认有效期30s
lock.unlock(); //解锁

Redisson内部提供了监控锁的看门狗
  • 锁的自动续期:如果业务超长执行,运行期间看门狗会自动对锁续30s的有效期

每10s续一次期

  • 业务运行完成,不会给锁自动续期,不进行解锁操作也会自动解锁
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
//自动解锁时间要大于业务执行时间
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

公平锁

请求是有顺序的依次上锁

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();

读写锁

  • 读写锁允许同时有多个读锁和一个写锁处于加锁状态。
  • 保证能读到最新数据,修改期间,写锁为排它锁(互斥锁),读锁为共享锁
  • 写锁没释放,读锁必须等待
  • 读+读:相当于无锁,会同时加锁成功,在redis中记录所有当前的读锁
  • 读+写:写锁需要等待读锁释放
  • 写+读:等待写锁释放
  • 写+写:阻塞状态,下一个锁需要等上一个锁释放
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock(); //读数据加读锁
// 或
rwlock.writeLock().lock(); //改数据加写锁

闭锁

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(5);//闭锁中参数为5
latch.await();//等待闭锁完成,参数为0时完成

// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();//参数-1

信号量

可以做服务限流,每个请求释放一个信号

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();//获取一个信号,值
semaphore.release();//释放一个信号
//或
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();
//或
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//或
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);

//或
semaphore.releaseAsync();

解决缓存穿透、缓存击穿、缓存雪崩

TimeUnit.DAYS          //天  
TimeUnit.HOURS         //小时  
TimeUnit.MINUTES       //分钟  
TimeUnit.SECONDS       //秒  
TimeUnit.MILLISECONDS  //毫秒 
TimeUnit.NANOSECONDS   //毫微秒
TimeUnit.MICROSECONDS  //微秒

使用分布式锁解决缓存击穿问题

    @Override
    public List<CategoryEntity> listWithTree(){
        /**
         * 1.使用布隆过滤器,或将空数据进行缓存 解决:缓存穿透
         * 2.设置随机过期时间 解决:缓存雪崩
         * 3.加分布式锁 解决:缓存击穿
         */
        //加入缓存逻辑,缓存中存的数据是JSON字符串
        //JSON好处:跨语言,跨平台
        String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
        //缓存中没有数据
        if (StringUtils.isEmpty(categoryJSON)) {
            //查询数据库,进行加锁操作
            return listWithTreeRedisson();
            //响应用户
        }
        //将JSON格式转为java对象,响应用户
        return JSON.parseObject(categoryJSON,
                    new TypeReference<List<CategoryEntity>>(){});
    }


    public List<CategoryEntity> listWithTreeRedisson(){
        //加锁
        RLock lock = redissonClient.getLock("anyLock");
        //开启锁
        lock.lock();
        try {
            return listWithTreeFromdb();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //从数据库中查询
    public List<CategoryEntity> listWithTreeFromdb() {
            //在查询一次redis中有没有缓存数据
            String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
            if (StringUtils.isNotEmpty(categoryJSON)) {
                return JSON.parseObject(categoryJSON,
                        new TypeReference<List<CategoryEntity>>(){});
            }
            System.out.println("缓存未命中。。。查询数据库");
            //查出所有分类
            List<CategoryEntity> entities = baseMapper.selectList(null);
            //查出所有一级分类:将所有分类进行过滤
            List<CategoryEntity> level1Menus = entities.stream().filter((categoryEntity) ->
                            categoryEntity.getParentCid() == 0
                    //使用peek和使用map效果相同,peek不需要使用return返回menu
            ).peek((menu) -> {
                //menu是当前分类菜单,使用递归查找子类菜单,并保存到children属性中
                menu.setChildren(getChildrens(menu, entities));
                //使用sorted对使用分类菜单进行排序
            }).sorted((menu1, menu2) -> {
                return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
                //将所有数据转换成list集合
            }).collect(Collectors.toList());
            Random random = new Random();
            //3~303
            int i = 3+random.nextInt(300);
            //将查询结果转为JSON格式,存入缓存
            String string = JSON.toJSONString(level1Menus);
            //随机过期时间,单位为分钟
            redisTemplate.opsForValue().set("categoryJSON",string,i,TimeUnit.MINUTES);
            return level1Menus;
    }

保证缓存一致性

双写模式

数据库改完改缓存

失效模式

在修改和删除业务完成后,传入key删除缓存中的数据
stringRedisTemplate.delete(categoryJSON);

  • 设置过期时间
  • 解决脏读问题,使用分布式读写锁
  • 使用Canal解决
使用Canal伪装成mysql的从服务器

StringRedisTemplate常用操作 / Redis中删除过期Key的三种策略

过期键的三种清除策略

  • 惰性删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
  • 定时删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key
  • 内存淘汰:当前已用内存超过maxmemory限定时,触发主动清理策略
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值