热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

redis解析:缓存及常见问题!

01没缓存的日子对于web来说,是用户量和访问量支持项目技术的更迭和前进。随着服务用户提升。可能会出现一下的一些状况:页面并发量和访问量并不多

01 没缓存的日子

对于web来说,是用户量和访问量支持项目技术的更迭和前进。随着服务用户提升。可能会出现一下的一些状况:

页面并发量和访问量并不多,mysql足以支撑自己逻辑业务的发展。那么其实可以不加缓存。最多对静态页面进行缓存即可。

页面的并发量显著增多,数据库有些压力,并且有些数据更新频率较低反复被查询或者查询速度较慢。那么就可以考虑使用缓存技术优化。对高命中的对象存到key-value形式的redis中,那么,如果数据被命中,那么可以省经效率很低的db。从高效的redis中查找到数据。

当然,可能还会遇到其他问题,你可以需要静态页面本地缓存,cdn加速,甚至负载均衡这些方法提高系统并发量。这里就不做介绍。


02 缓存思想无处不在

我们从一个算法问题开始了解缓存的意义。

问题1&#xff1a;输入一个数n(n<20),求n&#xff01;&#xff1b;

分析1&#xff1a;单单考虑算法&#xff0c;不考虑数值越界问题。当然我们知道n!&#61;n * (n-1) * (n-2) * ... * 1&#61; n * (n-1)!;那么我们可以用一个递归函数解决问题。

这样每输入求一次需要执行n次。

问题2&#xff1a;输入t组数据(可能成百上千)&#xff0c;每组一个x(n<20),求x&#xff01;&#xff1b;

分析2&#xff1a;

时间复杂度才O(n)。这里的思想就和缓存思想差不多。先将数据在jiecheng[21]数组中储存。执行一次计算。当后面继续访问的时候就相当于当问静态数组值。为O(1)。就能大大的减少查询、执行成本啦&#xff01;


03 缓存的应用场景

 

缓存适用于高并发的场景&#xff0c;提升服务容量。主要是将从经常被访问的数据或者查询成本较高从慢的介质中存到比较快的介质中&#xff0c;比如从硬盘—>内存。我们知道大多数关系数据库是基于硬盘读写的&#xff0c;其效率和资源有限&#xff0c;而redis等非关系型就是基于内存存储。其效率差别很大。当然&#xff0c;缓存也分为本地缓存和服务端缓存&#xff0c;这里只讲redis的服务端缓存。

举个例子。例如如果一个接口sql查询需要2s。你每次查询都会2s并且加载的时候都会等在&#xff0c;这个长期等待给用户的体验是非常糟糕的。而用户能够接受的往往是第一次的等待。如果你用了缓存技术。你第一次查询放到redis里面。然后数据再从redis返回给你。后面当你继续访问这个数据的时候。查询到redis中有备份&#xff0c;那么不需要通过db直接能从redis中获取数据。那么&#xff0c;你想想&#xff0c;从一个key value的Nosql中取一个value能要多久呢&#xff01;

所以对于像样的&#xff0c;有点规模的网站&#xff0c;缓存isnecessary的.redis也是必不可少的。并且服务端的缓存设计也是要根据业务有所区别的。也要防止占用内存过大&#xff0c;redis雪崩等问题。


04 需要注意的问题

缓存使用不当会带来很多问题。所以需要对一些细节进行认真考量和设计。笔者对于分布式的经验并不是很丰富&#xff0c;就相对于笔者的眼中谈谈缓存设计不好会带来那些问题。

&#xff08;1&#xff09;是否用缓存

现在不少项目&#xff0c;为了缓存而缓存&#xff0c;然而缓存并不是适合所有场景&#xff0c;比如如果对数据一致性要求极高&#xff0c;又或者数据频繁更改而查询并不多。有的可以不需要缓存。因为如果使用redis缓存多多少少可能会遇到数据一致性问题。那你可以考虑使用redis做成分布式锁去锁sql的数据。同样如果频繁更新数据&#xff0c;那么redis能起到的作用就仅仅是多了一层中转站。反而浪费资源。使得传输过程臃肿。

&#xff08;2&#xff09;过期策略选择

大部分场景不适合缓存一致存在&#xff0c;首先&#xff0c;你的sql数据库的内容可能很多就不说了&#xff0c;另外&#xff0c;返回给你的对象如果是完整的pojo对象还好&#xff0c;但是如果是使用不同参数各种关联查询出来的结果那么redis中会储存太多冷数据。占用资源而得不到销毁。我们学过操作系统也知道在计算机的缓存实现中有&#xff09;先进先出的算法&#xff08;FIFO&#xff09;&#xff1b;最近最少使用算法&#xff08;LRU&#xff09;&#xff1b;最佳淘汰算法&#xff08;OPT&#xff09;&#xff1b;最少访问页面算法&#xff08;LFR&#xff09;等磁盘调度算法。对于web开发也可以借鉴。根据时间来的FIFO是最好实现的。因为redis在全局key支持过期策略。

