读取缓存的步骤:1,判断缓存中是否有数据,没有则从数据库加载,加载后,写入缓存。
缓存的更新策略:将其加入一个单独的缓存队列,让其不断执行删除;
采用的理由:性能:直接从缓存中获取数据。并发:大并发情况下,所有请求访问数据库,数据库会出现连接异常。
采用先访问缓存在访问数据库的方式。
为什么redis这么快;1,纯内存、单线程避免了频繁的上下文切换、采用了非阻塞的io多路复用的机制
定期删除与惰性删除相结合:每过一段时间随机删除过期的健;惰性删除:获取的时候判断是否过期。
缓存与数据库的双写一致性问题
双写一致性是向缓存和数据库都写数据,采取的策略是先写数据库再写删除缓存。为了解决删除缓存失败的问题,利用消息队列补偿。
延时双删除策略:先淘汰缓存,再写数据库,先休眠一段时间,再次淘汰缓存。
缓存雪崩问题
缓存同一时间大面积失效,这时候又来了一波请求,导致都去访问数据库。
解决方案:给缓存的失效时间,加上一个随机值,避免集体失效。使用互斥锁。
双缓存:从缓存a读取数据,没有则从b读取数据直接返回,并异步启动一个更新线程,同时更新两个缓存。
缓存击穿问题
故意请求缓存中不存在的数据,导致所有请求都访问数据库而导致连接异常
解决方案:利用互斥锁,缓存失效的时候,先获得锁再去请求数据库否则休眠。采用异步更新策略,无论取到值否都直接返回,每个值维护一个缓存失效时间,if过期则异步一个线程去读数据库。更新缓存。
布隆过滤器是对关键字进行哈希计算得到其值存入位数组中。
提供一个能迅速判断请求是否有效的拦截机制,比如布隆过滤器,判断是否合法。
缓存的并发竞争问题
采用分布式锁,抢到锁则操作;按顺序执行,保存一个时间戳;
延时任务:
定期任务与延时任务的区别:
定时任务是有明确的触发时间。而延时任务没有、
定时任务周期性的执行批量处理操作多个任务而延时任务一般是单个任务。
订单是否超时为列:
1,数据库轮询:一个线程定时扫描数据库,通过订单时间判断是否有超时的订单。缺点对内存库及数据库消耗比较大。
2.JDK自带的延迟队列,
3,时间轮算法。
4,redis缓存:每个元素关联一个score,通过score排序来取集合中的值。我们将订单超时时间戳与订单号分别设置为score和member,系统扫描第一个元素判断是否超时。出现多线程并发的问题:采用分布式锁
或者键空间机制,就是利用该机制可以在key失效之后,提供一个回调,实际上是redis会给客户端发送一个消息。是需要redis版本2.8以上。
5)使用消息队列
我们可以采用rabbitMQ的延时队列。RabbitMQ具有以下两个特性,可以实现延迟队列
分布式思想架构:
分拆:
1,大系统小做:
系统分拆:将复杂的系统分拆成多个子系统。每个子系统独立。子系统再分拆。系统-子系统-层-模块。
存储分拆:MongoDB,其天生就是分布式的,很容易实现数据的分片。 对于Mysql,或者其它关系型数据库,就会设计到分库分表。而分库分表,就会涉及到几个关键性的问题:
切分维度,join的处理,分布式事务
计算分拆:两种思路:将大数据集拆分成小数据集并行计算;按任务分拆:将一个长的任务拆分成几个环节,各个环节并行计算。
Java中多线程的Fork/Join框架,Hadoop中的Map/Reduce,都是计算分拆的典型框架。其思路都是相似的,先分拆计算,再合并结果。
再比如分布式的搜索引擎中,数据分拆,分别建索引,查询结果再合并。
并发:多线程并发;多次rpc顺序调用,通过异步rpc转化为并发调用。比如多次rpc顺序调用,通过异步rpc转化为并发调用;
比如数据分片,你的一个Job要扫描全表,跑几个小时,数据分片,用多线程,性能会加快好几倍。
缓存:
缓存大家都不陌生,遇到性能问题,大家首先想到的就是缓存。关于缓存,一个关键点就是:缓存的粒度问题。
比如Tweet的架构,缓存的粒度从小到大,有Row Cache, Vector Cache, Fragment Cache, Page Cache。
粒度越小,重用性越好,但查询需要多次,需要数据拼装;
粒度越大,越容易会失效,任何一个小的地方改动,都可能造成缓存的失效。
在线计算 vs. 离线计算 / 同步 vs. 异步
在实际的业务需求中,并不是所有需要都需要完全实时的:
比如内部针对产品、运营开发的各种报表查询、分析系统;
比如微博的传播,我发了一个微博,我的粉丝延迟几秒才看到,这是可以接受的,因为他并不会注意到晚了几秒;
比如搜索引擎的索引,我发了一篇博客,可能几分钟之后,才会被搜索引擎索引到;
比如支付宝转帐、提现,也并非这边转出之后,对方立即收到;
这类例子很多。这种“非实时也可以接受“的场景,就为架构的设计赢得了充分的回旋余地。
因为非实时,我们就可以做异步,比如使用消息队列,比如使用后台的Job,周期性处理某类任务;
也因为非实时,我们可以做读写分离,读和写不是完全同步,比如Mysql的Master-Slave。
全量 + 增量
全量/增量其实也是在线/离线的思路:
比如搜索引擎的全量索引 + 增量索引,前者是为了吞吐,后者为了实时;
比如OceanBase数据库,每次更新存在一个小表里面,定期merge;
Push vs Pull:所有分布式系统中都涉及到节点之间的状态通知问题:a变化,push给b;Pull: 也就是轮询。节点B周期性的去询问节点A的状态。
批量
批量其实也是在线/离线的一种思想,把实时问题,转化为一个批量处理的问题,从而降低对系统吞吐量的压力
比如Kafka中的批量发消息;
比如广告扣费系统中,把多次点击累积在一起扣费;
重写轻读:先复杂计算,存储起来,取的时候直接取。
读写分离:但在很多业务场景下,读和写并不需要完全同步。这个时候,就可以分开存储,写到一个地方,再异步的同步到另一个地方。这样就可以实现读写分离。
比如Mysql的Master/Slave就是个典型,Slave上面的数据并不是和Master实时同步的;
再比如各种报表分析,OLTP/OLAP,线上/线下数据分离,线上数据定期同步到Hive集群,再做分析。
CAP:
Consistency:数据一致性,这个很容易理解,就是没有脏数据。我们知道,在Mysql中有一致性的概念,比如参照完整性约束、事务等。但这里的C主要特指同1份数据的多个备份之间的一致性。
Availability:可用性有2重意思,一个是说稳定性,服务可用,不会挂;另外一个是性能,也就是要快,如果延迟很高,经常超时,那和挂了也就区别不大了。
Partition tolerance(分区容错性):分区,其实指网络分区。当你把数据从1个物理设备,分到多个物理设备之后,设备之间必然是通过网络进行通信。这就会遇到网络分区,也就是典型的“2将军问题“,网络超时时间不定。学术上有个词,叫“异步通信环境“。
最终一致性的实现,通常都需要一个高可靠的消息队列。