主要有两种策略
- 先更新db,后删缓存(有脏数据概率)先删缓存,后更新db(不保证一致性)
最经典的缓存+数据库读写的模式,也叫 Cache Aside Pattern。
1.1 问题- 存在读取脏数据的概率。线程1更新db,db刚被更新但是缓存还未删除时,这时线程2读的缓存是旧的数据。如果删除缓存失败,有不一致的风险
可以用队列来保证操作的顺序性。更新数据的时候,根据数据的唯一标识,如商品id,对唯一标识hash,分配到一个队列中,这样对相同数据的操作都会分配到同一队列里。这样可以降低读旧数据的概率。注意,读操作是缓存没有才会进队列里。
引入了队列的问题:
- 会增加系统的复杂性。要加机器,吞吐量也会降低。如果队列里挤压了很多别的商品的更新操作,需要等待这些操作完,才能读,这时候可以估算下系统的流量,然后加机器,多个队列平均分。读操作进队列之前也可以加个判断,看队列中是否有该商品id的操作,有就把读操作加入队列,没有就可以不进队列直接读数据库。
- 如果删除缓存失败,可以发送到mq里,通过ack机制,不断重试,保证缓存删除订阅mysql的binlog,来删除缓存
这种策略会有数据不一致的问题。
2.1 问题线程1删除缓存,还没来得及更新db,这时线程2读缓存,发现缓存为空,去db里拿到旧的数据,并把旧数据写入缓存,线程1更新db。这样缓存里就是旧的数据,db里是新的数据。
2.2 改进- 法一:通过2pc或者其他一致性算法保证强一致性。法二:延时双删。(延时双删一样有上面有可能缓存删除失败的问题)
总的有两种策略
- 更新db -> 删缓存删缓存 -> 更新db -> 删缓存
1比较好,因为1有的问题2也有,反而还多了一步
补充:
还有一种方式,可以参考CPU的cache方案和mysql的buffer pool,就是如果缓存有,只更新缓存,然后定时更新db,如果缓存没有就直接更新db。



