栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis三大难点之缓存击穿

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis三大难点之缓存击穿

文章目录
    • 缓存击穿
    • 解决方案
    • 基于互斥锁方式解决缓存击穿问题
      • 设计思想
      • 代码实现
    • 基于逻辑过期方式解决缓存击穿问题
      • 设计思想
      • 代码实现

缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

解决方案
  • 互斥锁
  • 逻辑过期

基于互斥锁方式解决缓存击穿问题 设计思想

代码实现
    
    @Override
    public ResponseEntity getUserByIdWithMutex(String id) {

        // 从redis中根据key查询用户列表信息
        String user = (String) redisTemplate.opsForValue().get("user:" + id);

        // 如果用户信息不为null、""、"tn",则存在,转化为Issa,返回
        if (StrUtil.isNotBlank(user)) {
            Issa issa = JSONUtil.toBean(user, Issa.class);
            return ResponseEntity.ok(issa);
        }

        // 如果user不为null,说明user为空字符串,直接返回
        if (user != null) {
            return ResponseEntity.status(400).body("没有用户信息");
        }
        Issa userById = null;
        try {
            // 获取锁
            boolean lockFlag = BooleanUtil.isTrue(redisTemplate.opsForValue().setIfAbsent("lock:" + id, "1", 10, TimeUnit.SECONDS));
            // 如果没有得到锁,则休眠50ms,递归调用该方法
            if (!lockFlag) {
                Thread.sleep(50);
                return getUserByIdWithMutex(id);
            }
            // 如果得到了锁,则去数据库查询
            userById = userMapper.getUserById(id);

            // 如果为null,说明数据库没有该数据,为了避免缓存穿透,存上""空字符串,并以TTL过期时间作为兜底方案
            if (userById == null) {
                // 将空值写入redis
                redisTemplate.opsForValue().set("user:" + id, "", 1, TimeUnit.MINUTES);
                // 返回错误信息
                return ResponseEntity.status(400).body("没有用户信息");
            }

            // 如果不为null,将该数据缓存到redis服务器
            redisTemplate.opsForValue().set("user:" + id, userById, 60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 最后释放锁
            redisTemplate.delete("lock:" + id);
        }
        // ok
        return ResponseEntity.ok(userById);
    }
 
基于逻辑过期方式解决缓存击穿问题 
设计思想 

代码实现
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    
    @Override
    public ResponseEntity getUserByIdWithLogicalExpire(String id) {

        // 从redis中根据key查询用户列表信息
        String user = (String) redisTemplate.opsForValue().get("user:" + id);

        // 如果没有用户信息
        if (StrUtil.isBlank(user)) {
            return ResponseEntity.status(400).body("没有用户信息");
        }

        RedisData redisData = JSONUtil.toBean(user, RedisData.class);
        Issa issa = JSONUtil.toBean((JSONObject) redisData.getData(), Issa.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        if(expireTime.isAfter(LocalDateTime.now())) {
            return ResponseEntity.ok(issa);
        }
        boolean lockFlag = BooleanUtil.isTrue(redisTemplate.opsForValue().setIfAbsent("lock:" + id, "1", 10, TimeUnit.SECONDS));

        if (lockFlag) {
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                try {
                    // 查询数据库
                    Issa userById = userMapper.getUserById(id);
                    // 重建缓存
                    // 设置逻辑过期
                    RedisData newRedisData = new RedisData();
                    newRedisData.setData(issa);
                    newRedisData.setExpireTime(LocalDateTime.now().plusSeconds(TimeUnit.SECONDS.toSeconds(60)));
                    // 写入Redis
                    redisTemplate.opsForValue().set("user:" + id, JSONUtil.toJsonStr(newRedisData));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }finally {
                    // 释放锁
                    redisTemplate.delete("lock:" + id);
                }
            });
        }
        // ok
        return ResponseEntity.ok(issa);
    }
 
@Data
public class RedisData {
    private LocalDateTime expireTime;
    private Object data;
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号