2021-04-17-Redis总结(四)

发布订阅

  • 这个跟我们之前使用消息队列技术一样。
    主要有两个关键操作:
    消费者:订阅渠道subscribe channel1
    生产者:发布消息到渠道 publish channel1 “redis is good”
    在这里插入图片描述

内存管理–超时指令

Reids设置超时

  • Redis 基于内存 ,而内存对于一个系统是最为宝贵的资源,而且它远远没有磁盘那么大,所以对于 Redis 的键值对的内存回收也是一个十分重要的问题,如果操作不当会产生 Redis岩机的问题,使得系统性能低下。
    热数据:经常被查询的放到Redis中,
    冷数据: 偶尔访问一次的放到磁盘的数据库中
  • 数据销毁的方式有两种
    1,del的方式,直接删除掉指定的key
    2,设置key的超时时间
  • 超时命令:
命令作用
expire key seconds设置超时时间戳,单位为秒
ttl key查看超时时间,-1表示没有超时时间,-2表示已经超时
persist key持久化key,取消超时时间

在这里插入图片描述

  • 关键问题:
    如果key超时了,Redis会回收key的存储空间吗?
    答案是不会。Redis的key超时了,不会被自动回收,只会标识哪些键值对超时了。
    Redis这么设计的原因是在于避免自动频繁回收,而造成的系统卡顿的情况。避免出现太多的内存碎片

Spring设置超时

@Test
public void testKeyTimeout2() throws  Exception{

   // 设置序列化方式方便客户端查看
   redisTemplate.setKeySerializer(new StringRedisSerializer());
   redisTemplate.setValueSerializer(new StringRedisSerializer());

   redisTemplate.opsForValue().set("k1","v1");
//redisTemplate.opsForValue().set("k1","v1",60,TimeUnit.SECONDS); // 创建key的时候设置超时时间

// 查看k1还有多久过期
   redisTemplate.expire("k1",60,TimeUnit.SECONDS);

   Thread.sleep(2000);
   System.out.println(redisTemplate.getExpire("k1"));
   System.out.println("-------------------------");
   Thread.sleep(2000);
   System.out.println(redisTemplate.getExpire("k1"));
}

Redis做为缓存服务的使用

查询

  • 查询的时候先到Redis缓存中查询,如果有就返回,如果没有就到数据库中查询,查询完后需要缓存重建。
public List<User> getUserList() {
    List<User> userList = null;
    // 先到数据库中查询
    userList = (List<User>) redisTemplate.opsForValue().get("userList");
    if (userList == null) {

        // 从数据库中查询
        userList = userDao.select(null);

        // 缓存重建
        redisTemplate.opsForValue().set("userList", userList);

        // 设置缓存失效时间
        redisTemplate.expire("userList", 5, TimeUnit.MINUTES); // 5s后失效
    }
    return userList;
}

删除

public int addUser(User user) {
    int i = userDao.insertSelective(user);
    redisTemplate.delete("userList"); // 更新完后删除缓存数据
    return i;
}

Redis锁处理

单体架构锁处理

@Override
public List<User> getUserList() {
    List<User> userList = null;
    userList= (List<User>) redisTemplate.opsForValue().get("userList");
    if(userList == null){
        synchronized (this){
            if(userList == null){
                System.out.println("查询数据库");
                userList = userDao.select(null);
                // 进行缓存重建
                redisTemplate.opsForValue().set("userList",userList);
                redisTemplate.expire("userList",5, TimeUnit.SECONDS); // 5s后失效
            }
        }
    }
    return userList;
}

分布式锁处理

  • 利用setnx机制实现分布式锁处理,setnx机制的:在指定的 key 不存在时,为 key 设置指定的值,如果存在就不设置。

@PostConstruct // 在当前bean初始化完成后(redisTemplate已经注入成功)调用该方法
public void init(){
    redisConnection = redisTemplate.getConnectionFactory().getConnection();
}

@Override
public List<User> getUserList() {
    List<User> userList = null;
    userList= (List<User>) redisTemplate.opsForValue().get("userList");
    if(userList == null){
        // 如果是true说明当前服务器已经拿到分布式锁
        Boolean look = redisConnection.setNX("lock".getBytes(), "true".getBytes());
        redisConnection.expire("lock".getBytes(),60); // 设置锁的超时时间为60s,60s后自动释放分布式锁
        if(look){
            if(userList == null){
                System.out.println("查询数据库");
                userList = userDao.select(null);

                // 进行缓存重建
                redisTemplate.opsForValue().set("userList",userList);
                redisTemplate.expire("userList",5, TimeUnit.MINUTES); // 5s后失效

                // 缓存完后要释放分布式锁,要不然永远就进行不了缓存重建
                redisConnection.del("lock".getBytes());
            }
        }else{
            // 如果没有拿到分布式锁就等待50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return  getUserList();
        }
    }
    return userList;
}

  • 在分布式锁的时候要注意以下三点
  1. 没有拿到分布式锁的线程要等待一段时间后再次调用查询方法
  2. 拿到分布式锁在进行缓存重建后要对锁进行释放
  3. 设置分布式锁的时候要设置超时时间,避免刚拿到锁就出现故障的问题

SpringBoot注解实现缓存服务器

添加依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
   <version>2.1.5.RELEASE</version>
</dependency>

启动添加开启缓存注解

@SpringBootApplication(scanBasePackages = "com.qf")
@MapperScan(basePackages = "com.qf.dao")
@EnableCaching // 开启缓存注解
public class UserRedisApplication {
   public static void main(String[] args) {
      SpringApplication.run(UserRedisApplication.class, args);
   }

自定义缓存过期时间

#缓存的类型是redis
spring.cache.type=redis

#缓存的过期时间是60s,单位是毫秒
spring.cache.redis.time-to-live=60000

通过注解操作缓存

  • 注解
    @Cacheable:用该注解修饰的方法表示,进入方法之前先查询缓存,最后把查询的结果放入到缓存中。
    Value:缓存名称中的前缀
    Key:缓存名称的后缀,需要用单引号引起来,在这个属性中可以通过#获取方法的返回值
    #result.id – result代表方法的对象
    #id – id代表方法的形参
    @CacheEvict:删除指定的缓存
    @CachePut:将方法的返回值添加到缓存中
    @Caching:可以设置多个相同的缓存注解

具体操作

@Cacheable(value = "user",key = "'List'") // 查询数据库之前先到缓存中查询,key需要加引号
 public List<User> getUserList() {
    System.out.println("查询数据库");
    return userDao.select(null);
}

 @CacheEvict(value = "user",key = "'List'") // 添加之前删除缓存
 @CachePut(value = "user",key = "'id'+#result.id") // 添加成功后添加单个的对象的缓存
 public User addUser(User user) {
     userDao.insertSelective(user);
    return user;
 }

@Override
@Caching(evict = {
        @CacheEvict(value = "user",key = "'id'+#user.id"), // 删除单个对象缓存
        @CacheEvict(value = "user",key = "'List'") // 删除列表缓存
})
 public int updateUser(User user) {
     return userDao.updateByPrimaryKey(user);
 }

 @Cacheable(value = "user",key = "'id'+#id")
 public User getUserById(Integer id) {
     System.out.println("查询了user:"+id);
     return userDao.selectByPrimaryKey(id);
 }

 //Caching执行多个相同的缓存
 @Caching(evict = {
     @CacheEvict(value = "user",key = "'id'+#id"), // 删除单个对象缓存
     @CacheEvict(value = "user",key = "'List'") // 删除列表缓存
 })
 public int deleteUser(Integer id) {
     return userDao.deleteByPrimaryKey(id);
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值