草庐IT

三分钟带你掌握缓存穿透、缓存雪崩、缓存击穿,以及应对方案!

鸭血粉丝Tang 2023-03-28 原文

​一、介绍

每场后端面试,似乎都少不了关于 redis 的话题,比如项目使用过哪些分布式缓存服务,为什么要使用 redis,有没有碰到过缓存失效、缓存穿透、缓存雪崩等问题。

在前几篇关于 redis 的介绍文章中,我们说到项目中之所以会引入分布式缓存服务,主要是为了解决集群环境下,内存数据不共享的问题,比如 session 会话,以及一些字典缓存等等,在当前服务器的内存中存储,在另一台服务器中难以获取查询的问题,通过引入缓存服务,将缓存数据统一归一到一个服务器里面,以解决系统中内存数据不共享的问题,同时缓存性能也不会受到很大影响。

当然软件开源市场上,也有很多的分布式缓存服务,比如比较有名的有 redis、memcached 等,相对比 memcached,redis 各项指标都要比 memcached 强很多,Redis 号称能读的速度是 110000 次/s,写的速度是 81000次/s,无数的实践证明 redis 确实是当前一款非常高性能的内存数据库。

站在面试官的角度,软件系统的技术选型以及以上的相关技术问题,在实际的生产环境中确实也会发生,通过以此话题为切入点,可以更加清晰的了解面试者是否也碰到过类似的问题,以及对应处理的办法。

那么站在面试者的角度,除了熟练的掌握 redis 的使用方法以外,我们可能还需要更加深入的了解如果引入 redis 之后,系统中可能会发生的一些问题以及应对办法。

今天我们一起聊聊吧。

二、常见问题

2.1、问题一:为什么存入 redis 的数据,查询失效

Redis 的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上;也可以把每一次数据变化都写入到一个aof日志文件里面,当 redis 的服务器重启的时候,自动从日志文件里面恢复数据到内存中。

有哪些场景会发生缓存失效呢?总结起来有以下两种场景:

  • 当 redis 服务器重启的时候,可能会发生缓存失效,此时可以将 redis 的持久化方式改成AOF模式,也就是全持久化模式,但是性能会消耗很大
  • 存入redis 的数据,设置了自动过期时间,这种情况可以重新调整过期时间

2.2、问题二:缓存与数据库的数据不一致

通常情况下我们使用缓存,其中有一个很重要的目的就是降低数据库的访问压力,比如商品的信息查询,优先是从缓存中查询,如果没有,再从数据库里面查询。

对于既有数据库写入又有缓存操作的接口,一般分为两种情况执行。

  • 先写入数据库,再操作缓存。这种情况下如果数据库操作成功,缓存操作失败就会导致缓存和数据库不一致
  • 先操作缓存,再写入数据库。这种情况下如果缓存操作成功,数据库操作失败也会导致数据库和缓存不一致
大部分情况下,缓存理论上都是需要可以从数据库恢复出来的,所以基本上采取第一种顺序都是不会有问题的,但是无法保证数据库和缓存完全一致。

也就是说,使用缓存,就可能会出现缓存与数据库不一致的情况,只是说这种几几率的情况有多大。

针对那些必须保证数据库和缓存一致的情况,通常是不建议使用缓存的,直接从数据库查询。

2.3、问题三:什么是缓存穿透

缓存穿透,表示恶意用户频繁的模拟请求缓存中不存在的数据,此时如果有大量的接口请求,短时间内会直接落在了数据库上,缓存被击穿,导致数据库性能急剧下降,最终影响服务整体的性能。

这个在实际项目中很容易遇到,如抢购活动、秒杀活动、抢优惠券等接口 API 被大量的恶意用户刷,导致短时间内数据库宕机。对于缓存击穿的问题,有以下几种解决方案。

  • 使用分布式锁排队。当从缓存中获取数据失败时,给当前接口加上锁,从数据库中加载完数据并写入后再释放锁。若其它线程获取锁失败,则等待一段时间后再重试。
  • 使用布隆过滤器。将所有可能存在的数据哈希到一个足够大的bitmap​中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
  • 对空结果进行缓存。如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟,这样第二次到缓存中获取就有值了,而不会继续访问数据库,简单粗暴好使。

