美文网首页
缓存更新策略

缓存更新策略

作者: 丑人林宗己 | 来源:发表于2018-06-23 21:03 被阅读1760次

    随着移动互联网的野蛮疯长,各种互联网技术层出不穷,但是不管各种技术框架如何滋长,永远无法绕过的一个坎,那就是数据库性能。

    之前讨论过一个问题,计算机为了在性能上有一个倍数式的增长,引入了缓存设计,同时也间接引入了并发问题。抛开让人闻风丧胆的并发问题,缓存的引入,本质的问题是磁盘虽然硬件成本低,但是性能低效,而缓存则相反,成本高,但是性能极好,所以在二者权衡之下,选择折中的方案,两者相结合得最大性价比。系统架构同样也面临着性能问题,响应问题。

    数据库性能瓶颈

    为什么说数据库性能是瓶颈呢?

    因为请求可以分发到不同的tomcat上,tomcat可以瞬间部署N份实例出来,数据库你能怎么处理?实际开发过程中,我们常常会讨论一个问题:Mysql连接数配置?Mysql连接数池化?

    可见,连接数是一种非常稀缺的资源!

    主从同步,读写分离

    那么怎么提升连接数,更好的利用这些资源?根据大多数互联网业务都具备读多写少的特性,提出了读写分离,主从配置的技术方案。通过主从同步技术分离出大量的只读库,读写分离技术将大量的读请求分摊到其他从库上,主库只做写操作,这样子勉强可以解决小问题,但是一个主库就可以抗住所有的写吗?业务上来了,一个主库的写根本无法解决问题,这算得上是治标不治本的办法!

    分库分表

    根据业务特性做分库,将多个库分成不同的数据库,将一个大表的数据分摊到多个表中。从此,分布式事务常伴吾身……

    分布式数据库

    分布式数据库技术算的上是互联网公司的一大福音,虽然技术难度较大,但是至少是一个很好的解决办法。它又做了什么?它将数据分摊到多个库上。从单库的数据迁移到分布式数据库上,个中艰难,不足为外人道,网上各种酷炫解决办法,但是很多时候却受限于业务,受限于人手……

    数据库的性能瓶颈就是不断将数据分摊出去,分摊到不同的库,分摊到不同的表上……可以访问数据库的性能还是不足以让人激动,聪明的架构师们很快就想到在中间层加入一层缓存!

    架构演变.png

    这个缓存层给我们带来了什么作用?

    由于redis的容量一般都只有几个G(那些大土豪请忽略),如此宝贵的缓存当然是放最合适的数据!行业上有个说法是:冷热数据分离,可以考虑把热数据放置在redis中。当用户发起请求时,优先访问redis缓存层,由于缓存访问速度高效的特性,响应速度更好,体验更好,且多数请求不会直接流到数据层。

    任何事都是有必有弊的,缓存亦是一个道理。

    计算机加入高速缓存层后,出现了并发问题,系统架构中加入缓存后,同样出现了比如:缓存更新问题,缓存穿透,缓存雪崩,缓存一致性等等的一系列问题。缓存的数据会失效,所以需要一个更新策略,来保证缓存中的数据与数据库中的数据一致。

    缓存更新策略

    今天先了解一些缓存更新策略,以及为什么会有这种一种策略,是为了解决什么样的问题,每种策略有何优缺点?

    更新缓存的设计模式有四种:Cache aside, Read through, Write through, Write behind caching

    Cache aside

    • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
    • 命中:应用程序从cache中取数据,取到后返回。
    • 更新:先把数据存到数据库中,成功后,再让缓存失效。

    从正常的业务逻辑在跑的时候,应该是不会有问题的,但是某些特殊的情况下可能会出现问题。如下图:

    image.png

    从上图大概可以非常清晰明了的看清楚问题出在哪里,这种并发情况下是有可能出现某个key在某段时间内的数据时不一致的,这个问题一般有两种处理方式。

    • 加锁。锁,意味着要去解决并发问题,那么也就意味着并发的处理会被串行的处理,性能自然会略低。
    • 对key加上过期时间,把控好这个过期时间,不过业务上本身是具备容忍这个时间内的数据不一致的问题方可。
    • 降低出现这个问题的概率

    Read/Write Through Pattern

    从设计上看,Cache aside这个模式的缺点在于需要程序去维护两个不同的存储设备,(比如一个MySQL,一个Redis),硬编码的成本较大,而且容易出现编码失误,更好的方式是,提供特殊服务专门维护,将其与业务隔离开。

    • Read Through
      Read Through 是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

    • Write Through
      Write Through 在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

    image.png

    Write Behind Caching Pattern

    俗称write back,在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比,因为异步(比如消息队列),write back还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

    但是这个设计的最大致命问题在于数据的不强一致性,极可能造成数据的丢失。假如使用redis作为缓存数据库,最致命的问题在于redis并不能保证绝对不丢失数据,也就是redis的持久化能力(两种持久化都无法保证数据绝对丢失)不足,redis一旦挂了,可能造成数据丢失且无法恢复。

    image.png

    总结

    任何的架构都不是没有缺陷的,任何的技术都不是完美的。只有合适的技术选型,没有绝对完美的技术方案,架构师往往需要从业务上去做抉择,从技术团队资源上去做抉择,任何一种抉择都有一定的合理性,也都需要去填一些坑。前文提到“为什么会有这种一种策略,是为了解决什么样的问题,每种策略有何优缺点?”,当了解完三种缓存更新策略之后,是否可以明白它们出现的意义,它们要解决的业务场景,以及它们技术难点?除了三种多年来总结出来的最佳实践,是否可以根据业务指定其他更合适的业务场景?

    掌握一门技术,不是要去画地为牢,而是通过它让自己的技术视野更加广阔,做到举一反三,这样你才会在在技术的道路上继续砥砺前行,然后继续穷下去……

    引入缓存设计过程中,除了缓存更新的问题,还会面临很多问题,后续继续学习探索。

    相关文章

      网友评论

          本文标题:缓存更新策略

          本文链接:https://www.haomeiwen.com/subject/jleryftx.html