而开发中可能还会遇到其他问题。比如过期时间的选择上&#xff0c;如果过久会导致数据聚集。而过少可能导致频繁查询数据库甚至可能会导致缓存雪崩等问题。

所以&#xff0c;过期策略一定要设置。并且对于关键key一定要小心谨慎设计。

&#xff08;3&#xff09;数据一致性问题★

上面其实提到数据一致性问题。如果对一致性要求极高那么不建议使用缓存。下面稍微梳理一下缓存的数据。

在redis缓存中经常会遇到数据一致性问题。对于一个缓存。下面罗列逼仄

① 读

read&#xff1a;从redis中读取&#xff0c;如果redis中没有&#xff0c;那么就从mysql中获取更新redis缓存。该流程图描述常规场景。一般没啥争议。

② 写1&#xff1a;先更新数据库&#xff0c;再更新缓存(普通低并发)

更新数据库信息&#xff0c;再更新redis缓存。这是常规做法&#xff0c;缓存基于数据库&#xff0c;取自数据库。但是其中可能遇到一些问题。例如上述如果更新缓存失败(宕机等其他状况)&#xff0c;将会使得数据库和redis数据不一致。造成DB新数据&#xff0c;缓存旧数据

③ 写2&#xff1a;先删除缓存&#xff0c;再写入数据库(低并发优化)

解决的问题

这种情况能够有效避免写1中防止写入redis失败的问题。将缓存删除进行更新。理想是让下次访问redis为空去mysql取得最新值到缓存中。但是这种情况仅限于低并发的场景中而不适用高并发场景。

存在的问题

写2虽然能够看似写入redis异常的问题。看似较为好的解决方案但是在高并发的方案中其实还是有问题的。我们在写1讨论过如果更新库成功&#xff0c;缓存更新失败会导致脏数据。我们理想是删除缓存让下一个线程访问适合更新缓存。问题是&#xff1a;如果这下一个线程来的太早、太巧了呢&#xff1f;

因为多线程你也不知道谁先谁后&#xff0c;谁快谁慢。如上图所示情况&#xff0c;将会出现redis缓存数据和mysql不一致。当然你可以对key进行上锁。但是锁这种重量级的东西对并发功能影响太大&#xff0c;能不用锁就别用&#xff01;上述情况就高并发下依然会造成缓存是旧数据&#xff0c;DB是新数据。并且如果缓存没有过期这个问题会一致存在。

④ 写3&#xff1a;延时双删策略

这个就是延时双删策略&#xff0c;能过缓解在写2中在更新mysql过程中有读的线程进入造成redis缓存与mysql数据不一致。方法就是删除缓存->更新缓存->延时(几百ms)(可异步)再次删除缓存。即使在更新缓存途中发生写2的问题。造成数据不一致&#xff0c;但是延时(具体实间根据业务来&#xff0c;一般几百ms)再次删除也能很快的解决不一致。

但是就写的方案其实还是有漏洞的&#xff0c;比如第二次删除错误、多写多读高并发情况下对mysql访问的压力等等。当然你可以选择用mq等消息队列异步解决。其实实际的解决很难顾及到万无一失&#xff0c;所以不少大佬在设计这一环节可能会因为一些纰漏会被喷。作为菜菜的笔者在这里就更不献丑了&#xff0c;策略只是提供大纲&#xff0c;具体设计还是需要自己团队实践和摸索。并且也对一致性的要求级别有所区别。

⑤ 写4&#xff1a;直接操作缓存&#xff0c;定期写入sql(适合高并发)

当有一堆并发(写)扔过来的后&#xff0c;前面几个方案即使使用消息队列异步通信但也很难给用户一个舒适的体验。并且对大规模操作sql对系统也会造成不小的压力。所以还有一种方案就是直接操作缓存&#xff0c;将缓存定期写入sql。因为redis这种非关系数据库又基于内存操作KV相比传统关系型要快很多(找值最多多碰撞几次)。

上面适用于高并发情况下业务设计&#xff0c;这个时候以redis数据为主&#xff0c;mysql数据为辅助。定期插入(好像数据备份库一样)。当然&#xff0c;这种高并发往往会因为业务对读、写的顺序等等可能有不同要求&#xff0c;可能还要借助消息队列以及锁完成针对业务上对数据和顺序可能会因为高并发、多线程带来的不确定性和不稳定性。提高业务可靠性。

总之&#xff0c;越是高并发、越是对数据一致性要求高的方案在数据一致性的设计方案需要考虑和顾及的越复杂、越多。上述也是笔者针对redis数据一致性问题的学习和自我发散(胡扯)学习。如果有解释理解不合理或者还请联系告知&#xff01;


05 缓存穿透、缓存雪崩和缓存击穿

如果不了解&#xff0c;可能对这几个概念都不了解&#xff0c;听着感觉太高大上&#xff0c;至少笔者刚开始是这么觉得&#xff0c;本文并不是详细介绍如何解决和完美解决&#xff0c;更主要的是认识和认知吧。

&#xff08;1&#xff09;redis缓存穿透

① 理解

