缓存分为客户端缓存和服务端缓存,服务端缓存又分本机缓存和网络缓存,我们要讨论的是网络中的缓存,也就是缓存与真实数据的交换需要通过网线的情况,我们假设讨论的背景:数据库1主1从,读写分离,缓存、数据库、网站应用分别处在不同的机器上。顺序上,我们先了解概念,再讨论制作缓存的方式,最后一一讨论概念部分提出的问题的解决方案。通常来说,本机缓存的情况比网络缓存要相对简单,因此,本机缓存也可以参考本文的讨论结果。
1 概念
缓存一致性问题:同一条记录,缓存中的数据和数据库的数据不一致。
缓存穿透:大规模查询请求没有命中缓存(也就是这些记录没有被缓存),缓存这一层被击穿,请求直接到达数据库。
缓存雪崩:大批缓存几乎同时过期,查询请求直接到达数据库。
缓存并发:多个请求并发查询同一条没有缓存的记录,造成并发读数据库,并发写缓存。
缓存失效策略:缓存中的记录不可能无限制增多,一部分缓存必然被淘汰,主动淘汰这些记录的策略就是缓存失效策略。
2 缓存更新策略
缓存更新策略就是缓存的制作方式,缓存更新策略通常是在努力优化“速度、一致性、低脏数据概率和持久化程度”几个特性,主流策略有三种:Cache Aside、Read/Write Through、Write Behind,下面就它们的原理和特性做一点总结,我们用A的数量来表示该特性的得分,A越多越好,但是AA不代表是A的两倍性能。
2.1 Cache Aside
读线程( R):去Cache中读数据,如果没有命中,去DB中读取,更新Cache中的数据。
写线程( W):去DB中写数据,让缓存失效。
不让写线程写完后更新缓存的原因:如果R的读在W的写之前,同时R的缓存更新在W的缓存更新之后,那么缓存中存储的是R的数据,形成脏数据。
特点:速度(A);缓存一致性(AA),脏数据少(A),持久化程度高(AA)。
优点:速度快;缓存一致性强,脏数据少;数据持久化程度高,不易丢失数据。
缺点:存在一定概率的脏数据。
2.2 Read/Write Through
读写线程直接从Cache中读写,无视DB的存在。DB的数据更新采用同步更新方式。
特点:速度(AA);缓存一致性(AA),脏数据更少(AA),持久化程度高(AA)。
优点:速度快;缓存一致性更强,脏数据更少。
缺点:数据持久化程度较低,可能导致数据丢失;适合数据量偏小的情况,数据量大时不能全部加载进内存;启动完成前需要数据预热,否则查询不到数据。
2.3 Write Behind
读写线程直接从Cache中读写,无视DB的存在。DB的数据更新采用异步任务更新方式。
特点:速度(AA);缓存一致性(A),脏数据更少(AA),持久化程度较高(A)。
优点:速度快;缓存一致性强,脏数据更少。
缺点:数据持久化程度低,可能导致数据丢失;适合数据量偏小的情况,数据量大时不能全部加载进内存;启动完成前需要数据预热,否则查询不到数据。
可以看到,由于内存与磁盘的速度差距悬殊,更强的速度意味着更弱的缓存一致性,Read/Write Through是在另外两个策略之间的折中方案,我们很难在一个策略中同时提升这几个特性。
3 缓存一致性
多数情况下我们使用Cache Aside策略,所以这里缓存一致性也讨论的是该策略及其变种策略。因为Read/Write Through、Write Behind两种策略可能需要应用层大改,会引入更高的复杂度,而且高度依赖缓存,而Cache Aside策略相对更加可靠。
Cache Aside策略的使用:
绝对的缓存一致很难实现,看起来缓存和数据库中的数据始终有一个不一致的时间间隙,在这个间隙中存在脏读的可能性。