TreeviewCopyright © qgao 2021-* all right reserved, powered by aleen42
缓存常用的3种读写策略(保证缓存与数据库的一致性)
3 种缓存读写策略各有优劣,不存在最佳,需要我们根据具体的业务场景选择更适合的。
旁路(Cache Aside Pattern)
平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。
写 :
- 先更新 DB;
- 然后直接删除 cache 。
读 :
- 从 cache 中读取数据,读取到就直接返回;
- cache中读取不到的话,就从 DB 中读取数据返回;
- 再把数据放到 cache 中。
问题
在写数据的过程中,可以先删除 cache ,后更新 DB 么?
不行,可能会造成多线程读取的数据不一致,即数据库(DB)和缓存(Cache)数据不一致,
比如:
请求1先把cache中的A数据删除 -> 请求2从DB中读取数据->请求1再把DB中的A数据更新。
就和volatile一样,必须保证读之前的写入要对所有的线程可见。
在写数据的过程中,先更新DB,后删除cache就没有问题了么?
理论上可能会出现数据不一致,不过概率非常小,因为缓存的写入速度远快于数据库的写入速度。
出现不一致的情况,比如:
请求1从DB读数据A->请求2写更新数据 A 到数据库并把删除cache中的A数据->请求1将数据A写入cache。
缺陷
缺陷1:首次请求数据一定不在 cache 的问题
解决办法:可以将热点数据可以提前放入cache 中。
缺陷2:写操作比较频繁的话导致cache中的数据会被频繁被删除,这样会影响缓存命中率 。
解决办法:
数据库和缓存数据强一致场景 :更新DB的时候同样更新cache,不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程安全问题。
可以短暂地允许数据库和缓存数据不一致的场景 :更新DB的时候同样更新cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
读写穿透(Read/Write Through Pattern)
在 "旁路" 之上进行了封装,
写(Write Through):
- 先查 cache,cache 中不存在,直接更新 DB。
- cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(同步更新 cache 和 DB)。
读(Read Through):
- 从 cache 中读取数据,读取到就直接返回 。
- 读取不到的话,先从 DB 加载,写入到 cache 后返回响应。
异步缓存写入(Write Behind Pattern)
"异步" 和 "读写穿透" 很相似,两者都是由 cache 服务来负责 cache 和 DB 的读写。
"读写穿透" 是同步更新 cache 和 DB,
而 "异步" 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。