Redis常见缓存问题

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

目录

缓存穿透

造成缓存穿透的原因

缓存穿透问题解决方案

1、缓存空对象返回

2、布隆过滤器

缓存失效(击穿)

缓存雪崩

热点缓存key重建优化

缓存与数据库双写不一致

1、双写不一致情况

2、读写并发不一致

解决方案


缓存穿透

        缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中, 通常出于容错的考虑, 如果从存储层查不到数据则不写入缓存层。缓存穿透将导致不存在的数据每次请求都要到存储层去查询, 失去了缓存保护后端存储的意义。

造成缓存穿透的原因

1. 自身业务代码或者数据出现问题。

2.  一些恶意攻击、 爬虫等造成大量空命中。

缓存穿透问题解决方案
1、缓存空对象返回
String get(String key) {
    // 从缓存中获取数据
    String cacheValue = cache.get(key);
    // 缓存为空
    if (StringUtils.isBlank(cacheValue)) {
        // 从存储中获取
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        // 如果存储数据为空, 需要设置一个过期时间(300秒)
        if (storageValue == null) {
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    } else {
        // 缓存非空
        return cacheValue;
    }
}
2、布隆过滤器

       对于恶意攻击,向服务器请求大量不存在的数据造成的缓存穿透,可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一般都能够过滤掉,不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。

Redis常见缓存问题,分布式中间件,缓存,redis,性能优化,java,开发语言,后端

       向布隆过滤器中添加key时,会使用多个hash函数对key进行hash算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1就完成了add操作。

        向布隆过滤器询问key是否存在时,跟add一样,也会把hash的几个位置都算出来,看看位数组中这几个位置是否都为1,只要有一个位为0,那么说明布隆过滤器中这个key不存在。如果都是 1,这并不能说明这个key就一定存在,只是极有可能存在,因为这些位被置为1可能是因为其它的 key存在所致。如果这个位数组长度比较大,存在概率就会很大,如果这个位数组长度比较小,存在概率就会降低。

         这种方法适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景, 代码维护较为复杂, 但是缓存空间占用很少。

redisson实现布隆过滤器,引入依赖

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.6.5</version>
</dependency>
package com.redisson;

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonBloomFilter {

    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        //构造Redisson
        RedissonClient redisson = Redisson.create(config);

        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bFileter");
        //初始化布隆过滤器:预计元素为100000000L,误差率为3%,根据这两个参数会计算出底层的bit数组大小
        bloomFilter.tryInit(100000000L,0.03);
        //将zhuge插入到布隆过滤器中
        bloomFilter.add("zhangsan");

        //判断下面号码是否在布隆过滤器中
        System.out.println(bloomFilter.contains("lisi"));//false
        System.out.println(bloomFilter.contains("wangwu"));//false
        System.out.println(bloomFilter.contains("zhangsan"));//true
    }
}

        使用布隆过滤器需要把所有数据提前放入布隆过滤器,并且在增加数据时也要往布隆过滤器里放,布隆过滤器缓存过滤伪代码:

//初始化布隆过滤器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bFileter");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%
bloomFilter.tryInit(100000000L,0.03);
        
//把所有数据存入布隆过滤器
void init(){
    for (String key: keys) {
        bloomFilter.put(key);
    }
}

String get(String key) {
    // 从布隆过滤器这一级缓存判断下key是否存在
    Boolean exist = bloomFilter.contains(key);
    if(!exist){
        return "";
    }
    // 从缓存中获取数据
    String cacheValue = cache.get(key);
    // 缓存为空
    if (StringUtils.isBlank(cacheValue)) {
        // 从存储中获取
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        // 如果存储数据为空, 需要设置一个过期时间(300秒)
        if (storageValue == null) {
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    } else {
        // 缓存非空
        return cacheValue;
    }
}

注意:布隆过滤器不能删除数据,如果要删除得重新初始化数据。


缓存失效(击穿)

       由于大批量缓存在同一时间失效可能导致大量请求同时穿透缓存直达数据库,可能会造成数据库瞬间压力过大甚至挂掉,对于这种情况我们在批量增加缓存时最好将这一批数据的缓存过期时间设置为一个时间段内的不同时间。

String get(String key) {
    // 从缓存中获取数据
    String cacheValue = cache.get(key);
    // 缓存为空
    if (StringUtils.isBlank(cacheValue)) {
        // 从存储中获取
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        //设置一个过期时间(300到600之间的一个随机数)
        int expireTime = new Random().nextInt(300)  + 300;
        if (storageValue == null) {
            cache.expire(key, expireTime);
        }
        return storageValue;
    } else {
        // 缓存非空
        return cacheValue;
    }
}

缓存雪崩

       缓存雪崩指的是缓存层支撑不住或宕掉后, 流量会像奔逃的野牛一样, 打向后端存储层。由于缓存层承载着大量请求, 有效地保护了存储层, 但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支撑不住,或者由于缓存设计不好,类似大量请求访问bigkey,导致缓存能支撑的并发急剧下降), 于是大量请求都会打到存储层, 存储层的调用量会暴增, 造成存储层也会级联宕机的情况。

预防和解决缓存雪崩问题

1. 保证缓存层服务高可用性,比如使用Redis Sentinel或Redis Cluster。

2. 依赖隔离组件为后端限流熔断并降级。比如使用Sentinel或Hystrix限流降级组件。

3. 在项目上线前,充足测试,在此基础上做一些预案设定。


热点缓存key重建优化

