Redis与缓存解读

2025-01-11 04:50
文章标签 redis 解读 缓存

本文主要是介绍Redis与缓存解读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步...

缓存

在业务开发中,必然会存在需要频繁访问的数据即热点数据,如果通过访问数据库访问这些数据,由于数据存储在磁盘上,在频繁访问下会进行频繁的IO操作,会导致数据库压力过大,响应速度变慢。

那么我们可以在添加php一层中间缓存层,将热点数据缓存在内存中,在访问数据时我们不在直接查询数据库,而是先访问缓存,如果数据存在(命中),直接返回即可。如果数据不存在(未命中),再访问数据库。

Redis与缓存解读

Redis 将数据存储在内存中,因此可以提供接近于内存的访问速度。所以 redis 天然适合作为缓存层。

缓存并不是万能的,实际上缓存更使用于读密集场景,在写密集场景中由于需要保证缓存于数据库的一致性,在修改缓存时还需要修改数据库,反而加重了后端压力。

缓存优缺点

优点:

  • 降低后端负载
  • 提高读写效率,降低响应时间

缺点:

  • 增加数据一致性成本
  • 增加代码维护成本

缓存更新策略

为了保证缓存数据有效,我们需要更新缓存。这里主要有六种缓存更新策略android

超时剔除

在 redis 中我们可以设置数据的生存时间(TTL),在超时后,redis会自动删除缓存,在下次查询该数据时,由于缓存不存在,会重新写入缓存,完成更新。

这种方法实现简单,但一致性一般,在缓存未过期之前,对数据库的数据进行增删查改都不会影响缓存,用户查到的数据始终是旧数据。

先删缓存再更新数据库

在对数据库进行更新时,先删除缓存,然后更新数据,在下次查询该数据时,由于缓存不存在,会重新将新数据写入缓存,javascript完成更新。

这种方法也无法保证数据的一致性。假设有两个线程,线程A 与 线程B , 在线程 A 更新缓存时,线程 B 发起查询,可能出现这种情况:

Redis与缓存解读

在这种情况下,一直到下次数据更新之前,缓存始终不一致,因此不推荐使用这种方法。

旁路缓存(先更新数据库,再删缓存)

在更新数据时,先更新数据库,再删除缓存,在下次查询该数据时,由于缓存不存在,会重新将新数据写入缓存,完成更新。

这种方法同样无法完全保证数据的一致性,但他是最常用的更新策略。因为它发生问题的概况较小,假设有两个线程,线程A 与 线程B , 在线程 B 更新数据库时,线程 A 发起查询,可能出现这种情况:

Redis与缓存解读

同样这种情况会出现数据不一致问题,但这种情况出现概率非常小,出现这种情况需要至少满足四个条件:

  • 读操作所读数据缓存失效
  • 有个并发的写操作
  • 写操作比读操作更快
  • 读操作早于写操作进入数据库,晚于写操作更新缓存

这样的条件是十分苛刻的,即使发生也是小概率事件,即使出现也可以通过缓存生存时间兜底。

这种方法最大的问题是删除缓存后的并发问题即缓存击穿问题,在缓存常见问题我们会介绍。

先更新数据库,再更新缓存

在更新数据时,先更新数据库,再更新缓存。

理论上这种方式比先更新数据库再删缓存有着更高的读性能,因为它事先准备好数据。但由于要更新数据库和缓存两块数据,所以它的写性能就比较低,同时他也不能完全保证数据的一致性。

假设有两个线程,线程A 与 线程B , 在线程 A B 同时更新,可能出现这种情况:

Redis与缓存解读

读写穿透

客户端只与缓存交互,缓存负责与数据库的交互。

读操作先查询缓存,如果缓存未命中,则缓存从数据库加载数据并写入缓存。写操作是直接写缓存,然后缓存同步更新数据库。这种模式下,缓存和数据库的一致性由缓存中China编程间件维护。

Redis与缓存解读

异步缓存写入模式

客户端只与缓存交互,缓存异步地将数据更新到数据库,实现最终一致性。

Redis与缓存解读

这种模式适用于写操作频繁的场景,但可能会导致数据一致性问题。

缓存常见问题

使用缓存比较常见的问题有下面三种问题:缓存击穿,缓存雪崩,缓存穿透。

缓存穿透

在我们的业务逻辑中,如果客户端访问的数据不存在于缓存我们会访问数据库,如果数据库存在数据就写入缓存,如果不存在就返回,那么如果客户端不怀好意,频繁发起对不存在数据的请求会发生什么呢?大量请求会直接打入数据库,增大后端压力,实现对服务器的攻击。

Redis与缓存解读

解决方案有很多种,最常见的有两种方法:缓存空对象,布隆过滤器

