昨天面试了一家公司,简历上我写了用redis做缓存,当面试官问到,当有用户修改了信息,怎么做到缓存的信息和数据库一致呢,当时或许是紧张或许是真不知道,还是脑子短路了,就没回答出来。
面试完和我舍友提到的时候,慢慢就想起以前看到过类似的解决方法:那就是当用户的信息有更新的时候就进行缓存和数据库双写,然后在具体的操作中,考虑延迟双删策略:即
- 删除缓存
- 更新数据库
- 再次删除缓存
就是说先删除缓存,再更新数据库,过几秒后再删除一下缓存,这就避免了并发产生的的脏数据,在这其中延伸出几个思考:
1、为什么选择删除缓存呢?
缓存中的数据可能不只是简单的一个字符串类型,还有可能是其他json串的。如果需要对其进行修改的话则需要进行序列化之后,再解析出其中的字段,把其修改之后再序列化,最后再更新到缓存之中。由此可知更新缓存的动作相比于直接删除缓存更加的复杂,页容易出错。
2、先写数据库还是先删缓存?
先写数据库:
因为数据库和缓存的 操作是两个不同的操作,所以没办法做到原子性,所以就有可能一个操作成功另外一个操作失败。
把删除缓存放到第二步,如果失败了,会出现一个情况,那就是数据库的数据已经更新,但是缓存还是旧数据,导致数据不一致的问题。
先删缓存
如果是先删缓存后写数据库的话,那么第二步的失败还是可以接受的,以为这样不会有脏数据,没什么影响,只需要重试就好。
第二个好处就是:缓存删除的失败概率比较低,一般只有网络问题或者是缓存服务器宕机才出现,所以先删除缓存还是很有保障的。
先删缓存后写数据库这种方式,也会存在一个问题。我们先捋一下使用缓存后读线程的过程:
- 查询缓存,如果缓存中有值,则直接返回
- 查询数据库
- 把数据库中的查询结果更新到缓存之后
所以对于一个读线程来说,虽然不会写数据库,但是会更新缓存,所以在并发情况下会存在数据不一致的情况。
也就是说,假如一个读线程,在读缓存的时候没查询值就是到数据库中查询。但是如果在查到结果之后,更新缓存之前,数据库被另一个线程进来更新了,但是前面那个读线程是完全不知道的,那么就是导致最终缓存会被更新成一个“旧值”覆盖掉了。
虽然发生的概率比较低,但是还是有很低的概率发生,因为读的操作是比写操作要耗时的。一旦这种情况发生,后果也是比较严重的,那就是缓存中的值一旦是错的,就会导致后续所有的从缓冲中查询到的结果都是错的,所以为了进一步解决这种问题,那就是过几秒钟之后再删除一次缓存,就是一开始提到的延迟双删。
3、有第二次删除,第一次还有意义吗?
如果没有第一次删除,只保留第二次删除那么这个流程就变成了:
- 更新数据库
- 更新缓存
这个方案一旦删除缓存失败了就会导致数据不一致的问题。
那如果延迟 双删的第二次删除不也一样有可能失败么?
的确,第二次删除也还是有失败的概率,但是比因为我们在延迟双删的方案中先做了一次删除,而延迟双删的第二次删除只为了 尝试解决 因为读写并发导致的不一致问题,或者说尽可能降低这种情况发生的概率。
如果没有第一次删除,只靠第二次删除,那么就不只是解决读写并发情况下不一致的问题了。即使没有并发,第二次只要删除失败,就会存在缓存不一致的问题,所以,第一次删除的目的就是降低不一致的发生概率。
总之,通过前后两次删除的设置,就可以降低不一致的概率了。
标签:缓存,双删,删除,数据库,Redis,更新,一致 From: https://www.cnblogs.com/ChenRicky/p/18031392