Redis使用 springboot整合redis以及使用场景

StringRedisTemplate是RedisTemplate的子类。StringRedisTemplate它的泛型key和value都是String类型。RedisTemplate它的key value的泛型是Object。

springboot整合redis

介绍

StringRedisTemplate和RedisTemplate是java操作redis数据库提供的两个类库

本文通过springboot整合redis

步骤:
引入操作redis数据的依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

进行数据库连接,配置数据源 application.properties

spring.redis.host=192.168.31.31
spring.redis.port=6379
spring.redis.jedis.pool.max-active=20
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-wait=20000

测试

@Autowired
private StringRedisTemplate redisTemplate;

@Test
public void contextLoads() {
   //获取所有key
   Set<String> keys = redisTemplate.keys("*");
   //删除key
   redisTemplate.delete("kkk");
   //设置key的过期时间
   redisTemplate.expire("k3", 60, TimeUnit.SECONDS);
   //获取到设置的过期时间
   redisTemplate.getExpire("k3");
   //操作字符串
   redisTemplate.opsForValue();
}

RedisTemplate

RedisTemplate的数据存放

RedisTemplate该类可以存放任意类型的数据,但是该类型的数据必须实现序列,获取redis中对应的数据时,会进行反序列化。 如果使用RedisTemplate建议大家指定key,value,以及hashkey的序列化方式。

RedisTemplate的配置类

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    //比如验证码
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

springboot连接集群

两种集群模式:哨兵和去中心化
哨兵模式的配置

spring.redis.sentinel.master=master
spring.redis.sentinel.nodes=192.168.31.31:26379

去中心化集化的配置

spring.redis.cluster.nodes=192.168.31.31:8000 192.168.31.31:8001 192.168.31.31:8002  192.168.31.31:8003  192.168.31.31:8004  192.168.31.31:8005

Redis的用途

作为缓存

作用:减少数据库的访问频率。 提高数据的访问率。

热点数据 、修改频率比较低、安全系数低的可以放入缓存

测试:
代码实现
搭建项目,配置redis省略

public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisTemplate redisTemplate;

    public User findById(Integer deptId){
        //1.从缓存中查询该数据
        Object o = redisTemplate.opsForValue().get("findById::" + deptId);
        if(o!=null){//表示从缓存中获取该数据
            return (Dept) o;
        }
        User user = userDao.selectById(userId);
        redisTemplate.opsForValue().set("findById::"+userId,user);//把查询的结果放入缓存
        return user;
    }


    //数据库和缓存同步问题!
    public int delete(Integer userId){
        redisTemplate.delete("findById::"+userId);//删除缓存
        int i = userDao.deleteById(userId);
        return i;
    }

    public int update(User user){
        redisTemplate.delete("findById::"+user.getUserId());//删除缓存
        int i = userDao.updateById(user);
        redisTemplate.opsForValue().set("findById::"+user.getDeptId(),user);
        return i;
    }
}

使用注解实现
主启动类:

@SpringBootApplication
@MapperScan(basePackages = "com.redis.dao")
@EnableCaching //开启缓存的注解
public class SpringbootRedisApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRedisApplication.class, args);
    }

}

业务层:

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    //该注解作用:会先查询缓存,如果缓存存在,则不会执行代码块。 如果缓存中不存在则执行该方法,并把该方法的返回值存放到redis中
    @Cacheable(cacheNames = "findById",key = "#userId")  //缓存的key值 为findById
    public Dept findById(Integer userId){
        User user = userDao.selectById(userId);
        return user;
    }


    //数据库和缓存同步问题!
    // beforeInvocation:是否在方法执行前就清空,缺省为 false,
    // 如果指定为 true,则在方法还没有执行的时候就清空缓存。缺省情况下,如果方法执行抛出异常,则不会清空缓存。
    @CacheEvict(cacheNames = "findById",key = "#userId")
    public int delete(Integer userId){
        int i = userDao.deleteById(userId);
        return i;
    }

    //这个注解是必须执行方法体,而且会把方法体执行的结果放入到缓存中。 如果发生异常则不操作缓存。
    @CachePut(cacheNames = "findById",key = "#user.userId")
    public User update(Dept dept){
        int i = userDao.updateById(dept);
        return user;
    }
}

分布式锁

概念:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源访问。
注意:不可使用synchronized,它只可锁同一系统中的不同线程

关键:

锁信息要设置超时,不能让一个线程长期占用锁导致思索
同一时刻只能有一个线程获得锁

代码实现

@Service
public class StockService {

    @Resource
    private StockDao stockDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public String decrStock(Integer productId) {//synchronized () 同步方法    同步代码块
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("product::" + productId, "ykq",30, TimeUnit.SECONDS);
        //查询对应的id的库存
         if(flag) {//获取锁了
             try {
                 Stock stock = stockDao.selectById(productId);
                 if (stock.getNum() > 0) {
                     //根据id修改库存
                     sleep
                     stock.setNum(stock.getNum() - 1);
                     stockDao.updateById(stock); //异常发生
//                   int c=1/0;
                     System.out.println("库存剩余:" + (stock.getNum()));
                     return "库存减少成功";
                 } else {
                     return "库存不足";
                 }
             }catch (Exception e){
                  throw  new RuntimeException(e.getMessage());
             }
             finally {
                 redisTemplate.delete("product::" + productId);//释放锁资源 一定再finally
             }
         }else{
             System.out.println("服务器正忙请稍后再试..........");
             return "服务器正忙请稍后再试..........";//可重复调用
         }
    }
}

使用Redisson实现分布式锁

步骤:

  1. 导入依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.15.0</version>
</dependency>
  1. 配置类
 @Bean
 public RedissonClient getRedisson(){
     Config config=new Config();
     //config.useSingleServer().setAddress("redis://192.168.31.199:6379");
     config.useClusterServers().addNodeAddress(
                "redis://192.168.31.31:8000",
                "redis://192.168.31.31:8001",
                "redis://192.168.31.31:8002",
                "redis://192.168.31.31:8003",
                "redis://192.168.31.31:8004",
                "redis://192.168.31.31:8005"
        );
     RedissonClient redissonClient = Redisson.create(config);
     return redissonClient;
 }
  1. 测试
    使用trylock()方法解决
@Service
public class ProductService {

    @Autowired
    private ProductDao productDao;

    @Autowired
    private RedissonClient redissonClient;

    public String reduceStock(Integer productId) {
        RLock lock = redissonClient.getLock("lock::" + productId);
        if (lock.tryLock()) {
            try {
                Product product = productDao.selectById(productId);
                if (product != null && product.getCount() > 0) {
                    product.setCount(product.getCount() - 1);
                    productDao.updateById(product);
                    System.out.println("剩余票数为:" + product.getCount());
                    return "成功";
                } else {
                    return "没了";
                }
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            } finally {
                if (lock.isLocked()) {
                    lock.unlock(); // 释放锁
                }
            }
        }
        return reduceStock(productId);
    }
}

使用lock()方法:

@Service
public class ProductService {

    @Autowired
    private ProductDao productDao;

    @Autowired
    private RedissonClient redissonClient;

    public String reduceStock(Integer productId) {
        RLock lock = redissonClient.getLock("lock::" + productId);
        lock.lock();
        try {
            Product product = productDao.selectById(productId);
            if (product != null && product.getCount() > 0) {
                product.setCount(product.getCount() - 1);
                productDao.updateById(product);
                System.out.println("剩余票数为:" + product.getCount());
                return "成功";
            } else {
                return "没了";
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (lock.isLocked()) {
                lock.unlock(); // 释放锁
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值