2.4、问题四:什么是缓存雪崩

缓存雪崩,简单的说就是在短时间内有大量缓存失效,如果这期间有大量的请求发生,同样也有可能会导致数据库发生宕机。在 Redis 机群的数据分布算法上如果使用的是传统的 hash 取模算法,在增加或者移除 Redis 节点的时候就会出现大量的缓存临时失效的情形。

对于缓存雪崩的问题,有以下几种解决方案。

  • 像解决缓存穿透一样加锁排队
  • 建立备份缓存。比如缓存 A 和缓存 B,A 设置超时时间,B 不设值超时时间,先从 A 读缓存,A 没有读 B,当缓存 A 发生变化的时候,同时更新缓存 B
  • 计算数据缓存节点的时候采用一致性 hash 算法,这样在节点数量发生改变时不会存在大量的缓存数据需要迁移的情况发生

2.5、问题五:redis 缓存会不会出现并发问题

首先 Redis 是单线程执行命令的,在出现多个 Redis Client 并发操作数据时,秉承先发起先执行的原则,其它的处于阻塞状态。

redis 缓存并发问题,其实主要指的还是读取数据库数据的并发操作问题。

当缓存过期后会从数据库查询数据然后再存入Redis​缓存,但是在高并发情况下,可能还没来得及将数据库中查出来的数据存入Redis​时,其它Client​又从数据库里查询数据再存入Redis了。

这样一来会造成多个请求并发的从数据库获取数据,然后存入Redis,可能在读取的时候,出现脏数据。

针对这种场景,有以下几种解决方案。

  • 同步加锁处理。在写入数据库的时候,再操作缓存这个阶段,进行加锁处理,保证服务串行,可能会牺牲一点时间
  • 异步队列串行执行。把写入数据库和操作缓存的操作,放在队列中使其串行化,让他们一个一个的执行,比如通过消息中间件异步执行。
  • 使用类似SQL的乐观锁机制:在并发写入Redis​缓存时,把要写入数据的版本号和时间戳与Redis​中的数据进行对比,如果写入的数据时间戳或者版本号 比Redis高,则写入;否则就不写入

三、小结

本文主要围绕 redis 使用中出现的一些场景问题,进行一次简单的总结,如果有疏漏的地方,欢迎网友留言指出!

四、参考

1、博客园 - 卡斯特梅的雨伞- springboot中RedisTemplate的使用

有关三分钟带你掌握缓存穿透、缓存雪崩、缓存击穿,以及应对方案!的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  3. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

    尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  6. ruby - ruby 中的同一个程序如何接受来自用户的输入以及命令行参数 - 2

    我的ruby​​脚本从命令行参数获取某些输入。它检查是否缺少任何命令行参数,然后提示用户输入。但是我无法使用gets从用户那里获得输入。示例代码:test.rbname=""ARGV.eachdo|a|ifa.include?('-n')name=aputs"Argument:#{a}"endendifname==""puts"entername:"name=getsputsnameend运行脚本:rubytest.rbraghav-k错误结果:test.rb:6:in`gets':Nosuchfileordirectory-raghav-k(Errno::ENOENT)fromtes

  7. 三分钟集成 TapTap 防沉迷 SDK(Unity 版) - 2

    三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应

  8. ruby-on-rails - bundle 安装尝试使用缓存文件 - 2

    当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/

  9. ruby-on-rails - 应用程序显示错误的小时和分钟 - 2

    起初:那不是错误区域的问题。在irb和数据库中,一切都很好。当我想在我的View中显示日期(created_at、updated_at和所有由我自己在每个模型中定义的日期)时,就会出现问题。我试图在application.rb中设置时区并从初始化程序中删除时间格式,但这并没有解决我的问题。Annotategem生成的架构信息:#created_at:datetime#updated_at:datetime#publish_at:datetime来自irb:1.9.2-p290:004>Time.zone=>(GMT+00:00)UTC1.9.2-p290:005>Time.zone.n

  10. ruby-on-rails - Heroku Action 缓存似乎不起作用 - 2

    我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错

随机推荐