04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿

这篇具有很好参考价值的文章主要介绍了04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习目标:

提示:学习如何利用Redis逻辑过期实现添加缓存功能解决缓存击穿


学习产出:

缓存击穿讲解图
04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿,Redis,缓存,redis,数据库

解决方案:

  1. 采用互斥锁
  2. 采用逻辑过期

1. 准备pom环境

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>

2. 配置ThreadLocal和过滤器

public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate redis;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/code","/user/login","/blog/hot","/shop/**","/shop-type/**","/voucher/**").order(2);
        registry.addInterceptor(new RefreshTokenInterceptor(redis)).addPathPatterns("/**").order(1);
    }
}
---------------------------------------------
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    //controller执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.判断是否需要拦截ThreadLocal
        if (UserHolder.getUser()==null) {
            response.setStatus(401);
            return false;
        }
        //7.放行
        return true;
    }
    //渲染后返回给前台数据前
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户,避免内存泄露
        UserHolder.removeUser();
    }
}
---------------------------------------------------
@Slf4j
public class RefreshTokenInterceptor implements HandlerInterceptor {
    //这个对象不是由spring管理的所以不能用注解自动注入

    private StringRedisTemplate redis;

    public RefreshTokenInterceptor(StringRedisTemplate redis) {
        this.redis = redis;
    }

    //controller执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        //2.基于token获取redis中的用户
        //通过key取到hash中的map集合数据
        Map<Object, Object> userMap = redis.opsForHash().entries("login:token:" + token);
        //3.判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        //5.将查询到的hash数据转为userDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //6.存在,保存用户信息到ThreadLocal中
        UserHolder.saveUser(userDTO);
        //7.刷新token有效期
        redis.expire(LOGIN_USER_KEY + token, 30, TimeUnit.MINUTES);
        log.info("我是第一个拦截器当前拦截所有请求的用户为,线程为{},{}",UserHolder.getUser(),Thread.currentThread());
        //8.放行
        return true;
    }

3. RedisData接收数据

@Data
public class RedisData {
    private LocalDateTime expireTime;
    private Object data;
}

3. Controller层:负责接收请求和向下分配

@RestController
@RequestMapping("/shop")
public class ShopController {
    @Resource
    public IShopService shopService;
    /**
     * 根据id查询商铺信息
     * @param id 商铺id
     * @return 商铺详情数据
     */
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
        return Result.ok(shopService.queryShopById(id));
    }
}

4. Service层:负责业务的处理逻辑文章来源地址https://www.toymoban.com/news/detail-646918.html

@Service
@Slf4j
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Resource
    private StringRedisTemplate redis;
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);
    private boolean tryLock(String key) {
        Boolean setnx = redis.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.MINUTES);
        return BooleanUtil.isTrue(setnx);
    }
    private void unlock(String key) {
        redis.delete(key);
    }
    public Result queryShopById(Long id) {
        Shop shop = queryShopWithLogicExpire(id);
        if (shop == null) {
            return Result.fail("店铺不存在");
        }
        return Result.ok(shop);
    }
    //利用逻辑过期解决缓存击穿问题
    private Shop queryShopWithLogicExpire(Long id) {
        //1.从Redis查询商品缓存
        String cacheShop = redis.opsForValue().get("cache:shop:" + id);
        //2.未命中
        if (ObjectUtil.isEmpty(cacheShop)) {
            return null;
        }
        //3.命中
        RedisData redisDataWithShop = JSONUtil.toBean(cacheShop, RedisData.class);
        LocalDateTime expireTime = redisDataWithShop.getExpireTime();
        JSONObject shopData = (JSONObject) redisDataWithShop.getData();
        Shop shop = JSONUtil.toBean(shopData, Shop.class);
        //3.1判断缓存是否过期
        if (expireTime.isAfter(LocalDateTime.now())) {
            //3.2未过期,返回
            return shop;
        }
        //4.已过期,需要重建缓存
        //5.缓存重建
        //5.1获取互斥锁
        String lock = "lock:shop:" + id;
        boolean isLock = tryLock(lock);
        //5.2判断互斥锁是否成功
        if (isLock) {
            // TODO: 2023/8/9  //5.3成功,开启独立线程,实现缓存重建
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                //缓存重构
                try {
                    Shop shopItem = getById(id);
                    RedisData redisData = new RedisData();
                    redisData.setData(shopItem);
                    redisData.setExpireTime(LocalDateTime.now().plusSeconds(180L));
                    redis.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(redisData));
                } finally {
                    unlock(lock);
                }
            });
        }
        //5.4返回过期商铺信息
        return shop;
    }
}

