看过很多保持MYSQL 与redis保持一致性的文章都提到了延迟删除,其实脱离任何业务场景的设计都是不切实际的,所以我会本着一个通用的读写场景去分析为什么延迟删除大概率可以保证MYSQL与redis的最终一致。
通常的读写场景
通常在使用redis作为读写缓存时,我们采用的是cache aside pattern
的形式,这种形式的读写一般是这样,
1,读请求: 从缓存中获取不到数据时,从db中读取数据,然后再set到缓存里。
2,写请求: 修改db中的数据,然后删掉缓存。
这样的模式大概率是不会有问题的,但是会有极小的概率出现读请求中,可能会很长时间存在旧数据,来看一下这个例子, 下面的数字标明了每个步骤执行的顺序。
可以看到在并发环境下,如果读请求先从db中读取了旧数据,然后写请求再去执行修改db,删除缓存的操作,此时读请求再把读取出来的旧db数据 set到缓存中,那么后续的读请求便会会一直读取到旧数据的缓存,除非缓存过期。
延迟删除是如何解决db与缓存数据不一致的
其实无论是上述缓存模型,还是其他的读写缓存方式,在并发环境下,其实都有可能出现缓存旧数据的问题,其本质原因是,修改缓存的地方不是单协程进行的,多协程修改必然存在并发先后的问题。
解决上述不一致的方式是可以延迟写请求中,删除缓存的时间。先来看下读写请求的并发场景,
1,如果读请求发生在写请求前,那么写请求后续删除缓存,是不存在不一致问题的。
2,如果读请求发生在写请求后,那么后续读请求读取出来的数据就是新数据,也不会有问题。
3,如果读写同时发生,但是缓存还未过期,此时也不会有一致性问题。
4,如果读写同时发生,但是缓存过期,这个时候才有可能出现上面缓存中长时间存在旧数据的问题。
基于此,我们可以延迟写请求中删除缓存的时间,因为一般我们数据有段比较长的缓存时间,在最开始读请求更新缓存后,后续的读请求会比较长时间读取缓存中数据,我们让写请求等待(可以同步,也可以异步)最开始更新缓存的读请求结束,然后再去删除缓存,就避开了读写请求并发更新缓存的场景,这样,下次读请求就不会读取到旧db数据缓存,而是重新从db中获取新数据。
具体等待多长时间才进行缓存删除,需要根据业务读请求接口的处理时长自己决定。