缓存空对象:当请求的数据在数据库中不存在时,我们将这个“不存在”的结果缓存起来,设置一个较短的过期时间。 这样,相同的请求在缓存失效之前会直接命中缓存,减轻数据库的压力。

Redis与缓存解读

布隆过滤器:使用布隆过滤器存储所有可能查询的键,当请求到达时,先通过布隆过滤器判断键是否存在。如果布隆过滤器认为键不存在,则直接返回,不进行数据库查询和缓存操作。

Redis与缓存解读

缓存雪崩

缓存雪崩是指在高并发系统中,大量的缓存数据在同一时间过期或被清除,导致大量请求同时涌向数据库,从而对数据库造成巨大压力,甚至可能导致数据库宕机。类似于“雪崩”。

常见的解决方法有以下几种:

设置不同的过期时间: 对于缓存中的每个数据项,设置不同的过期时间,这样可以避免大量数据同时过期。例如,可以为每个数据项的过期时间加上一个随机值。

使用互斥锁: 当缓存数据过期时,如果多个请求同时到达,使用互斥锁确保只有一个请求去查询数据库并更新缓存,其他请求等待或重试。

热点数据永不过期: 对于访问非常频繁的热点数据,可以考虑设置为永不过期,或者设置一个非常长的过期时间。

缓存击穿

在高并发的访问下,当某个热点数据缓存处于过期失效的时间点时,极有可能出现多个线程同时查询该缓存。而查询数据库更新缓存又需要消耗一定时间,在同一时间会有大量并发请求直接访问数据库而导致数据库服务器的CPU或者内存负载过高,服务能力下降甚至宕机。

那么如何解决这个问题呢?有三种解决方案。

  • 加锁:在缓存失效后,通过加锁的方式只允许一个线程查询数据和写缓存,其他线程阻塞等待。这个方法会造成部分请求等待。
  • 二级缓存:A1为原始缓存,A2为拷贝缓存。A1失效时,可以访问A2,其中A1的缓存失效时间设置为短期(比如5min),A2的缓存失效时间设置为长期(比如1天)。如果缓存value很大,此方案的缓存空间利用率低。
  • 双key:思www.chinasem.cn路和方案2类似,不同的是双key分别缓存过期时间(key-time)和缓存数据(key-data),其中(key-time)的缓存失效时间设置为短期(比如5min),(key-data)的缓存失效时间设置为长期(比如1天)。当第一个线程发现 key-time 过期不存在时,则先更新key-time,然后去查询数据库并更新key-data 的值;当其他线程来获取数据时,虽然第一个线程还没有从数据库查询完毕并更新缓存,但发现key-time存在,会直接读取缓存的旧数据返回。和二级缓存的方案对比,该方案的缓存空间利用率高。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程China编程(www.chinasem.cn)。

这篇关于Redis与缓存解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153029

相关文章

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Redis客户端工具之RedisInsight的下载方式

《Redis客户端工具之RedisInsight的下载方式》RedisInsight是Redis官方提供的图形化客户端工具,下载步骤包括访问Redis官网、选择RedisInsight、下载链接、注册... 目录Redis客户端工具RedisInsight的下载一、点击进入Redis官网二、点击RedisI

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构

Linux系统之authconfig命令的使用解读

《Linux系统之authconfig命令的使用解读》authconfig是一个用于配置Linux系统身份验证和账户管理设置的命令行工具,主要用于RedHat系列的Linux发行版,它提供了一系列选项... 目录linux authconfig命令的使用基本语法常用选项示例总结Linux authconfi

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

一文详解Nginx的强缓存和协商缓存

《一文详解Nginx的强缓存和协商缓存》这篇文章主要为大家详细介绍了Nginx中强缓存和协商缓存的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、强缓存(Strong Cache)1. 定义2. 响应头3. Nginx 配置示例4. 行为5. 适用场景二、协商缓存(协

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

解读docker运行时-itd参数是什么意思

《解读docker运行时-itd参数是什么意思》在Docker中,-itd参数组合用于在后台运行一个交互式容器,同时保持标准输入和分配伪终端,这种方式适合需要在后台运行容器并保持交互能力的场景... 目录docker运行时-itd参数是什么意思1. -i(或 --interactive)2. -t(或 --

解读为什么@Autowired在属性上被警告,在setter方法上不被警告问题

《解读为什么@Autowired在属性上被警告,在setter方法上不被警告问题》在Spring开发中,@Autowired注解常用于实现依赖注入,它可以应用于类的属性、构造器或setter方法上,然... 目录1. 为什么 @Autowired 在属性上被警告?1.1 隐式依赖注入1.2 IDE 的警告:

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释