缓存再目前互联网乃至计算机当中都扮演着很重要也很关键的一部分,在高并发的场景下如何保证缓存与数据库的一致性是比较关键的。
目前我个人所理解对于缓存与数据库一致性的处理方案大致分为如下几种方案。
1.先更新缓存在更新数据库
2.先更新数据在更新缓存
3.先删除缓存在更新数据库
4.延时双删
那么我们逐一分析优劣。
1.先更新缓存在更新数据库
暂不考虑并发情况下的问题在更新缓存在更新数据库如果更新失败的情况下则会出现缓存不一致的情况,而这种情况发生时缓存无法回滚。会出现缓存不一致的情况
2.先更新数据在更新缓存
AB两个线程并发进行更新操作,如果在线程A更新age=15的时候出现了网络波动出现了网络波动或者数据库更新耗费了一点时间,导致线程B先更新了缓存age=16,而后线程A在更新了age=15。此时最后发起更新操作的线程B看到更新完后的age=15但是明明更新操作是16.依然会出现不一致的情况。
3.先删除缓存在更新数据库
线程A发起更新操作age=15,
1.先删除缓存,
2.然后更新DB age=14更改为15,
3.在删除缓存后线程B发起查询操作,
4.线程B查询缓存没有数据则查询DB,5.DB数据为线程A未更新数据age=14并且更新到缓存中,
6.线程A更新完DB后发现缓存有数据则不在插入。
此时线程A更新的age数据库中是15但是缓存中是14.存在不一致问题。
4.延时双删
1.先删除缓存,
2.然后更新DB age=14更改为15,
3.在删除缓存后线程B发起查询操作,
4.线程B查询缓存没有数据则查询DB,
5.线程B查询DB数据为age=14并且插入到缓存中,
6.线程A更新完DB后发起redis删除操作。
延时双删则是在先删除缓存在更新DB的基础上对更新完DB再次对redis进行删除,
如果对于并发要求不是特别高的情况,或者对于强一致性没有那么高要求情况下可以采用则可以采用。
因为假设在第5步线程B发生GC的情况导致STW,将旧数据在第6步线程A删除缓存后进行了缓存插入操作则依然会导致缓存不一致的情况。如果一定要完全一致可能要牺牲吞吐量从而保证串行化操作。
5.进阶版消息队列 (要求一致性比较高的场景)
将binlog日志采集发送到MQ队列里面,然后通过ACK机制确认处理这条更新消息,删除缓存,保证数据缓存一致性,避免了系统对中间件的依赖,减少对业务代码的入侵和解耦
关注我的公众号 LearnMoreDoMore 可以及时看到更多技术分享文章(不会该死的卖课广告)。