Redis与缓存

news2024/11/15 20:30:25

文章目录

    • Redis与缓存
      • 一致性问题
      • 大Key问题
      • 缓存穿透
      • 缓存击穿
      • 缓存雪崩

Redis与缓存

Redis作为缓存具有高性能、丰富的数据结构和灵活的过期机制等优点。由于Redis将数据存储在内存中,它能提供极低的延迟和高吞吐量,适合用于缓存数据库查询结果、会话数据和实时数据处理等场景。Redis的多种数据结构支持不同的缓存需求,如缓存静态内容、实现简单的消息队列,以及处理实时统计信息。

用户第一次访问数据库中的某些数据。整个过程会比较慢,因为是从硬盘上读取的。如果将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快,但随之而来的也会存在一些问题。

一致性问题

只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题。缓存一致性问题发生在缓存中的数据与源数据之间存在不一致的情况。这种不一致可能会导致系统中的数据错误或不准确。

解决一致性问题的关键在于,当源数据发生更改时,缓存中的数据也需要更新。

在更新源数据时,同时更新缓存和源数据库。这种方式保持了数据的一致性,因为所有写操作都会同时在缓存和数据库中完成。写操作的延迟可能增加,特别是在高负载情况下,更新操作可能成为性能瓶颈。适用于对数据一致性要求高的场景,例如金融系统和实时数据处理系统,其中一致性比性能更重要。

另一种方法是异步更新,先更新缓存,再通过后台任务异步更新源数据库。这种方式提高了写操作的性能,因为数据库更新是异步进行的,减少了写入延迟。缓存的写入速度较快,有助于提升用户体验。但数据库和缓存之间可能出现最终一致性问题,数据库更新的延迟可能导致缓存数据与源数据库不一致。适用于数据一致性要求可以容忍一定延迟的场景,如在线购物网站和社交媒体平台,其中用户体验和性能优于即时一致性。

缓存失效策略在读取数据时,如果缓存中不存在数据或数据已过期,会从源数据库加载数据并更新缓存。这种方式通过重新加载来保持数据的一致性。缓存失效或数据过期时,总是从源数据库中获取最新数据,避免了缓存和数据库间的数据不一致问题。读取延迟可能增加,特别是缓存频繁失效时,且频繁的数据库访问可能增加负载。适用于读取操作较多的场景,例如内容分发网络或新闻网站,通过缓存失效平衡数据一致性和性能。

当缓存或数据库更新失败时,也会导致数据不一致的问题。为了解决这个问题,可以采取几种方法。可以设置自动重试机制,如果更新操作失败,系统会尝试重新执行更新,增加成功的可能性,虽然这样可能会增加系统的负担。另一种方法是使用备用缓存,在主缓存更新失败时,将数据写入备用缓存,并在主缓存恢复正常时进行同步,这样可以保持系统的正常运转。记录更新失败的情况,并触发报警,也能帮助快速发现问题,防止问题长时间存在。还可以将缓存更新操作放在后台任务中,并设置补偿机制来修复数据不一致。如果后台任务失败,补偿机制会尝试重新同步数据。

大Key问题

所谓的大Key问题是指某个Keyvalue比较大,所以本质上是大value问题。因为Key往往是程序可以自行设置的,value往往不受程序控制,因此可能导致value很大。大Key占用的内存非常多,可能导致Redis实例的内存使用量急剧增加。大Key的读取、写入或删除可能会显著拖慢Redis的性能。例如,操作一个非常大的列表会占用大量的CPU和IO资源,导致其他操作的响应时间变慢。

Key问题一般是由于业务方案设计不合理,没有预见value的动态增长问题产生的。一直往value塞数据,没有删除机制,迟早要爆炸或数据没有合理做分片,将大Key变成小Key。在线上一般通过设置Redis监控,及时发现和处理大Key问题。可以使用工具监控键的大小,避免存储异常大的数据项。