重在穿透吧&#xff0c;也就是访问透过redis直接经过mysql&#xff0c;通常是一个不存在的key,在数据库查询为null。每次请求落在数据库、并且高并发。数据库扛不住会挂掉。

② 解决方案

可以将查到的null设成该key的缓存对象。

当然&#xff0c;也可以根据明显错误的key在逻辑层就就行验证。

同时&#xff0c;你也可以分析用户行为&#xff0c;是否为故意请求或者爬虫、攻击者。针对用户访问做限制。

其他等等&#xff0c;比如看到其他人用布隆过滤器(超大型hashmap)过滤。

&#xff08;2&#xff09;redis缓存雪崩

① 理解

雪崩&#xff0c;就是某东西蜂拥而至的意思&#xff0c;像雪崩一样。在这里&#xff0c;就是redis缓存集体大规模集体失效&#xff0c;在高并发情况下突然使得key大规模访问mysql&#xff0c;使得数据库崩掉。可以想象下国家人口老年化。以后那天人集中在70-80岁&#xff0c;就没人干活了。国家劳动力就造成压力。

② 解决方案

通常的解决方案是将key的过期时间后面加上一个随机数&#xff0c;让key均匀的失效。

考虑用队列或者锁让程序执行在压力范围之内&#xff0c;当然这种方案可能会影响并发量。

&#xff08;3&#xff09;redis缓存击穿

① 理解

击穿和穿透不同&#xff0c;穿透的意思是想法绕过redis去使得数据库崩掉。而击穿你可以理解为正面刚击穿,这种通常为大量并发对一个key进行大规模的读写操作。这个key在缓存失效期间大量请求数据库&#xff0c;对数据库造成太大压力使得数据库崩掉。就比如在秒杀场景下10000块钱的mac和100块的mac这个100块的那个订单肯定会被抢到爆。所以缓存击穿就是针对某个常用key大量请求导致数据库崩溃。

② 解决方案

能够达到这种场景的公司其实不多&#xff0c;我也不清楚他们的具体处理方法&#xff0c;但是一个锁拦截请求总是能防止数据库崩掉吧。


06 总结与感悟

其实缓存看起来&#xff0c;理解起来看似简单然而实际上的设计方案非常有学问。在细节设计上还会遇到消息队列、布隆过滤器、分布式锁、服务降级、熔断、分流这些。在缓存处理上甚至还有缓存预热(提前缓存部分热点数据防止刚开始缓存全部命中导致服务崩掉)等其他热门名词和问题这里就不做介绍了。

 


推荐阅读
  • 消息中间件RabbitMQ 高级特性之消费端ACK与重回队列
    什么是消费端的ACK和重回队列?消费端的手工ACK和NACK消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿如果由于服务器宕机等严重问题 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 玩转直播系列之消息模块演进(3)
    一、背景即时消息(IM)系统是直播系统重要的组成部分,一个稳定的,有容错的,灵活的,支持高并发的消息模块是影响直播系统用户体验的重要因素。IM长连接服务在直播系统有发挥着举足轻重的 ... [详细]
  • OWASP(安全防护、漏洞验证工具)开放式Web应用程序安全项目(OWASP,OpenWebApplicationSecurityProject)是一个组织 ... [详细]
  • 一面自我介绍对象相等的判断,equals方法实现。可以简单描述挫折,并说明自己如何克服,最终有哪些收获。职业规划表明自己决心,首先自己不准备继续求学了,必须招工作了。希望去哪 ... [详细]
  • 1、概述首先和大家一起回顾一下Java消息服务,在我之前的博客《Java消息队列-JMS概述》中,我为大家分析了:然后在另一篇博客《Java消息队列-ActiveMq实战》中 ... [详细]
  • RabbitMQ的消息持久化处理
    1、RabbitMQ的消息持久化处理,消息的可靠性是RabbitMQ的一大特色,那么RabbitMQ是如何保证消息可靠性的呢——消息持久化。2、auto ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Python开源库和第三方包的常用框架及库
    本文介绍了Python开源库和第三方包中常用的框架和库,包括Django、CubicWeb等。同时还整理了GitHub中最受欢迎的15个Python开源框架,涵盖了事件I/O、OLAP、Web开发、高性能网络通信、测试和爬虫等领域。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 模块化区块链生态系统的优势概述及其应用案例
    本文介绍了相较于单体区块链,模块化区块链生态系统的优势,并以Celestia、Dymension和Fuel等模块化区块链项目为例,探讨了它们解决可扩展性和部署问题的方案。模块化区块链架构提高了区块链的可扩展性和吞吐量,并提供了跨链互操作性和主权可扩展性。开发人员可以根据需要选择执行环境,并获得奖学金支持。该文对模块化区块链的应用案例进行了介绍,展示了其在区块链领域的潜力和前景。 ... [详细]
  • python中安装并使用redis相关的知识
    本文介绍了在python中安装并使用redis的相关知识,包括redis的数据缓存系统和支持的数据类型,以及在pycharm中安装redis模块和常用的字符串操作。 ... [详细]
author-avatar
昔日重来r_510
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有