0
点赞
收藏
分享

微信扫一扫

初级会计和中级会计的最大差别,不仅仅是多了一门财务管理!

曾宝月 04-13 18:30 阅读 1

Redis-缓存击穿-逻辑过期实现

缓存击穿:也称热点key问题,大量访问一个key,而这个key恰巧到期了,导致大量的请求访问数据库。增大数据库的负担。为了解决这个问题可以采用互斥锁或逻辑过期的方式解决。本章采用逻辑过期的方式解决此问题。

流程图:

第一点需要进行缓存预热,把经常用的key预先缓存到redis中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    /** 缓存数据KEY */
    private final String CACHE_SHOP_KEY = "CACHE_SHOP_KEY:";
    /** 缓存互斥锁KEY */
    private final String CACHE_SHOP_LOCK_KEY = "CACHE_SHOP_LOCK_KEY:";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);





/**
     * redis缓存
     * 缓存击穿
     */
    @Override
    public BooksVo selectById(Long bookId) {
        // 缓存击穿-互斥锁
        // return tryCacheMutex(bookId);

        // 缓存击穿-逻辑过期时间
        return tryCacheMutex2(bookId);
    }



/**
     * redis缓存
     * 缓存击穿-逻辑过期版本
     * @param bookId
     */
    private BooksVo tryCacheMutex2(Long bookId) {
        // RedisKey
        String cacheKey = CACHE_SHOP_KEY + bookId;
        // 1.从Redis查询商铺缓存
        // 获取缓存数据
        String contentBook = stringRedisTemplate.opsForValue().get(cacheKey);
        // 2.判断缓存是否命中
        if (StringUtils.isBlank(contentBook)){
            // 3.1缓存未命中 直接返回结果
            return null;
        }

       // 3.2缓存命中-获取数据
        RedisData redisData = JSONUtil.toBean(contentBook, RedisData.class);
        BooksVo booksVo = JSONUtil.toBean((JSONObject) redisData.getData(), BooksVo.class);
        LocalDateTime expireSecond = redisData.getExpireSecond();
        //  4.缓存未过期 直接返回数据
        if (expireSecond.isAfter(LocalDateTime.now())){
            return booksVo;
        }
        // 5.缓存过期-获取互斥锁
        if (tryLock(bookId)){
            // check double
            String s = stringRedisTemplate.opsForValue().get(cacheKey);
            RedisData redisData1 = JSONUtil.toBean(s, RedisData.class);
            if (BooleanUtil.isFalse(redisData1.getExpireSecond().isAfter(LocalDateTime.now()))){
                // 获取互斥锁成功,开启独立线程,缓存重建
                CACHE_REBUILD_EXECUTOR.submit(() -> {
                    try {
                        // 重建缓存
                        saveCacheBook(bookId, 20L);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    } finally {
                        // 释放互斥锁
                        stringRedisTemplate.delete(CACHE_SHOP_LOCK_KEY + bookId);
                    }
                });
            }
        }
        // 返回已过期的数据
        return booksVo;
    }
                
                
                
     /**
     * 保存缓存信息
     */
    public void saveCacheBook(Long bookId, Long expireSeconds){
        // 1.查询数据库数据
        BooksVo booksVo = this.queryById(bookId);
        // 2.封装逻辑过期时间
        RedisData redisData = new RedisData();
        redisData.setData(booksVo);
        // 获取当前的时间 + 指定秒数
        redisData.setExpireSecond(LocalDateTime.now().plusSeconds(expireSeconds));
        // 3.写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + bookId, JSONUtil.toJsonStr(redisData));
    }

redisData实体类

@Data
public class RedisData {
    /** 逻辑过期时间 */
    private LocalDateTime expireSecond;

    /** 拓展实体类 */
    private Object data;
}
举报

相关推荐

0 条评论