示例代码
2. 缓存更新我们把常见的缓存更新方案总结为两大类,业务层更新和外部组件更新,比较常见的是通过业务更新的方案。
(1)业务层更新缓存 a. 缓存更新的难点刚开始接触缓存方案的同学可能会纠结几个点,先更新缓存还是先更新存储,缓存的处理是通过删除来实现还是通过更新来实现。
这里我们面临的问题本质上是一个数据库的分布式事务的问题,需要处理数据可靠性的挑战,并发更新带来的隔离性挑战,和数据更新原子性的挑战。
数据可靠性:如果要保证数据的可靠性,在业务逻辑成功之前,必须保障有一份数据落地,我们有以下两个选择:
先更新成功存储,再更新缓存;
先更新成功缓存,再更新存储,如果存储更新失败,删除缓存。
操作隔离性:一条数据的更新涉及到存储和缓存两套系统,如果多个线程同时操作一条数据,并且没有方案保证多个操作之间的有序执行,就可能会发生更新顺序错乱导致数据不一致的问题。
更新原子性:引入缓存后,我们需要保证缓存和存储要么同时更新成功,要么同时更新失败,否则部分更新成功就会导致缓存和存储数据不一致的问题。
b. 业务层缓存更新方案我们看到大多数的常见是选择以下方案,保障数据可靠性,尽量减少数据不一致的出现,通过TTL超时机制在一定时间段后自动解决数据不一致现象。
Step1:更新存储,保证数据可靠性;
Step2:更新缓存,2个策略怎么选:
惰性更新:删除缓存,等待下次读MISS再缓存(推荐方案);
积极更新:将最新的值更新到缓存(不推荐)。
积极更新策略,缓存数据实时性更高,但是在缓存侧带来了更多的更新操作,这会提高更新冲突导致脏数据概率。
(2)外部组件更新缓存 a. 缓存MISS处理方案在通过第三方组件更新的方案中,为了保障数据的一致性,避免对单条数据的并行更新,缓存的所有更新操作都需要交给同步组件,因此缓存MISS场景下的逻辑
b. 缓存更新方案先更新存储,由第三方组件异步更新缓存。该方案投入较大,只适合特定的场景,并且有以下3个难点:
需要监控存储的日志,或者通过Triger来监控存储数据的变更,需要对存储系统非常熟悉;
需要对更新进行过滤,我们的目的是缓存热数据,但是像DDL、批量更新这一系列的操作是不需要更新缓存的,要把非业务更新操作过滤;
同步组件需要理解数据,不通用。
c. 其他缓存更新方案在实际的生产中,我们还会看到很多先更新缓存,然后通过第三方组件更新存储的场景,但是这个方案也会面临数据一致性和数据可靠性的挑战,虽然不推荐,但是确实还是能看到有在使用这个方案的,我们拿出来探讨下。
这个场景数据可靠性,不及先更新存储的方案,但是写入性能高,延迟低;
这个方案APP和第三方组件都会更新Cache,会存在数据一致性的问题,因为很难保障两个组件更新的时序。
3. 缓存淘汰缓存的作用是将热点数据缓存到内存实现加速,内存的成本要远高于磁盘,因此我们通常仅仅缓存热数据在内存,冷数据需要定期的从内存淘汰,数据的淘汰通常有两种方案: