如何做网站推广 求指点,石河子建设局网站,电子商务网站的建站流程,全屋定制怎么样做网站背景#xff1a;为了提高查询效率#xff0c;一般会用redis作为缓存。客户端查询数据时#xff0c;如果能直接命中缓存#xff0c;就不用再去查数据库#xff0c;从而减轻数据库的压力#xff0c;而且redis是基于内存的数据库#xff0c;读取速度比数据库要快很多。 更新… 背景为了提高查询效率一般会用redis作为缓存。客户端查询数据时如果能直接命中缓存就不用再去查数据库从而减轻数据库的压力而且redis是基于内存的数据库读取速度比数据库要快很多。 更新数据库更新缓存
由于引入了缓存那么在数据更新时不仅要更新数据库而且要更新缓存这两个更新操作存在前后的问题
先更新数据库再更新缓存先更新缓存再更新数据库
先更新数据库再更新缓存
会存在并发问题。
举个例子比如「请求 A 」和「请求 B 」两个请求同时更新「同一条」数据则可能出现这样的顺序 请求A先将数据库更新为1然后因为网络原因缓存更新延迟了在这之间请求B将数据库更新为2并且把缓存更新为2了之后缓存更新为1才更新成功那么此时数据库中数据是2缓存中数据为1出现了数据不一致现象。
先更新缓存再更新数据库
那换成「先更新缓存再更新数据库」这个方案还会有问题吗
依然还是存在并发的问题。
假设「请求 A 」和「请求 B 」两个请求同时更新「同一条」数据则可能出现这样的顺序 A 请求先将缓存的数据更新为 1然后在更新数据库前B 请求来了 将缓存的数据更新为 2紧接着把数据库更新为 2然后 A 请求将数据库的数据更新为 1。
此时数据库中的数据是 1而缓存中的数据却是 2出现了缓存和数据库中的数据不一致的现象。
所以无论是「先更新数据库再更新缓存」还是「先更新缓存再更新数据库」这两个方案都存在并发问题当两个请求并发更新同一条数据的时候可能会出现缓存和数据库中的数据不一致的现象。 更新数据库删除缓存
在更新数据时不更新缓存而是删除缓存中的数据。然后到读取数据时发现缓存中没了数据之后再从数据库中读取数据更新到缓存中。这个策略叫 Cache Aside 策略中文是叫旁路缓存策略。
该策略又可以细分为「读策略」和「写策略」。 写策略的步骤
更新数据库中的数据删除缓存中的数据。
读策略的步骤
如果读取的数据命中了缓存则直接返回数据如果读取的数据没有命中缓存则从数据库中读取数据然后将数据写入到缓存并且返回给用户。
先删除缓存再更新数据库
假设某个用户的年龄是 20请求 A 要更新用户年龄为 21所以它会删除缓存中的内容。这时另一个请求 B 要读取这个用户的年龄它查询缓存发现未命中后会从数据库中读取到年龄为 20并且写入到缓存中然后请求 A 继续更改数据库将用户的年龄更新为 21。 最终该用户年龄在缓存中是 20旧值在数据库中是 21新值缓存和数据库的数据不一致。
可以看到先删除缓存再更新数据库在「读 写」并发的时候还是会出现缓存和数据库的数据不一致的问题。
延迟双删
针对这个问题可以使用延迟双删
延迟双删实现的伪代码如下
#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)加了个睡眠时间主要是为了确保请求 A 在睡眠的时候请求 B 能够在这这一段时间完成「从数据库读取数据再把缺失的缓存写入缓存」的操作然后请求 A 睡眠完再删除缓存。
所以请求 A 的睡眠时间就需要大于请求 B 「从数据库读取数据 写入缓存」的时间。
先更新数据库再删除缓存
假如某个用户数据在缓存中不存在请求 A 读取数据时从数据库中查询到年龄为 20在未写入缓存中时另一个请求 B 更新数据。它更新数据库中的年龄为 21并且清空缓存。这时请求 A 把从数据库中读到的年龄为 20 的数据写入到缓存中。 最终该用户年龄在缓存中是 20旧值在数据库中是 21新值缓存和数据库数据不一致。
从上面的理论上分析先更新数据库再删除缓存也是会出现数据不一致性的问题但是在实际中这个问题出现的概率并不高。
因为缓存的写入通常要远远快于数据库的写入所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存请求 A 才更新完缓存的情况。
而一旦请求 A 早于请求 B 删除缓存之前更新了缓存那么接下来的请求就会因为缓存不命中而从数据库中重新读取数据所以不会出现这种不一致的情况。
所以「先更新数据库 再删除缓存」的方案是可以保证数据一致性的。
为了确保万无一失还可以给缓存数据加上了「过期时间」就算在这期间存在缓存数据不一致有过期时间来兜底这样也能达到最终一致。
问题
「先更新数据库 再删除缓存」其实是两个操作前面的所有分析都是建立在这两个操作都能同时执行成功但是删除缓存第二个操作的时候失败了导致缓存中的数据是旧值。 怎么解决
重试机制
我们可以引入消息队列将第二个操作删除缓存要操作的数据加入到消息队列由消费者来操作数据。
如果应用删除缓存失败可以从消息队列中重新读取数据然后再次删除缓存这个就是重试机制。当然如果重试超过的一定次数还是没有成功我们就需要向业务层发送报错信息了。如果删除缓存成功就要把数据从消息队列中移除避免重复操作否则就继续重试。 可能疑惑的点
为什么是删除缓存而不是更新缓存呢
删除一个数据相比更新一个数据更加轻量级出问题的概率更小。在实际业务中缓存的数据可能不是直接来自数据库表也许来自多张底层数据表的聚合。
比如商品详情信息在底层可能会关联商品表、价格表、库存表等如果更新了一个价格字段那么就要更新整个数据库还要关联的去查询和汇总各个周边业务系统的数据这个操作会非常耗时。 从另外一个角度不是所有的缓存数据都是频繁访问的更新后的缓存可能会长时间不被访问所以说从计算资源和整体性能的考虑更新的时候删除缓存等到下次查询命中再填充缓存是一个更好的方案。
系统设计中有一个思想叫 Lazy Loading适用于那些加载代价大的操作删除缓存而不是更新缓存就是懒加载思想的一个应用。