分布式系统的缓存设计你真的会了吗?

举报
JavaEdge 发表于 2021/06/04 00:01:00 2021/06/04
【摘要】 缓存不仅加快了IO,还可减少原始数据的计算工作。 缓存系统一般设计简单,功能单一,所以Redis吞吐量能是MySQL几倍~几十倍,对于互联网读多写少的高并发场景已不可或缺。 虽然简单好用,但是如果姿势不对,就会造成不必要的损失。 不要把Redis当数据库 很多人不仅把 redis 当缓存,更是把Redis当做数据库使用。很多因为Redis中数据消失导致业务逻辑错...

缓存不仅加快了IO,还可减少原始数据的计算工作。

缓存系统一般设计简单,功能单一,所以Redis吞吐量能是MySQL几倍~几十倍,对于互联网读多写少的高并发场景已不可或缺。

虽然简单好用,但是如果姿势不对,就会造成不必要的损失。

不要把Redis当数据库

很多人不仅把 redis 当缓存,更是把Redis当做数据库使用。很多因为Redis中数据消失导致业务逻辑错误,并且因为没有保留原始数据,业务都无法恢复。

虽然Redis有持久化功能,但这并不能以为它可作为高性能的KV数据库。
Redis处理请求很快,但那是因为在内存中,无法保存超过内存大小的数据。

因此,再将Redis用作缓存时,需要注意:

  • 客户端角度看,缓存数据的特点一定是有原始数据来源,且允许丢失,即使设置的缓存时间是1min,在30s时缓存数据因为某种原因消失了,也要能接受。当数据丢失后,需要从原始数据重新加载数据,永远不要认为缓存是绝对可靠的,也不要认为缓存不会删除没有过期的数据。
  • Redis服务端来看,缓存可以保存的数据量一定小于原始数据
    应该限制Redis内存使用量,即maxmemory参数
    根据数据特点,明确Redis数据淘汰策略

这些策略是Key范围+Key选择算法的搭配组合,范围有allkeys和volatile两种,算法有LRU、TTL和LFU三种。

如何选择合适的驱逐算法

算法角度

Redis 4.0以后推出的LFU比LRU更“实用”。试想一下,如果一个Key访问频率是1天一次,但正好在1秒前刚访问过,那么LRU可能不会选择优先淘汰这个Key,反而可能会淘汰一个5秒访问一次但最近2秒没有访问过的Key,而LFU算法不会有这个问题。而TTL会比较“头脑简单”一点,优先删除即将过期的Key,但有可能这个Key正在被大量访问。

Key范围角度

allkeys可确保即使Key没有TTL也能回收,如果使用的时候客户端总是“忘记”设置缓存的过期时间,那么可以考虑使用这系列算法。
而volatile会更稳妥,万一客户端把Redis当做了长效缓存,只是启动时候初始化一次缓存,那么一旦删除了此类没有TTL的数据,可能就会导致客户端出错。

缓存数据同步策略

实际开发中,如果修改了原始数据,考虑到缓存数据更新的及时性,我们可能会采用主动更新缓存的策略:

先更新缓存,再更新数据库

不可行。数据库设计复杂,压力集中,数据库因为超时等原因更新操作失败的可能性较大,此外还会涉及事务,很可能因为数据库更新失败,导致缓存和数据库的数据不一致。

先更新数据库,再更新缓存

不可行:

  • 如果线程A、B先后完成数据库更新,但更新缓存时却是B和A的顺序,那很可能会把旧数据更新到缓存中引起数据不一致
  • 我们不确定缓存中的数据是否会被访问,不一定要把所有数据都更新到缓存

先删除缓存,再更新数据库,访问时候按需加载数据到缓存

不可行。并发情况下,很可能删除缓存后还没来得及更新DB,就有另一个线程先读取旧值到缓存中去。并发量高时,概率还很大。

先更新DB,再删缓存,访问时按需加载数据至缓存

最好。虽然在极端情况下,这种策略也可能出现数据不一致,但概率很低,基本可以忽略。
一个极端的例子,更新数据的时间节点恰好是缓存失效了:

  • 【查询线程A】,先读取到了DB的旧值
  • 随后【更新线程B】操作DB完成更新,并删除缓存后
  • 【查询线程A】再把旧值加入缓存

更新DB后,删除缓存的操作可能失败,若失败则考虑把任务加入延迟队列进行延迟重试,确保数据可以删除,缓存可以及时更新。因为删除操作是幂等的,所以即使重复删,问题也不大,这也是删除比更新缓存好的一个原因。

所以对于缓存更新,推荐缓存中的数据不由数据更新操作主动触发,统一在需要使用的时候按需加载,数据更新后及时删除缓存中的数据即可。
并且要尽量设置合适的缓存过期时间,这样即便真的发生不一致,也可以在缓存过期后数据得到及时同步。

使用缓存系统的时候,要监控缓存系统的内存使用量、命中率、对象平均过期时间等重要指标,以便评估系统的有效性,并及时发现问题。

文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。

原文链接:javaedge.blog.csdn.net/article/details/115145814

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。