redis的应用,缓存,分布式锁

发布于:2025-02-19 ⋅ 阅读:(18) ⋅ 点赞:(0)

1.应用

1.1可以用作缓存

作用:提交数据的查询效率,减少对数据库的访问频率

什么数据时候放入缓存

1.修改频率高

2.访问频率高的数据

3.对安全系数比较低

 如何实现

@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    public Dept selectById(Integer id){
        //查询是否在缓存
        ValueOperations forValue = redisTemplate.opsForValue();
        Object o = forValue.get("dept::" + id);
        if(o!=null){
            return (Dept) o;
        }
        Dept dept = deptMapper.selectById(id);
        //存储缓存
        forValue.set("dept::" + id,dept);
        return dept;
    }
}

1.1.2缓存注解

spring4.0以后,提供了缓存注解,可以使用注解完成缓存功能,无需自己写

1.在配置类中添加缓存配置

 @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);
        //配置序列化(解决乱码的问题),过期时间600s
        RedisCacheConfiguration config=RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))//设置过期时间
                //设置key序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                //设置value序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager=RedisCacheManager.builder(factory).cacheDefaults(config).build();
        return cacheManager;
     }

2.使用注解@Cacheable

@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;
    //@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
    @Cacheable(value = "dept",key = "#id")
    public Dept selectById(Integer id){
        Dept dept = deptMapper.selectById(id);
        return dept;
    }
}

3.开启缓存注解

package com.ghx.server;

import com.ghx.dao.DeptMapper;
import com.ghx.pojo.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author :guo
 * @date :Created in 2025/2/18 14:22
 * @description:
 * @version:
 */
@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;
    //@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
    @Cacheable(value = "dept",key = "#id")
    public Dept selectById(Integer id){
        Dept dept = deptMapper.selectById(id);
        return dept;
    }
    //先执行方法体,再删除缓存
    @CacheEvict(value = "dept",key = "#id")
    public int delete(Integer id){
        return deptMapper.deleteById(id);
    }

    //先修改数据库,再修改缓存,把dept对象存入
    @CachePut(value = "dept",key = "#dept.id")
    public Dept update(Dept dept){
        deptMapper.updateById(dept);
        return dept;
    }

}
package com.ghx.controller;

import com.ghx.pojo.entity.Dept;
import com.ghx.server.DeptServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author :guo
 * @date :Created in 2025/2/18 14:24
 * @description:
 * @version:
 */
@RestController
public class DeptController {
    @Autowired
    private DeptServer deptServer;

    @GetMapping("selectById/{id}")
    public Dept selectById(@PathVariable Integer id) {
        //@PathVariable获取请求参数的{}参数
        return deptServer.selectById(id);
    }

    @GetMapping("delete/{id}")
    public int delete(@PathVariable Integer id) {
        return deptServer.delete(id);
    }

    @PutMapping("update")
    public Dept update(@RequestBody Dept dept) {
        return deptServer.update(dept);
    }
}

1.1.3缓存注解

@Cacheable针对查询的,缓存名如"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
@CacheEvict先执行方法体,再删除缓存
@CachePut先修改数据库,再修改缓存,把dept对象存入

1.2可以作为分布式锁

synchronized:

lock:接口

        实现类:

        ReentrantLock:重入锁
        ReentrantReadWriteLock:读写分离锁

                        加的为读锁则线程不堵塞

                        加的是写锁线程堵塞

上面的都是本地锁

本地锁

 //如果在单线程下---该功能没有任何问题。
    //如果在多线程的情况---jmeter压测工具---发现出现线程安全问题了。==使用锁synchronized ()或lock锁。
    //发现使用锁之前没有问题了。但是如果该项目是一个集群。--发现在集群的情况下本地锁【只对当前工程有效】无效了。
    //解决方案就是集群工程共用一把锁就行。---可以使用redis来解决问题。
public String decrement(Integer productid) {
        //根据id查询商品的库存
        synchronized (this) {
            int num = stockDao.findById(productid);
            if (num > 0) {
                //修改库存
                stockDao.update(productid);
                System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
            } else {
                System.out.println("商品编号为:" + productid + "的商品库存不足。");
                return "商品编号为:" + productid + "的商品库存不足。";
            }
        }
    }

手动实现

@Autowired
    private StringRedisTemplate redisTemplate;


    public String decrement(Integer productid) {
        ValueOperations<String,String> forValue = redisTemplate.opsForValue();
        //判断是否获取锁   如果程序死机了 ,无法执行完成
        //如果代码没有执行完成,会释放锁。      ——开启了一个守护线程,每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s
        //分配3次,33次后直接释放锁
        Boolean aBoolean = forValue.setIfAbsent("product::" + productid, "~~~~");
        if(aBoolean) {
            try {
                //根据id查询商品的库存
                int num = stockDao.findById(productid);
                if (num > 0) {
                    //修改库存
                    stockDao.update(productid);
                    System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                    return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
                } else {
                    System.out.println("商品编号为:" + productid + "的商品库存不足。");
                    return "商品编号为:" + productid + "的商品库存不足。";
                }
            } finally {
                redisTemplate.delete("product::" + productid);
            }

        }
        System.out.println("服务器正忙");
        return "服务器正忙";
    }

 Redisson

 每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s

依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.24.3</version>
</dependency>
 @Autowired
    private Redisson redisson;

    public String decrement(Integer productid) {
        RLock lock = redisson.getLock("product::" + productid);

        try {
            //必须为30s
            lock.lock(30, TimeUnit.SECONDS);
                //根据id查询商品的库存
                int num = stockDao.findById(productid);
                if (num > 0) {
                    //修改库存
                    stockDao.update(productid);
                    System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                    return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
                } else {
                    System.out.println("商品编号为:" + productid + "的商品库存不足。");
                    return "商品编号为:" + productid + "的商品库存不足。";
                }
        } finally {
             lock.unlock();
        }


    }

 


网站公告

今日签到

点亮在社区的每一天
去签到