前言: 在寒假找实习的过程中,科大讯飞技术面试官问我:
关于redis的内存数据和数据库里面的数据如何保持数据的一致性
当我们在做数据库与缓存数据同步时,究竟更新缓存,还是删除缓存,究竟是先操作数据库, 还是操作缓存?
1. 数据缓存
-
为何要使用数据缓存?
在一些应用场景,某些数据访问频率高, 更新频率低,为了减少对数据库的访问频率以及加快访问速度,将这些热点数据缓存到内存当中。 -
哪类数据适合数据缓存?
一般而言,对于经常变化的数据,不适合做缓存,一方面会增加系统的复杂性(缓存的更新, 缓存脏数据),另一方面给系统带来一定的不稳定性(缓存系统的维护)
- 缓存的利与弊?
上缓存的优点:
1. 缩短服务响应时间
2. 增大系统吞吐量
3. 减轻数据库压力
弊端:
1. 系统复杂度增加
2. 系统分布式存在很多坑
3. 缓存和数据库一致性的问题
2.如何保证缓存和数据库一致性
观点一:不更新缓存,而是删除缓存,由下个请求取缓存,如果不存在在读取数据库,写入缓存
由此引发出来的问题:
先删除缓存,然后再更新数据库, 还是先更新数据库,再删除缓存呢?
先来看看大佬们怎么说。
《【58沈剑架构系列】缓存架构设计细节二三事》58沈剑:
对于一个不能保证事务性的操作,一定涉及“哪个任务先做,哪个任务后做”的问题,解决这个问题的方向是:如果出现不一致,谁先做对业务的影响较小,就谁先执行。
假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss。
假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。
沈剑老师说的没有问题,不过没完全考虑好并发请求时的数据脏读问题,让我们再来看看孤独烟老师《分布式之数据库和缓存双写一致性方案解析》:
先删缓存,再更新数据库
该方案会导致请求数据不一致
同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:
(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。
所以先删缓存,再更新数据库并不是一劳永逸的解决方案,再看看先更新数据库,再删缓存这种方案怎么样?
先更新数据库,再删缓存这种情况不存在并发问题么?
不是的。假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生
(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存
ok,如果发生上述情况,确实是会发生脏数据。
然而,发生这种情况的概率又有多少呢?
发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。
所以,如果你想实现基础的缓存数据库双写一致的逻辑,那么在大多数情况下,在不想做过多设计,增加太大工作量的情况下,请先更新数据库,再删缓存!