到了这里,关于04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用Spring Boot实现Redis键过期回调功能

    当使用Redis作为缓存或数据存储的时候,有时候需要在键过期时执行一些特定的操作,比如清除相关数据或发送通知。在Spring Boot中,可以通过实现 RedisMessageListener 接口来实现Redis键过期回调功能。下面是一个实现Redis键过期回调功能的Spring Boot应用的示例: 步骤一:引入依赖

    2024年02月16日
    浏览(29)
  • Redis缓存设计与性能优化【缓存和数据库不一致问题,解决方案:1.加过期时间这样可以一段时间后自动刷新 2.分布式的读写锁】

    在大并发下,同时操作数据库与缓存会存在数据不一致性问题 1、双写不一致情况 2、读写并发不一致 解决方案: 1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等),这种几乎不用考虑这个问题,很少会发生缓存不一致, 可以给缓存数据加上过期时间,每隔一

    2024年04月13日
    浏览(34)
  • Redis 缓存过期及删除

    物理内存达到上限后,像磁盘空间申请虚拟内存(硬盘与内存的swap),甚至崩溃。 内存与硬盘交换 (swap) 虚拟内存,频繁I0 性能急剧下降,会造成redis内存急剧下降;  一般设置物理内存的3/4,在redis.conf中 maxmemory 1024mb 声明数据expires来存储过期时间,redis使用c语言开发,dict是字

    2024年02月13日
    浏览(31)
  • Redis的缓存过期淘汰策略

    生产上你们redis内存设置多少? 如何配置,修改redis的内存大小? 如果内存满了怎么办? redis清理内存的方式?定期删除和惰性删除了解过吗? redis缓存淘汰策略有哪些?分别是什么?你用哪个? redis的LRU了解过吗?请手写LRU。 LRU和LFU算法的区别是什么? … 如何查看Redis最

    2024年02月07日
    浏览(37)
  • 项目记录:利用Redis实现缓存以提升查询效率

    当我们查询所有数据时,如果缓存中没有,则去数据库查询,如果有,直接查缓存的数据就行。注意定期更新缓存数据。 BoundHashOperations是绑定键值的方法,意味着之后的操作都是对此键进行操作。 ObjectMapper类提供了一系列json序列化和反序列化的操作。 缓存更新操作是通过

    2024年02月03日
    浏览(35)
  • springboot监听Redis 缓存过期(Key 失效)事件

    事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发, 故需要开启 redis 的事件监听与发布 修改 redis.conf 文件(Windows上是redis.windows.conf和redis.windows-service.conf) 通过开启key过期的事件通知,当key过期时,会发布过期事件;我们定义key过期事件的监听器,当key过期时,就能收到

    2024年02月12日
    浏览(29)
  • 02.Redis实现添加缓存功能

    提示:学习如何利用Redis实现添加缓存功能 流程图 1. 准备pom环境 2. 配置ThreadLocal和过滤器 3. Controller层:负责接收请求和向下分配 4. Service层:负责业务的处理逻辑

    2024年02月14日
    浏览(35)
  • (高阶) Redis 7 第19讲 缓存过期淘汰策略 大厂篇

    1. 生产上,redis内存设置的多少 2. 如何配置、修改Redis 内存大小 3. 如果内存满了,如何处理 4. Redis 清理内存的方式有哪些?定期删除和惰性删除了解吗 5. Redis 缓存淘汰策略有哪些?分别是什么?你用哪个 6. Redis 的LRU了解过吗?请手写LRU 7. LRU和LFU 算法的区别是什么  Redis 默

    2024年02月07日
    浏览(32)
  • mall整合Redis实现缓存功能

    本文主要讲解mall整合Redis的过程,以短信验证码的存储验证为例。 Redis是用C语言开发的一个高性能键值对数据库,可用于数据缓存,主要用于处理大量数据的高访问负载。 下载Redis,下载地址:github.com/MicrosoftAr… 下载完后解压到指定目录 在当前地址栏输入cmd后,执行redis的启

    2024年01月19日
    浏览(33)
  • 【Redis系列】Spring Boot 集成 Redis 实现缓存功能

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年04月10日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包