【Redis】封装Redis缓存工具解决缓存穿透与缓存击穿问题

这篇具有很好参考价值的文章主要介绍了【Redis】封装Redis缓存工具解决缓存穿透与缓存击穿问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基于StringRedisTemplate封装一个缓存工具,主要有一下几个方法

方法1:将任意Java对象序列化为json并存储在String的指定key中且设置TTL

方法2:将任意Java对象序列化为json并存储在String的指定key中,并可以设置逻辑过期时间,用户处理缓存击穿问题

方法3:根据指定的key进行查询缓存,并反序列化为指定类型,利用缓存空值的办法解决缓存穿透问题

方法4:根据指定的key进行查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题

此处使用到的互斥锁加锁解锁方法,在前面的文章有提及【Redis】Java通过redis实现简单的互斥锁_1373i的博客-CSDN博客https://blog.csdn.net/qq_61903414/article/details/130509874?spm=1001.2014.3001.5501



import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

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

    public RedisData(LocalDateTime expireTime, Object data) {
        this.expireTime = expireTime;
        this.data = data;
    }
}

/**
 * 封装解决redis 缓存击穿、缓存雪崩、缓存穿透等问题的方法
 */
@Component
@Slf4j
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 存入时设置过期时间
     * @param key
     * @param value
     * @param time
     * @param unit
     */
    public void set(String key, Object value, Long time, TimeUnit unit) throws JsonProcessingException {
        stringRedisTemplate.opsForValue().set(key,objectMapper.writeValueAsString(value),time,unit);
    }

    /**
     * 存入时设置逻辑过期
     * @param key
     * @param value
     * @param time
     * @param unit
     */
    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) throws JsonProcessingException {
        RedisData data = new RedisData(LocalDateTime.now().plusSeconds(unit.toSeconds(time)),value);
        stringRedisTemplate.opsForValue().set(key,objectMapper.writeValueAsString(data));
    }

    /**
     * 查询缓存,缓存穿透时缓存null解决
     * @param id  要查询的id
     * @param keyPrefix   redis key的前缀
     * @param type        要返回的类型
     * @param dbFallback  查询数据库的方法的结果
     * @param <R>
     * @param <I>
     * @return
     * @throws JsonProcessingException
     */
    public <R,I> R getWithPassThrough(I id, String keyPrefix, Class<R> type, Function<I,R> dbFallback,Long time,TimeUnit unit) throws JsonProcessingException {
        // 生成redis中的key
        String key = keyPrefix + id;

        // 查询redis
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)) {
            // 存在缓存,则返回
            return objectMapper.readValue(json,type);
        }

        // 不存在时,判断是否是空值
        if (json != null) {
            // 返回错误信息
            return null;
        }

        // 查询数据库
        R result = dbFallback.apply(id);
        // 判空
        if (result == null) {
            // 发送缓存穿透,缓存null
            stringRedisTemplate.opsForValue().set(key,"",time,unit);
            return null;
        }

        // 存在则重建缓存
        stringRedisTemplate.opsForValue().set(key,objectMapper.writeValueAsString(result),time,unit);
        return result;
    }

    /**
     * 查询缓存,逻辑过期后异步重建缓存
     * @param id
     * @param keyPrefix
     * @param type
     * @param dbFallback
     * @param time
     * @param unit
     * @param <R>
     * @param <I>
     * @return
     * @throws JsonProcessingException
     */
    public <R,I> R get(I id, String keyPrefix,Class<R> type,Function<I,R> dbFallback,Long time,TimeUnit unit) throws JsonProcessingException {
        // 构建查询redis的key
        String key = keyPrefix + id;

        // 查询redis
        String json = stringRedisTemplate.opsForValue().get(key);
    

        // 反序列化转为RedisData对象
        RedisData data = objectMapper.readValue(json,RedisData.class);
        // 获取数据
        R r = JSONUtil.toBean((JSONObject) data.getData(),type);
        // 获取逻辑时间
        LocalDateTime expireTime = data.getExpireTime();

        // 判断是否过期
        if (expireTime.isAfter(LocalDateTime.now())) {
            // 没有过期
            return r;
        }

        // 到达此处已过期,重建缓存:加互斥锁--》查询数据库--》重建--》释放互斥锁
        // 1.获取互斥锁
        String lockKey = id + "_lock";
        boolean isLock = LockByRedis.tryLock(lockKey);

        if (isLock) {
            // 2.加锁成功,开启线程重建缓存
            new Thread(){
                @Override
                public void run() {

                    try {
                        // 3.查询数据库
                        R db = dbFallback.apply(id);
                        // 4.重建
                        stringRedisTemplate.opsForValue().set(key,objectMapper.writeValueAsString(db),time,unit);
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    } finally {
                        // 5.释放互斥锁
                        LockByRedis.unlock(lockKey);
                    }
                }
            }.start();
        }

        // 返回逻辑过期的数据
        return r;
    }
}