解决思路:

  • 可在应用层或客户端设置最大键值大小限制,防止大Key被写入Redis
  • 定期检查Redis中的大Key,并进行必要的清理或优化操作。
  • 如果Redis中已经存在大Key,根据大Key的实际用途可以分为可删除和不可删除,如果发现某些大Key并非热Key就可以在DB中查询使用,则可以在Redis中删掉。
    如果不可删除,则需要拆分大Key,将大Key拆分成多个小Key,然后进行删除。

缓存穿透

缓存穿透是指查询请求绕过缓存直接访问数据库,通常是因为请求中的数据在缓存中不存在。这个问题可能导致缓存失效,增加数据库负担,并影响系统性能。缓存穿透的原因通常是,用户请求的数据在缓存和数据库中都不存在,或者是请求的数据在缓存中未命中,直接查询数据库,并未将结果正确地缓存起来。

在这里插入图片描述

解决方法:

  • 布隆过滤器:布隆过滤器是一种空间高效的数据结构,用于判断某个元素是否在集合中。它可以减少对数据库的访问次数,通过在缓存层使用布隆过滤器,来快速判断请求的数据是否可能存在于数据库中。将可能存在的键加入布隆过滤器。在每次查询之前,先检查布隆过滤器。如果布隆过滤器显示数据不存在,则直接返回空值或错误,不访问数据库。
    public class BloomFilterExample {
        private BloomFilter<String> bloomFilter;
        private StringRedisTemplate redisTemplate;
        
        public BloomFilterExample(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
            // Initialize Bloom Filter with an expected insertions and false positive probability
            this.bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 100000, 0.01);
        }
    
        public String getData(String key) {
            // Check if the key is in the Bloom Filter
            if (!bloomFilter.mightContain(key)) {
                return null; // Key definitely not in cache or DB
            }
    
            // Check cache
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                return value;
            }
    
            // Load from DB (simulate)
            value = loadFromDatabase(key);
    
            // Cache the result and add to Bloom Filter
            if (value != null) {
                redisTemplate.opsForValue().set(key, value);
                bloomFilter.put(key);
            }
    
            return value;
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return "DatabaseValueFor" + key;
        }
    }
    
  • 缓存空对象:当数据库查询返回空结果时,将空结果缓存到Redis中,使用一个特殊的标识,如空字符串、null、特定的空值对象等。后续相同的查询可以直接从缓存中获取空结果,避免再次访问数据库。设置空对象的缓存时间较短,避免长时间缓存无效数据。
    public class CacheEmptyObjectExample {
        private static final String EMPTY_OBJECT_PLACEHOLDER = "EMPTY";
        private StringRedisTemplate redisTemplate;
    
        public CacheEmptyObjectExample(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public String getData(String key) {
            // Check cache
            String value = redisTemplate.opsForValue().get(key);
            if (EMPTY_OBJECT_PLACEHOLDER.equals(value)) {
                return null; // Data definitely does not exist
            }
            if (value != null) {
                return value;
            }
    
            // Load from DB (simulate)
            value = loadFromDatabase(key);
    
            if (value != null) {
                redisTemplate.opsForValue().set(key, value);
            } else {
                redisTemplate.opsForValue().set(key, EMPTY_OBJECT_PLACEHOLDER);
            }
    
            return value;
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return null; // Simulate no data found
        }
    }
    

缓存击穿

缓存击穿 是指在缓存中某个热点数据的缓存失效时,多个请求同时访问数据库,导致数据库压力剧增的情况。这种问题通常发生在缓存数据过期或被删除时,如果请求大量集中在短时间内,可能会导致数据库负载急剧上升。

在这里插入图片描述

解决方法:

  • 加锁机制:在缓存失效时,对数据的访问进行加锁,保证只有一个请求能够从数据库中加载数据并更新缓存。其他请求需要等待锁释放后,才能获取缓存中的数据。
    public class CacheLockExample {
        private StringRedisTemplate redisTemplate;
        private RedisTemplate<String, Object> redisLockTemplate;
        private static final String LOCK_KEY_PREFIX = "lock:";
    
        public CacheLockExample(StringRedisTemplate redisTemplate, RedisTemplate<String, Object> redisLockTemplate) {
            this.redisTemplate = redisTemplate;
            this.redisLockTemplate = redisLockTemplate;
        }
    
        public String getData(String key) {
            String cacheKey = "cache:" + key;
            String lockKey = LOCK_KEY_PREFIX + key;
    
            // Check cache
            String value = redisTemplate.opsForValue().get(cacheKey);
            if (value != null) {
                return value;
            }
    
            // Acquire lock
            Boolean lockAcquired = redisLockTemplate.opsForValue().setIfAbsent(lockKey, "locked");
            if (lockAcquired != null && lockAcquired) {
                try {
                    // Load from DB (simulate)
                    value = loadFromDatabase(key);
    
                    // Cache the result
                    if (value != null) {
                        redisTemplate.opsForValue().set(cacheKey, value);
                    }
                } finally {
                    // Release lock
                    redisLockTemplate.delete(lockKey);
                }
            } else {
                // Wait for lock to be released and retry
                try {
                    Thread.sleep(100); // Wait for 100 milliseconds
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return redisTemplate.opsForValue().get(cacheKey);
            }
    
            return value;
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return "DatabaseValueFor" + key;
        }
    }
    
  • 缓存预热:在缓存过期前,提前将热点数据加载到缓存中,减少缓存失效对数据库的冲击。这可以通过定期刷新缓存或使用缓存预热策略来实现。
    public class CachePreheatExample {
        private StringRedisTemplate redisTemplate;
    
        public CachePreheatExample(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public void preheatCache() {
            // Simulate loading all hotspot data
            for (String key : getHotspotKeys()) {
                String value = loadFromDatabase(key);
                if (value != null) {
                    redisTemplate.opsForValue().set("cache:" + key, value);
                }
            }
        }
    
        private Iterable<String> getHotspotKeys() {
            // Simulate getting all hotspot keys
            return List.of("key1", "key2", "key3");
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return "DatabaseValueFor" + key;
        }
    }
    

缓存雪崩

缓存雪崩 是指当大量缓存同时失效或遭遇问题时,造成大量请求同时涌入数据库,导致数据库负载过重,从而引发服务不可用的情况。这种情况常见于缓存失效时间集中或缓存服务宕机等场景。

在这里插入图片描述

解决方法:

  • 缓存过期时间随机化:通过对缓存的过期时间进行随机化,避免所有缓存同时过期。根据业务需求设置合理的缓存过期时间。避免缓存过期时间设置过长或过短,导致缓存失效的集中现象。
    public class CacheRandomExpirationExample {
        private StringRedisTemplate redisTemplate;
        private static final int EXPIRATION_TIME = 600; // 10 minutes
    
        public CacheRandomExpirationExample(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public String getData(String key) {
            String cacheKey = "cache:" + key;
            String value = redisTemplate.opsForValue().get(cacheKey);
    
            if (value != null) {
                return value;
            }
    
            // Load from DB (simulate)
            value = loadFromDatabase(key);
    
            if (value != null) {
                // Set cache with random expiration time between 10 and 20 minutes
                int expiration = EXPIRATION_TIME + (int) (Math.random() * 600);
                redisTemplate.opsForValue().set(cacheKey, value, expiration, TimeUnit.SECONDS);
            }
    
            return value;
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return "DatabaseValueFor" + key;
        }
    }
    
  • 使用多级缓存:引入多级缓存策略,比如本地缓存和分布式缓存结合使用。这样即使分布式缓存失效,本地缓存仍能提供服务,减少对数据库的压力。
    public class MultiLevelCacheExample {
        private StringRedisTemplate redisTemplate;
        private Cache<String, String> localCache;
    
        public MultiLevelCacheExample(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
            this.localCache = Caffeine.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
        }
    
        public String getData(String key) {
            // Check local cache first
            String value = localCache.getIfPresent(key);
            if (value != null) {
                return value;
            }
    
            String cacheKey = "cache:" + key;
            value = redisTemplate.opsForValue().get(cacheKey);
            if (value != null) {
                localCache.put(key, value);
                return value;
            }
    
            // Load from DB (simulate)
            value = loadFromDatabase(key);
    
            if (value != null) {
                redisTemplate.opsForValue().set(cacheKey, value, 10, TimeUnit.MINUTES);
                localCache.put(key, value);
            }
    
            return value;
        }
    
        private String loadFromDatabase(String key) {
            // Simulate DB access
            return "DatabaseValueFor" + key;
        }
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1956656.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

集成测试:确保软件模块协同工作的关键步骤

目录 前言1. 集成测试的概念1.1 增量集成测试1.2 大爆炸集成测试 2. 集成测试的主要作用2.1 确保模块间正确交互2.2 发现设计缺陷2.3 提高系统稳定性 3. 集成测试在整个测试过程中的地位3.1 从单元测试到集成测试3.2 从集成测试到系统测试 4. 常用的集成测试工具4.1 JUnit4.2 T…

嵌入式开发测试实训室解决方案

一、建设背景 随着物联网、人工智能等技术的飞速发展&#xff0c;嵌入式系统作为这些技术的重要载体&#xff0c;其重要性日益凸显。为了满足市场对嵌入式技术人才的需求&#xff0c;培养具有扎实理论基础、熟练实践技能及创新能力的嵌入式开发工程师&#xff0c;设计并建设一…

3. 类的生命周期

类的生命周期是指一个类被加载&#xff0c;使用&#xff0c;卸载的一个过程&#xff0c;如下图&#xff1a; 类的加载阶段&#xff1a; 加载(Loading)阶段第一步是类加载器根据类的**全限定名&#xff08;也就是类路径&#xff09;**通过不同的渠道以二进制流的方式获取字节码…

ClkLog:开源用户行为分析框架,让数据分析更轻松

ClkLog&#xff1a;开源用户行为分析框架&#xff0c;让数据分析更轻松 在数据驱动的时代&#xff0c;找到一个好用的用户行为分析工具真是难上加难。但是今天你有福了&#xff0c;开源免费的 ClkLog 就是你的不二选择&#xff01;本文将为你详细介绍 ClkLog 的功能特点、技术架…

OpenCV 彩色直方图

彩色直方图 &#xff08;1&#xff09;直方图是图像中像素强度分布。 &#xff08;2&#xff09;直方图统计了每一个强度值所具有的像素个数。 &#xff08;3&#xff09;cv2.calcHist(images, channels, mask, histSize, ranges) import cv2 import matplotlib.pyplot as …

2023年系统架构设计师考试总结

原文链接&#xff1a;https://www.cnblogs.com/zhaotianff/p/17812187.html 上周六参加了2023年系统架构设计师考试&#xff0c;这次考试与以前有点区别&#xff0c;是第一次采用电子化考试&#xff0c;也是教材改版后的第一次考试。 说说考前准备&#xff1a;为了准备这次考试…

cadence SPB17.4 - allegro - DRC error “Line to SMD Pin Same Net Spaing“

文章目录 cadence SPB17.4 - allegro - DRC error "Line to SMD Pin Same Net Spaing"概述笔记END cadence SPB17.4 - allegro - DRC error “Line to SMD Pin Same Net Spaing” 概述 铺铜前DRC正确。 铺铜后&#xff0c;报错 DRC error “Line to SMD Pin Same …

Squeeze-and-Excitation Networks

1、引言 论文链接&#xff1a;Squeeze-and-Excitation Networks (thecvf.com) 为了提高 CNN(convolutional neural network) 的表示能力&#xff0c;最近的几种方法已经显示出增强空间编码的好处。而 Jie Hu[1] 等专注于通道关系并提出了 SE(Squeeze and Excitation) 块&#x…

uniapp 使用css实现大转盘

思路&#xff1a; 1.一个原型的外壳包裹 2.使用要分配的个数&#xff0c;计算出角度&#xff0c;利用正切函数tan计算出角度对应对边长度 3.使用clip-path画出一个扇形 4.使用v-for循环出对应的份数&#xff0c;依次使用transform rotate旋转对应的角度。 注意&#xff1a…

DM集群的高可用的配置方式(多语言)

一、介绍 强调以下&#xff1a;dm_svc.conf必须放置到应用服务器上才行&#xff0c;放到其他服务器上识别不到。 文章中有的框架可能没写到并不代表不支持&#xff0c;有没提到的可文章下方留言。 1.dm_svc.conf的作用&#xff1a; dm_svc.conf文件主要是为了当DM数据库集群…

ES(ElasticSearch)倒排索引

目录 正排与倒排索引 1.正排索引 作用&#xff1a; 优点&#xff1a; 缺点&#xff1a; 2.倒排索引 原理&#xff1a; 倒排索引的构建流程&#xff1a; 倒排索引的搜索流程&#xff1a; 优点&#xff1a; 缺点&#xff1a; 3. 应用场景 倒排索引中有几个非常重要的概念…

Skyeye云智能制造企业版源代码全部开放

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

【图文详解】Spring是如何解决循环依赖的?

Spring是如何解决循环依赖的呢&#xff1f; 很多小伙伴在面试时都被问到过这个问题&#xff0c;刷到过这个题的同学马上就能回答出来&#xff1a;“利用三级缓存”。面试官接着追问&#xff1a;“哪三级缓存呢&#xff1f;用两级行不行呢&#xff1f;” 这时候如果没有深入研究…

AI副业赚钱免费资源大汇总

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;的热潮无处不在&#xff0c;许多人对于如何利用AI技术来创造收入机会感到好奇。本文将介绍一个名为aimoneyhunter的开源项目&#xff0c;这是一个专注于AI副业赚钱的资源汇总&#xff0c;旨在帮助人们在AI时代找…

技术干货 | 针对Spring-Boot 框架漏洞的初探

0x1 前言 这篇文章主要是给师傅们介绍下Spring-Boot 框架漏洞的打法以及主要对于Spring-Boot漏洞的接口泄露信息进行一个分析&#xff0c;后面使用了曾哥的Spring-Boot漏洞扫描工具&#xff0c;可以很大减轻我们对于这个漏洞接口的分析。 0x2 Spring框架简述 Spring是一个ja…

Ollama 在Windows Server 2019中部署 qwen:4b

1、环境 1.1、Ollama 1.2、Windows Server 2019 1.3、qwen:4b 1.4、nginx-1.27.0 2、部署后效果如下。 3、环境安装包下载链接包含本文所有的配置内容。 https://download.csdn.net/download/xingchengaiwei/89571623 4、安装ollama&#xff0c;官网下载地址Download Ollama…

【SpringCloud】企业认证、分布式事务,分布式锁方案落地-2

目录 高并发缓存三问 - 穿透 缓存穿透 概念 现象举例 解决方案 缓存穿透 - 预热架构 缓存穿透 - 布隆过滤器 布隆过滤器 布隆过滤器基本思想​编辑 了解 高并发缓存三问 - 击穿 缓存击穿 高并发缓存三问 - 雪崩 缓存雪崩 解决方案 总结 为什么要使用数据字典&…

一文带你了解RAG(检索增强生成) | 概念理论介绍+ 代码实操

一、LLMs 已经具备了较强能力了&#xff0c;为什么还需要 RAG(检索增强生成)? 尽管 LLM 已展现出显著的能力&#xff0c;但以下几个挑战依然值得关注&#xff1a; 幻觉问题&#xff1a;LLM 采用基于统计的概率方法逐词生成文本&#xff0c;这一机制内在地导致其可能出现看似逻…

PPT模板替换秘籍:一键撤销原模板,轻松更换新风格!

将PPT中的模板换成另一个模板&#xff0c;可以通过几种不同的方法实现。以下是几种常用的方法&#xff1a; 方法一&#xff1a;使用PowerPoint内置的设计选项卡 打开PowerPoint&#xff1a;首先&#xff0c;打开你想要更改模板的PPT文件。 选择“设计”选项卡&#xff1a;在…

Python设计模式 - 工厂方法模式

定义 工厂方法模式是一种创建型设计模式&#xff0c;它定义一个创建对象的接口&#xff0c;让其子类来处理对象的创建&#xff0c;而不是直接实例化对象。 结构 抽象工厂&#xff08;Factory&#xff09;&#xff1a;声明工厂方法&#xff0c;返回一个产品对象。具体工厂类都…