String get(String key) {
    // 从Redis中获取数据
    String value = redis.get(key);
    // 如果value为空, 则开始重构缓存
    if (value == null) {
        // 只允许一个线程重建缓存, 使用nx, 并设置过期时间ex
        String mutexKey = "mutext:key:" + key;
        if (redis.set(mutexKey, "1", "ex 180", "nx")) {
             // 从数据源获取数据
            value = db.get(key);
            // 回写Redis, 并设置过期时间
            redis.setex(key, timeout, value);
            // 删除key_mutex
            redis.delete(mutexKey);
        }// 其他线程休息50毫秒后重试
        else {
            Thread.sleep(50);
            get(key);
        }
    }
    return value;
}

        利用互斥锁来解决,此方法只允许一个线程重建缓存, 其他线程等待重建缓存的线程执行完, 重新从缓存获取数据即可。

缓存与数据库双写不一致

1、双写不一致情况

                                Redis常见缓存问题,分布式中间件,缓存,redis,性能优化,java,开发语言,后端

2、读写并发不一致

                      Redis常见缓存问题,分布式中间件,缓存,redis,性能优化,java,开发语言,后端

解决方案

1、并发几率很小的数据(如个人维度的订单数据、用户数据等),这种几乎不用考虑这个问题,很少会发生缓存不一致,可以给缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。

2、并发很高,如果业务上能容忍短时间的缓存数据不一致(如商品名称,商品分类菜单等),缓存加上过期时间依然可以解决大部分业务对于缓存的要求。

3、如果不能容忍缓存数据不一致,可以通过加分布式读写锁(Redisson)保证并发读写或写写的时候按顺序排好队,读读的时候相当于无锁。

4、可以用阿里开源的canal通过监听数据库的binlog日志及时的去修改缓存,但是引入了新的中间件,增加了系统的复杂度。文章来源地址https://www.toymoban.com/news/detail-814755.html

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

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

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

相关文章

  • 【Java程序员面试专栏 分布式中间件】Redis 核心面试指引

    关于Redis部分的核心知识进行一网打尽,包括Redis的基本概念,基本架构,工作流程,存储机制等,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 明确redis的特性、应用场景和数据结构 Redis是一个 开源的、内存中的数据结构存储系统

    2024年02月20日
    浏览(46)
  • Redis 分布式缓存

    单点 Redis 的问题及解决 数据丢失:实现Redis数据持久化 并发能力:搭建主从集群,实现读写分离 存储能力:搭建分片集群,利用插槽机制实现动态扩容 故障恢复能力:利用哨兵机制,实现健康检测和自动恢复 RDB RDB全称Redis Database Backup file (Redis数据备份文件),也被叫做

    2024年02月10日
    浏览(52)
  • Redis分布式缓存

    -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题: Redis有两种持久化方案: RDB持久化 AOF持久化        RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做 Redis数据快照 。简单来说就是把 内存中的所有数据都记录到磁盘 中。当Redis实例故障重启后,

    2024年02月12日
    浏览(52)
  • Redis高级-分布式缓存

    – 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题: Redis有两种持久化方案: RDB持久化 AOF持久化 RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取

    2024年04月16日
    浏览(39)
  • Redis(分布式缓存详解)

    Redis:基于内存的键值存储系统,通常用作高性能的数据库、缓存和消息队列代理,是互联网广泛应用的存储中间件 特点 :基于内存存储,读写性能高 Redis与MySQL区别 Redis以键值对形式存储,MySQL以表格形式存储 Redis存储在 内存 ,MySQL存储在 磁盘 Redis存储 高效 ,MySQL存储 安

    2024年02月16日
    浏览(48)
  • Redis分布式缓存方案

    数据丢失:数据持久化 并发能力弱:搭建主从集群,实现读写分离 故障恢复问题:哨兵实现健康检测,自动恢复 存储能力:搭建分片集群,利用插槽机制实现动态扩容 RDB持久化 数据库备份文件,也叫快照,把内存数据存到磁盘。使用save进行主动RDB,会阻塞所有命令。建议

    2023年04月25日
    浏览(43)
  • 缓存的变更(JVM本地缓存->Redis分布式缓存)

    在一次需求修改中,下游的服务附加提出了,针对某个业务数据缓存的生效时间的要求 原JVM设计方案: 采用jvm本地缓存机制,定时任务30秒刷新一次 现在redis方案: 因为很多地方使用了这个业务数据缓存,使用方面不能改动过多 因为是分布式部署,如果只使用jvm缓存,无法

    2024年02月11日
    浏览(48)
  • 23-MyBatis缓存、本地缓存、分布式Redis缓存、前端缓存

             MyBatis一级缓存、          MyBatis二级缓存、          本地缓存:单节点          分布式Redis缓存:多节点          前端sessionStorage缓存:会话缓存          前端localStorage缓存:前端本地缓存 MyBatis一级缓存默认是开启的。 在Spring Boot中需要添加

    2024年02月13日
    浏览(36)
  • SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】

    上一篇实现了单体应用下如何上锁,这一篇主要说明如何在分布式场景下上锁 上一篇地址:加锁 需要注意的点是: 在上锁和释放锁的过程中要保证 原子性操作 核心是上锁和解锁的过程 关于解锁使用脚本参考:SET key value [EX seconds] [PX milliseconds] [NX|XX] 3.1 一个服务按照多个端口同时

    2023年04月10日
    浏览(52)
  • Redis分布式缓存部署方案详解

    高可用性 :分布式部署可以避免单点故障,提高系统的可用性。 高性能 :分布式部署可以通过增加节点数量来提高系统的吞吐量和响应速度。 易于扩展 :分布式部署可以方便地扩展系统的容量和性能,只需添加新节点即可。 Redis的分布式部署有多种方式,例如主从复制、

    2024年02月07日
    浏览(54)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包