当前位置: 首页 > news >正文

应用缓存的常见问题及解决

使用缓存一些常见的套路问题。

 

缓存穿透

  • 场景:大量请求访问某个不存在的KEY

在缓存设计中,查询缓存 -> key不存在 -> 回源DB -> 更新缓存,这是一个典型的方案。

缓存穿透是指查询一个一定不存在的Key,由于缓存层不存在,将导致这个不存在的数据每次请求都要到存储层去查询,直接对DB造成影响。在恶意攻击和失败回调中可能会出现这种情况。

  • 解决方案

1.对空对象进行缓存。对查询结果为空的情况也进行缓存,如当此查询结果为空,设置Key对应对象为NULL,缓存时间设置短一点,存储层中有数据后及时更新。

2.对所有可能查询的参数Key以hash形式存储,在控制层先进行校验,不符合则丢弃。最常见的是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。比较适合命中不高,但是更新不频繁的数据。

 

缓存失效

  • 场景:缓存中大量的Key集中在一段时间内失效,数据库的压力凸显
  • 解决方案

1.可以分析用户行为,尽量让失效时间点均匀分布。针对失效时间相同的key,在设置失效时间时不设置固定的时间,而在原有基础上加上一个随机的值,比如1分钟-5分钟,这样就可以有效分散开缓存失效的时间。

2.考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

 

缓存并发

  • 场景:在高并发场景下,某些业务有可能多个请求并发的去从数据库获取数据

有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大。

  • 解决方案

1.添加分布式锁,在缓存更新或者过期的情况下,先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据。

2.定期从DB里查询数据,再刷到缓存里面,确保缓存里面的数据一直可以读到。

 

缓存雪崩

  • 场景:当发生大量的缓存穿透,例如缓存挂掉,或者对某个失效的缓存的大并发访问

由于缓存扛了大量的请求,有效保护了数据库的安全。但是当缓存雪崩,所用请求就会瞬间全部打到DB上,可能会导致数据库崩溃。

  • 解决方案

1.保证缓存服务的高可用性,当一个实例挂掉的时候,请求也可以转移到集群的其他实例上。缓存失效时的雪崩效应对底层系统的冲击非常大,这时候可以使用双缓存机制,在工作缓存之外另外维护一层灾备缓存。

2.使用降级策略,当缓存服务出现问题时,可以暂时对用户展示一份固定的数据,避免系统的崩溃,等待缓存服务的恢复。前端也应该有此机制,比如当后端接口返回非正常数据时,将之前保存的旧数据固定展示给用户,避免页面崩溃的问题。

 

缓存数据的淘汰

 

缓存淘汰的策略有两种:

1.定时去清理过期的缓存

2.当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存

两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,根据自己的应用场景来权衡。

 

更新缓存还是淘汰缓存

 

什么是更新缓存:数据不但写入数据库,还会写入缓存

什么是淘汰缓存:数据只会写入数据库,不会写入缓存,只会把数据淘汰掉

更新缓存的优点:缓存不会增加一次miss,命中率高

淘汰缓存的优点:简单

 

先操作数据库还是先操作缓存

 

 

假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。

假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss。

所以结论是:先淘汰缓存,再写数据库

 

参考:

缓存与数据库一致性保证

缓存更新的套路

相关文章:

  • 一个简单的例子演示:通过浏览器的滚动条来动态加载数据
  • Code Kata:螺旋矩阵 javascript实现
  • C++容器与算法
  • PostgreSQL在何处处理 sql查询之四十三
  • Tomcat建立多个应用(Web Server),多个主机,多个站点的方法
  • org.tmatesoft.svn.core.SVNCancelException: svn: E200015: authentication canc
  • Linux下查看Tomcat的控制台输出信息
  • 每天一句话
  • Android基础 使用ToolBar教你打造一个通用的标题栏
  • 单点登录配置问题
  • Mac 10.11 React Native 安装记录
  • 字符串编辑距离(转载)
  • 2.10-2.13环境变量、CP、MV、文档查看
  • Civil 3D 2013新功能及新API Webcast下载
  • wpf treeview 数据绑定 递归绑定节点
  • [deviceone开发]-do_Webview的基本示例
  • CSS3 变换
  • flask接收请求并推入栈
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Laravel 中的一个后期静态绑定
  • PHP CLI应用的调试原理
  • python_bomb----数据类型总结
  • STAR法则
  • Sublime Text 2/3 绑定Eclipse快捷键
  • 初识 webpack
  • 对超线程几个不同角度的解释
  • 如何设计一个微型分布式架构?
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 线上 python http server profile 实践
  • 项目实战-Api的解决方案
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 自定义函数
  • 积累各种好的链接
  • ​虚拟化系列介绍(十)
  • ###C语言程序设计-----C语言学习(3)#
  • #stm32驱动外设模块总结w5500模块
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (黑马C++)L06 重载与继承
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (译)2019年前端性能优化清单 — 下篇
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET 设计一套高性能的弱事件机制
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .NET企业级应用架构设计系列之结尾篇