从多线程到分布式

项目实践(六)缓存考虑

2023-08-21  本文已影响0人  吟游雪人

说到缓存,你应该不陌生。相对于磁盘操作,基于内存的缓存对耗时敏感的高并发应用来说,在性能方面的提升是非常明显的。同样是 1MB 的数据读取,从磁盘读取的耗时,比从内存读取的耗时相差近 100 倍,这也是为什么业界常说“处理高并发的三板斧是缓存、降级和限流”了。使用缓存虽然能够给我们带来诸多性能上的收益,但存在一个问题是缓存的资源成本非常高。因此,在 IM 系统中对于缓存的使用,就需要我们左右互搏地在“缓存命中率”和“缓存使用量”两大指标间不断均衡。

更新缓存的模式
1.缓存穿透到下一层,也就是回源更新
2.定时过期,也相当于主动穿透回源
缓存穿透 等于 回源更新

主从模式
主从模式是最常见的、使用最多的缓存应用模式。但是主从模式在某些突发流量的场景下会存在一些问题,就比如刚刚提到的“长文章流量热点”问题。我们对某篇长文章的唯一 ID 来进行哈希,在主从模式下,一篇文章只会映射到一个从库节点上。虽然能够通过增加从库副本数来提升服务端对一篇文章的读取能力,但由于文章大小比较大,即使是多从库副本,对于千兆网卡的从库实例机器来说,带宽层面也很难抗住这个热点。举个例子,单台机器 120MB 带宽,对于 1MB 大小的文章来说,如果 QPS 到 1000 的话,至少需要 8 个实例才可以抗住。
多从库副本是对主库数据的完整拷贝,从成本上考虑也是非常不划算的。除了带宽问题,对于某些 QPS 很高的资源请求来说,如果采用的是单主单从结构,一旦从库宕机,瞬间会有大量请求直接穿透到 DB 存储层,可能直接会导致资源不可用。

L1+ 主从的多层模式(L1缓存分组冗余热数据)

L1 缓存作为最前端的缓存层,在用户请求的时候,会先从 L1 缓存进行查询。如果 L1 缓存中没有,再从主从缓存里查询,查询到的结果也会回种一份到 L1 缓存中。
与主从缓存模式不一样的地方是:L1 缓存有分组的概念,一组 L1 可以有多个节点,每一组 L1 缓存都是一份全量的热数据(不是全量数据,是热数据),一个系统可以提供多组 L1 缓存,同一个数据的请求会轮流落到每一组 L1 里面。

比如同一个文章 ID,第一次请求会落到第一组 L1 缓存,第二次请求可能就落到第二组 L1 缓存。通过穿透后(穿透到主从缓存,而不是DB)的回种,最后每一组 L1 缓存,都会缓存到同一篇文章。通过这种方式,同一篇文章就有多个 L1 缓存节点来抗读取的请求量了。

L1 缓存一般采用 LRU(Least Recently Used)方式进行淘汰,这样既能减少 L1 缓存的内存使用量,也能保证热点数据不会被淘汰掉。并且,采用 L1+ 主从的双层模式,即使有某一层节点出现宕机的情况,也不会导致请求都穿透到后端存储上,导致资源出现问题。

L1的容量一般比从库容量小很多,但是会冗余多组,通过这种方式来承担极热点数据的访问,带宽上由于冗余多组来随机访问,所以带宽上自然相当于扩大了,另外由于容量都很小,也比扩从库成本上要更省。

L1缓存一般也是和主从缓存一样,采用中央缓存如memcached或者redis,只是在hash规则上和主从缓存有区别。实现上并不复杂,其实就是二次哈希的过程,比如将原来哈希到slave1的请求再采用round robin轮流打到多组L1上,这样就实现流量分散了。

image.png
如果热点数据都被 L1 缓存层拦截命中,会导致主从缓存层相应的这个热点数据,由于长时间得不到读取而被 LRU 淘汰掉。这样,如果下线 L1 缓存,还是会有不少的请求直接穿透到 DB 存储层。那么有没有办法,能够让主从缓存在有 L1 缓存层的情况下,依旧能保持数据热度?
可以考虑把master也加入到L1缓存层中,这样能保持数据热度。

本地缓存 +L1+ 主从的多层模式

对于大部分请求量较大的应用来说,应用层机器的部署一般不会太少。如果我们的应用服务器本身也能够承担一部分数据缓存的工作,就能充分利用应用层机器的带宽和极少的内存,来低成本地解决带宽问题了。


image.png

本地缓存一般位于应用服务器的部署机器上,使用应用服务器本身的少量内存。它是应用层获取数据的第一道缓存,应用层获取数据时先访问本地缓存,如果未命中,再通过远程从 L1 缓存层获取,最终获取到的数据再回种到本地缓存中。通过增加本地缓存,依托应用服务器的多部署节点,基本就能完全解决热点数据带宽的问题。而且,相比较从远程 L1 缓存获取数据,本地缓存离应用和用户设备更近,性能上也会更好一些。
但是使用本地缓存有一个需要考虑的问题,那就是数据的一致性问题。还是以“长文章”为例。我们的服务端可能会随时接收到用户需要修改文章内容的请求,这个时候,对于本地缓存来说,由于应用服务器的部署机器随着扩缩容的改变,其数量不一定是固定的,所以修改后的数据如何同步到本地缓存中,就是一个比较复杂和麻烦的事情了。要解决本地缓存一致性问题,业界比较折中的方式是:对本地缓存采用“短过期时间”的方式,来平衡本地缓存命中率和数据更新一致性的问题。比如说,针对“长文章”的本地缓存,我们可以采用 5 秒过期的策略,淘汰后再从中央缓存获取新的数据。(因为一定要回源才能更新数据,所以采用这种短时自动回源的方式)这种方式对于大部分业务场景来说,在产品层面上也是都能接受的。本地缓存的最大问题是可能造成本地服务器的I/O问题

上一篇 下一篇

猜你喜欢

热点阅读