背景
缓存是数据库的副本,应用在查询数据时,先从缓存中查询,如果命中直接返回,如果未命中,去数据库查询最新数据并返回,同时写入缓存。
问题
当业务发生时,系统状态改变,需要同时修改数据库和缓存的数据。如何保证应用从缓存读取到最新的数据,且即使数据库立即崩溃,数据也不丢失?这就是缓存与数据库的一致性问题。
分析
一个系统状态同时存在于缓存和数据库,缓存是数据库的副本,数据库可以读和写,把缓存的写看作是读缓存未命中的一部分,则缓存只有读。只要保证数据库写对缓存读的可见性,就能保证它们的一致性。下面通过串行数据库写与缓存读来解决问题。
解决方案
读缓存
- 如果命中直接返回。
- 如果未命中
- 获取key对应的分布式锁
- 读数据库
- 返回结果并更新缓存
- 释放key对应的分布式锁
写数据库
- 获取缓存key对应的分布式锁
- 修改数据库
- 操作缓存删除键(放修改数据库后面,在修改过程中缓存还有效,放前面就早失效)
- 释放缓存key对应的分布式锁
总结
在系统状态不变时,缓存与数据库是一致的。在系统状态变化时,通过分布式锁串行化对状态的读写。写时先让缓存(即数据库副本)失效,只需保证数据库本身的更新;写后的读操作恢复缓存。即状态变化过程中,只有数据库保存状态,没有副本,数据是一致的。
====================
实现优化点
因分布式锁是针对单个key的状态标记,是一个信号量而不是资源锁定,所以可以用一个(本地或远程)原子操作替代。