撇开业务谈技术都是耍流氓。——KevinWan为什么需要缓存?让我们从一个老问题开始:我们的程序是如何工作的?该程序存储在磁盘中。程序运行在RAM中,即我们所说的主存程序的计算逻辑是在CPU中执行的。我们看一个最简单的例子:a=a+1loadx:x0=x0+1loadx0->RAM有上面提到的3种存储介质。我们都知道三种类型的读写速度与成本成反比,所以我们需要引入一个中间层来克服速度问题。这个中间层需要高速访问,但是成本是可以接受的。于是乎,Cache被引入,在计算机系统中,默认有两种缓存:CPU中的末级缓存,即LLC。缓存内存中数据内存中的高速页面缓存,即页面缓存。缓存盘中的数据缓存读写策略引入Cache后,我们继续看缓存发生了什么。因为存在访问速度的差异“而且差异非常大”,在操作数据时,延迟或者程序故障都会导致缓存和实际存储层的数据不一致。下面我们来看看标准的Cache+DB的经典读写策略和应用场景。CacheAside先考虑最简单的业务场景,比如user表:userId:用户id,phone:用户phonetoken,avtoar:用户头像url,我们以phone为key在缓存中存储用户头像。用户修改头像url怎么办?UpdateDBdata,thenupdateCachedataUpdateDBdata,anddeleteCachedata首先,改数据库和改缓存是两个独立的操作,我们没有对这些操作进行并发控制。那么当两个线程并发更新的时候,就会因为写入顺序的不同导致数据不一致。所以比较好的方案是2:更新数据的时候不更新缓存,而是直接删除缓存。后续请求发现缓存丢失,返回查询DB,将结果加载到缓存中。这个策略就是我们最常用的缓存策略:CacheAside。策略数据基于数据库中的数据,按需加载缓存中的数据,分为读策略和写策略。但是也有可见的问题:频繁的读写操作会导致Cache被反复更换,缓存命中率下降。当然,如果业务中有命中率的监控告警,可以考虑如下方案:在更新数据的同时更新缓存,但是在更新缓存前加分布式锁。这样同一时间只有一个线程操作缓存,解决了并发问题。同时在后续的读请求中读取最新的缓存,解决了不一致的问题。在更新数据的同时更新缓存,但给缓存一个更短的TTL。当然,除了这种策略,计算机系统中还有其他几种经典的缓存策略。
