当前位置: 首页 > 科技观察

千万级并发!如何设计多级缓存系统?

时间:2023-03-15 15:15:12 科技观察

什么是多级缓存系统?它有什么用?我们如何设计多级缓存系统?图片来自Pexels所谓多级缓存系统,是指在一个系统的不同架构层级进行数据缓存,以提高访问效率。我们都知道缓存系统会面临很多问题,比如缓存击穿、缓存穿透、缓存雪崩、缓存热点等等。那么,多级缓存系统有哪些问题呢?有以下几种:缓存热点:多级缓存系统多用于高并发场景,需要解决热点key的问题。如何检测热点键?分布式缓存之前的数据一致性问题。缓存过期:缓存数据可以分为两类,过期缓存和未过期缓存?怎么设计,过期缓存怎么设计?在此之前,我们先来看一个简单的多级缓存系统的架构图:整个多级缓存系统分为三层:应用层Nginx缓存分布式Redis缓存集群Tomcat堆缓存整个架构过程是如下:当收到请求时,会先分发到Nginx集群中,这里可以使用Nginx负载均衡算法分发到某台机器上,可以使用轮询来降低负载,也可以使用一致性Hash算法用于提高缓存命中率。当Nginx层没有缓存数据时,会继续向下请求,在分布式缓存集群中查找数据。如果缓存命中,则直接返回(并写入Nginx应用缓存)。如果没有命中,则返回到Tomcat集群。查询堆内缓存。如果在分布式缓存中查询不到数据,就会去Tomcat集群去堆内缓存中查询。如果查询成功,则直接返回(并写入子redis主集群)。如果查询失败,则请求数据库;堆内缓存。如果以上缓存都没有命中,则直接请求数据库,返回结果,同步数据到分布式缓存。在简单了解了多级缓存的基本架构之后,我们就应该思考如何解决上面提到的一系列问题。缓存热点缓存热点是一个很常见的问题,比如“某明星官宣结婚”等,可能会引起大量的访问请求。最麻烦也最容易被忽视的事情之一就是如何检测热点键。在缓存系统中,除了一些常用的热键外,在一些特殊场合也会有大量的热键。我们怎么知道?有以下策略:数据研究。您可以分析历史数据并预测不同场合的热键。虽然这种方法不能使缓存命中率达到100%,但它是最简单且性价比最高的解决方案。实时计算。可以利用现有的实时计算框架,如Storm、SparkStreaming、Flink等框架,统计一段时间内的请求数,从而确定热点key。也可以自己实现定时任务统计请求量。这里我们着重介绍第二种解决方案。对于热键问题,当缓存系统中没有缓存时,需要从数据库中读取数据。当大量的请求过来时,一个请求获取锁去请求数据库,其他的阻塞,然后全部访问缓存,这可能会导致一个服务器因为hold不住而崩溃。比如一个普通服务器的并发能力在5W左右,生成热点key时达到10W甚至20W,服务器肯定会宕机。所以我们还要做在发现热点Key后如何自动负载均衡。结合以上问题,我们重新设计了架构,如下图所示:我们将整个应用架构分为:应用层分布式缓存系统层数据层在应用层,我们使用Nginx集群,连接实时计算链接,并通过FlumeLog监控Nginx,将数据传输到Kafka集群,然后Flink集群消费数据进行统计。如果统计结果是hotkey,则将数据写入Zookeeper节点,应用系统监听Znode节点,读取hotkey数据,从数据库中加载到缓存中,实现负载均衡。实际上,对于应用系统中的每一个服务器,都需要一层保护机制,比如限流、熔断等。这样做的目的是为了防止单机请求量过大,导致服务器负载过高,导致服务器宕机。或者大量请求访问数据库。简单的思路就是为每台服务器设计一个阈值。当请求量超过这个值时,会直接将用户返回到空白页面或提示用户刷新,过几秒后重新访问。数据一致性数据一致性问题主要体现在更新缓存时如何更新缓存,保证数据库、缓存、各层缓存层之间的一致性。对于缓存更新问题,是先写缓存还是先写数据库,这里省略一些话。上一篇文章对此进行了介绍,有兴趣的读者可以阅读。在单层缓存系统中,我们可以先删除缓存,再更新数据库来解决它的数据一致性问题。多级缓存呢?如果我们使用这种方案,我们需要考虑如果我们先删除缓存,那么我们需要逐层进行删除操作,这一系列操作给系统带来的耗时也是相当可观的。如果我们使用分布式事务机制,我们需要考虑是否将写缓存放入事务中,因为我们需要通过网络通信来更新分布式缓存,大量的请求会造成网络抖动甚至阻塞,增加系统的延迟,导致系统短时间不可用。如果我们不把写缓存操作放到事务中,可能会造成短时间内的数据不一致。这就是分布式系统的CAP理论。我们无法同时实现高可用性和一致性。那么我们应该如何选择呢?这里我们选择保证系统的可用性。就秒杀系统而言,短期的不一致问题对用户体验影响不大(当然,这里不涉及支付系统)。可用性对用户来说非常重要。一个活动可能会在很短的时间内结束,用户需要在这段时间内抢到自己喜欢的商品,所以易用性更重要(这个需要根据具体场景来权衡)。在保证系统可用性的基础上,如何实现呢?如果实时性要求不是很高,我们可以采用全量+增量同步的方式。首先,我们可以根据预期的热点key预热系统的缓存,将数据完全同步到缓存系统。那么当缓存需要更新的时候,我们就可以使用增量同步来更新缓存。比如我们可以使用阿里运河框架同步Binlog来同步数据。缓存过期缓存系统中的所有数据。根据数据的使用频率和场景,我们可以将它们分为过期键和非过期键。那么我们如何消除过期的缓存呢?下面有几种常用的解决方案:FIFO:使用FIFO算法来淘汰过期的缓存。LFU:使用LFU算法驱逐过期的缓存。LRU:使用LRU算法淘汰过期的缓存。以上解决方案是缓存达到最大缓存大小时的淘汰策略。如果没有达到最大缓存大小,我们有以下方法:定时删除策略:设置一个定时任务,在指定时间内检查并删除过期的key。周期删除策略:该策略需要设置删除周期和时长。如何设置需要根据具体场合来计算。懒删除策略:使用时检查是否过期,如果过期直接更新缓存,否则直接返回。