标签:情况下 服务端 冷数据 意义 成功 acm ext 越界 一个
对于web来说,是用户量和访问量支持项目技术的更迭和前进。随着服务用户提升。可能会出现一下的一些状况:
足以支撑
自己逻辑业务的发展。那么其实可以不加缓存。最多对静态页面进行缓存即可。反复被查询
或者查询速度较慢
。那么就可以考虑使用缓存技术优化。对高命中的对象存到key-value形式的redis中,那么,如果数据被命中,那么可以省经效率很低的db。从高效的redis中查找到数据。当然,可能还会遇到其他问题,你可以需要静态页面本地缓存,cdn加速,甚至负载均衡这些方法提高系统并发量。这里就不做介绍。
我们从一个算法问题开始了解缓存的意义。
问题1:
n!
;分析1:
n!=n * (n-1) * (n-2) * ... * 1= n * (n-1)!
;static long jiecheng(int n)
{
if(n==1||n==0)return 1;
else {
return n*jiecheng(n-1);
}
}
这样每输入求一次需要执行n
次。
问题2:
x!
;分析2:
递归
,输入t组数据,每个位x,那么每次都要执行时间复杂度
为O(n^2^)打表
(也可以理解位动态规划
)。打表常用于ACM算法中,常用于解决多组输入输出、图论搜索结果、路径储存问题。那么,对于这个求阶乘。我们只需要申请一个数组。每个数据为前一个数据
*当前index
。那么思想很明确啦!import java.util.Scanner;
public class test3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
int t=sc.nextInt();
long jiecheng[]=new long[21];
jiecheng[0]=1;
for(int i=1;i<21;i++)
{
jiecheng[i]=jiecheng[i-1]*i;
}
for(int i=0;i<t;i++) {
int x=sc.nextInt();
System.out.println(jiecheng[x]);
}
}
}
缓存
思想差不多。先将数据在jiecheng[21]数组中储存。执行一次计算。当后面继续访问的时候就相当于当问静态数组值。为O(1)。就能大大的减少查询、执行成本啦!经常被访问的数据
或者查询成本较高
从慢的介质中存到比较快的介质中,比如从硬盘
—>内存
。我们知道大多数关系数据库是基于硬盘读写
的,其效率和资源有限,而redis等非关系型就是基于内存存储。其效率差别很大。当然,缓存也分为本地缓存和服务端缓存,这里只讲redis的服务端缓存。需要2s
。你每次查询都会2s并且加载的时候都会等在,这个长期等待给用户的体验是非常糟糕
的。而用户能够接受的往往是第一次
的等待。如果你用了缓存技术。你第一次查询放到redis里面。然后数据再从redis返回给你。后面当你继续访问这个数据的时候。查询到redis中有备份,那么不需要通过db
直接能从redis
中获取数据。那么,你想想,从一个key value的Nosql中取一个value能要多久呢!necessary
的.redis也是必不可少的。并且服务端的缓存设计也是要根据业务有所区别的。也要防止占用内存过大,redis雪崩等问题。现在不少项目,为了缓存而缓存,然而缓存并不是适合所有场景,比如如果对数据一致性
要求极高,又或者数据频繁
更改而查询并不多。有的可以不需要缓存。因为如果使用redis缓存多多少少可能会遇到数据一致性问题。那你可以考虑使用redis做成分布式锁去锁sql的数据。同样如果频繁更新数据,那么redis能起到的作用就仅仅是多了一层中转站。反而浪费资源
。使得传输过程臃肿。
不适合缓存一致存在
,首先,你的sql数据库的内容可能很多就不说了,另外,返回给你的对象如果是完整的pojo对象还好,但是如果是使用不同参数各种关联查询出来的结果那么redis中会储存太多冷数据。占用资源而得不到销毁。我们学过操作系统
也知道在计算机的缓存实现
中有)先进先出的算法(FIFO);最近最少使用算法(LRU);最佳淘汰算法(OPT);最少访问页面算法(LFR)等磁盘调度算法。对于web开发也可以借鉴。根据时间来的FIFO是最好实现的。因为redis在全局key
支持过期策略。其他问题
。比如过期时间的选择上,如果过久会导致数据聚集。而过少可能导致频繁查询数据库甚至可能会导致缓存雪崩等问题。所以,过期策略一定要设置。并且对于关键key
一定要小心谨慎设计
。
上面其实提到数据一致性问题。如果对一致性要求极高那么不建议使用缓存。下面稍微梳理一下缓存的数据。
在redis缓存中经常会遇到数据一致性问题。对于一个缓存。下面罗列逼仄
read
:从redis中读取,如果redis中没有,那么就从mysql中获取更新redis缓存。
该流程图
描述常规场景。一般没啥争议。
数据不一致
。造成DB新数据,缓存旧数据。
解决的问题
mysql
取得最新值到缓存中。但是这种情况仅限于低并发的场景中而不适用
高并发场景。存在的问题
看似写入redis异常的问题
。看似较为好的解决方案但是在高并发的方案中其实还是有问题的。我们在写1讨论过如果更新库成功,缓存更新失败会导致脏数据。我们理想是删除缓存让下一个线程
访问适合更新缓存。问题是:如果这下一个线程来的太早、太巧了呢?上锁
。但是锁这种重量级的东西对并发功能影响太大,能不用锁就别用!上述情况就高并发下依然会造成缓存是旧数据,DB是新数据。并且如果缓存没有过期这个问题会一致存在。删除缓存
->更新缓存
->延时(几百ms)(可异步)再次删除缓存
。即使在更新缓存途中发生写2的问题。造成数据不一致,但是延时(具体实间根据业务来,一般几百ms)再次删除也能很快的解决不一致。第二次删除错误
、多写多读高并发
情况下对mysql访问的压力等等。当然你可以选择用mq等消息队列异步解决。其实实际的解决很难顾及到万无一失,所以不少大佬在设计
这一环节可能会因为一些纰漏
会被喷
。作为菜菜的笔者在这里就更不献丑了,策略只是提供大纲,具体设计还是需要自己团队实践和摸索。并且也对一致性的要求级别有所区别。一堆并发(写)
扔过来的后,前面几个方案即使使用消息队列异步通信但也很难给用户一个舒适的体验。并且对大规模操作sql对系统也会造成不小的压力。所以还有一种方案就是直接操作缓存,将缓存定期写入sql。因为redis这种非关系数据库又基于内存操作KV相比传统关系型要快很多(找值最多多碰撞几次)。读
、写
的顺序等等可能有不同要求,可能还要借助消息队列
以及锁
完成针对业务上对数据和顺序可能会因为高并发、多线程
带来的不确定性和不稳定性。提高业务可靠性。总之,越是高并发
、越是对数据一致性要求高
的方案在数据一致性的设计方案需要考虑和顾及
的越复杂、越多
。上述也是笔者针对redis数据一致性问题的学习和自我发散(胡扯)学习。如果有解释理解不合理或者还请联系告知!
如果不了解,可能对这几个概念都不了解,听着感觉太高大上,至少笔者刚开始是这么觉得,本文并不是详细介绍如何解决和完美解决,更主要的是认识和认知吧。
理解
穿透
吧,也就是访问透过redis直接经过mysql,通常是一个不存在的key
,在数据库查询为null
。每次请求落在数据库、并且高并发。数据库扛不住会挂掉。解决方案
验证
。其他等等,比如看到其他人用布隆过滤器(超大型hashmap)过滤。
雪崩,就是某东西蜂拥而至
的意思,像雪崩一样。在这里,就是redis缓存集体大规模集体失效
,在高并发情况下突然使得key大规模访问mysql,使得数据库崩掉。可以想象下国家人口老年化
。以后那天人集中在70-80岁,就没人干活了。国家劳动力就造成压力。
解决方案
随机数
,让key均匀的失效。理解
绕过
redis去使得数据库崩掉。而击穿你可以理解为正面刚
击穿,这种通常为大量并发对一个key进行大规模的读写操作。这个key在缓存失效期间大量请求数据库,对数据库造成太大压力使得数据库崩掉。就比如
在秒杀场景下10000块钱的mac和100块的mac这个100块的那个订单肯定会被抢到爆。所以缓存击穿就是针对某个常用key大量请求导致数据库崩溃。解决方案
有学问
。在细节设计上还会遇到消息队列、布隆过滤器、分布式锁、服务降级、熔断、分流这些。在缓存处理上甚至还有缓存预热
(提前缓存部分热点数据防止刚开始缓存全部命中导致服务崩掉)等其他热门名词和问题这里就不做介绍了。操作系统
的存储管理以及可能遇到的锁的设计上与读者优先、写者优先有着很大关系,大家可以参考和交流!当然,redis的内容深度很深,笔者水平有限可能有地方有错误还请大佬指出或者交流。当然本文基本为笔者个人理解难免有疏漏。同时写本文前也阅读了一些前辈的文章学习(转来转去不知道谁是原创就不放链接)还请多多指教!
如果对后端、爬虫、数据结构算法
等感性趣欢迎关注我的个人公众号交流:bigsai
标签:情况下 服务端 冷数据 意义 成功 acm ext 越界 一个
原文地址:https://www.cnblogs.com/bigsai/p/11314669.html