缓存读写策略
我们都知道缓存大多数情况下是用来减轻数据库压力的。缓存读写策略就是在进行数据读/写时以何种策略读写缓存和数据库,即:读请求时先读缓存还是数据库,缓存中数据不存在怎么办,写请求时先更新数据库还是缓存,同步更新还是异步更新等一系列问题的方案。
Cache Aside Pattern
Cache Aside Pattern 译为旁路缓存模式。该模式以数据库为主,缓存为辅。主要策略如下:
读请求
在读请求时,先查询缓存:1. 缓存中存在,直接返回;2. 缓存中不存在,查询数据库,然后将结果写入缓存。
写请求
在写请求时,先更新数据库,然后直接删除对应缓存。
问题:写请求时,可以先删除缓存,后更新数据库吗?
答案:不能。可能会造成数据不一致问题。如:请求 1 对数据 A 发起写请求,先删除数据 A 的缓存,这时请求 2 对数据 A 发起读请求,由于缓存不存在,会先读取数据库中的 A 的值,然后写入缓存;之后请求1 更新数据库(此时数据 A 在缓存中的值和数据库中的值已经不一致了)。那么当下次对数据 A 的读请求来临时,由于缓存中存在数据 A ,直接返回,但是此时缓存中 A 的数据和数据库中 A 的数据不一致。
问题:写请求时,先更新数据库,后删除缓存就一定没有问题吗?
答案:不一定,但大概率没有问题。如:请求 1 对数据 A 发起读请求,缓存中不存在,这时请求 2 对数据 A 发起写请求,先更新数据库中的 A 的值,然后删除缓存;之后请求1 将自己读取到的数据写入缓存。这个时候数据 A 在缓存和数据库中的值也不一致了。但是由于缓存写入速度远高于数据库写入速度,请求 1 写入缓存
一般比请求 2 写入数据库然后删除缓存
先完成。
该模式适合读多的场景。
旁路缓存模式的缺点
-
第一次读取时肯定会先读取数据库。
解决方案:事先将热点数据载入缓存(缓存预热)。
-
写操作频繁时会频繁删除缓存中的数据,导致缓存命中率较低。
解决方案:1. 更新数据库的同时更新缓存,注意不是删除缓存,这通常需要加锁来保证这两个操作的原子性。适用于数据强一致性的场景。2. 更新数据库的同时更新缓存,并给缓存设置一个较短的存活时间,并且不需要加锁,但会出现数据不一致的问题。适用于可以接受短暂的数据不一致的场景。
Read/Write Through Pattern
Read/Write Through Pattern 译为读写穿透模式。该模式以缓存为主,数据库为辅。主要策略如下:
读请求
和旁路缓存模式类似,先查询缓存:1. 缓存中存在,直接返回;2. 缓存中不存在,缓存服务自动从数据库中读取数据写入缓存,然后返回。
和旁路缓存模式的区别就是,旁路缓存模式是我们手动写入缓存,而读写穿透模式是自动从数据库中读取数据并写入缓存。
写请求
在写请求时,先查询缓存中存不存在:1. 不存在,直接写入数据库。2. 存在,先更新缓存,然后同步
更新数据库。
Write Behind Pattern
Write Behind Pattern 又称 Write Back 。类似于前面的 Read/Write Through Pattern,都是以缓存为主,数据库为辅。主要策略如下:
读请求
和 Read/Write Through Pattern 一样。
写请求
在写请求时,先查询缓存中存不存在:1. 不存在,直接写入数据库。2. 存在,先更新缓存,然后异步批量
更新数据库。
这种模式写性能非常好,因为都是直接写缓存,但问题是数据不是强一致性的,还可能会导致数据丢失。适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。