难点:在重建缓存时,我们需要去查询数据库,而查询数据库不同的reids缓存重建所需要查询的数据库表可能不同,其方法也可能不同。

解决:通过Function<参数类型,返回值>来把该查询数据库的方法交给方法的调用者进行传入文章来源地址https://www.toymoban.com/news/detail-464400.html

到了这里,关于【Redis】封装Redis缓存工具解决缓存穿透与缓存击穿问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Redis - 三大缓存问题(穿透、击穿、雪崩)

    概念: 查询一个数据库中也不存在的数据,数据库查询不到数据也就不会写入缓存,就会导致一直查询数据库 解决方法: 如果数据库也查询不到,就把空结果进行缓存 缺点是 - 消耗内存 布隆过滤器的作用 :检索一个元素是否在某个集合中 布隆过滤器由组成 : 位图 + 若干

    2024年02月14日
    浏览(40)
  • Redis 如何解决缓存雪崩、缓存击穿、缓存穿透难题

    Redis 作为一门热门的缓存技术,引入了缓存层,就会有缓存异常的三个问题,分别是缓存击穿、缓存穿透、缓存雪崩。我们用本篇文章来讲解下如何解决! 缓存击穿 : 指的是缓存中的某个热点数据过期了,但是此时大量的并发请求访问这个key的值,此时因为缓存过期无法从

    2024年02月14日
    浏览(42)
  • redis缓存穿透、缓存击穿、缓存雪崩及对应解决方法

    缓存穿透、击穿和雪崩是缓存使用中的常见问题,对它们的理解和相应的解决方法对于维护系统性能和稳定性至关重要。 定义 : 当客户端请求的数据在缓存中和数据库中都不存在时,该请求会直接打到数据库上,这种情况称为缓存穿透。如果持续请求这样的数据,会给数据

    2024年04月08日
    浏览(46)
  • redis缓存雪崩、穿透、击穿解决方案

    关于缓存异常,我们常见的有三个问题:缓存雪崩、缓存击穿、缓存穿透。这三个问题一旦发生,会导致大量请求直接落到数据库层面。如果请求的并发量很大,会影响数据库的运行,严重的会导致数据库宕机。 为了避免缓存异常带来的损失,我们需要了解每种异常的原因以

    2024年02月10日
    浏览(73)
  • Redis缓存问题(穿透, 击穿, 雪崩, 污染, 一致性)

    目录 1.什么是Redis缓存问题? 2.缓存穿透 3.缓存击穿 4.缓存雪崩 5.缓存污染(或满了)    5.1 最大缓存设置多大    5.2 缓存淘汰策略 6.数据库和缓存一致性    6.1 4种相关模式    6.2 方案:队列+重试机制    6.3 方案:异步更新缓存(基于订阅binlog的同步机制) 在高并发的业

    2024年02月12日
    浏览(33)
  • Redis什么是缓存穿透、击穿、雪崩?如何解决

    通常后端会采用Mysql等磁盘数据库,可以持久化但是访问慢,高并发时性能差,需要设置Nosql内存型数据库缓存:Redis等 但缓存可能出现:缓存穿透、缓存击穿、缓存雪崩等问题 查找数据的顺序是:先查找缓存,再查找数据库 当查找一个数据时,缓存没有都会请求数据库,当

    2024年02月09日
    浏览(62)
  • Redis:缓存击穿、缓存穿透与缓存雪崩的区别、解决方案

            近期学习redis相关原理,记录一下开发过程中Redis的一些常见问题及应对方法。 一句话总结:先查 redis发现没数据 ,再去 数据库查发现还是没数据 。 这种情况下 缓存永远不会生效 ,数据库将承担巨大压力。         我们知道,redis的缓存作用,是在客户端发起查

    2024年02月06日
    浏览(49)
  • redis中缓存雪崩,缓存穿透,缓存击穿的原因以及解决方案

    在redis中,新,旧数据交替时候,旧数据进行了删除,新数据没有更新过来,造成在高并发环境下,大量请求查询redis没有数据,直接查询mysql,造成mysql的压力骤增,给mysql造成极大的压力,造成一连串的后续异常反应。 1.大多数系统设计者 考虑用加锁 (最多的解决方案)或

    2024年02月15日
    浏览(54)
  • Redis缓存问题:穿透,击穿,雪崩,双写一致性等

    在高并发场景下,数据库往往是最薄弱的环节,我们通常选择使用 redis 来进行缓存,以起到缓冲作用,来降低数据库的压力,但是一旦缓存出现问题,也会导致数据库瞬间压力过大甚至崩溃,从而导致整个系统崩溃.今天就聊聊常见的 redis 缓存问题. 缓存击穿 缓存击穿一般指redis中的一

    2024年04月27日
    浏览(50)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包