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

您的系统是否存在热点缓存问题?如何为系统设计缓存架构?

时间:2023-03-21 11:03:01 科技观察

这篇文章,我将从程序员的角度告诉大家,我的后台架构应该如何抵御一天3个热点的巨大流量涌入!(1)为什么要使用缓存集群其实在使用缓存集群的时候,最怕的就是hotkey和largevalue这两种情况,那么什么是hotkey和largevalue呢?热键简单的说就是你的缓存集群中的某个键被几万甚至几十万的并发请求瞬间淹没。较大的值意味着您的某个键对应的值可能具有GB级别的大小,从而导致在查询该值时出现与网络相关的故障。我们先来看下图。假设你手边有一个系统,它部署在一个集群中,然后在它后面有一组缓存集群。这个集群不管你用redis集群,memcached,还是公司自研的缓存集群,都可以。那么,这个系统使用缓存集群有什么用呢?很简单,把一些变化不大的数据放在缓存中,然后当用户查询大量变化不大的数据时,直接从缓存中去不行吗?缓存集群并发能力很强,读缓存性能很高。举个例子,假设你每秒有20000个请求,但是其中90%是读请求,那么每秒18000个请求都是在读一些变化不大的数据,而不是写数据。这个时候你把这些数据都放到数据库里,然后每秒发送20000个请求去读写数据库上的数据,你觉得合适吗?当然不合适。如果你要用数据库承载每秒20000个请求,那不好意思,你很可能要搞分库分表+读写分离。比如你打分3个主库,每秒承载2000个写请求,然后每个主库有3个从库,总共9个从库承载每秒18000个读请求。在这种情况下,你可能一共需要12台高配置的数据库服务器,非常昂贵,非常昂贵,非常不合适。看看下面的图片就可以了解这种情况。因此,我们可以将平时不会变化的数据放在缓存集群中。缓存集群可以使用2个master和2个slave。主节点用于写入缓存,从节点用于读取缓存。以缓存集群的性能,两个从节点可以承载每秒18000个的大量读请求,然后三个数据库主库可以承载每秒2000个写请求和少量的其他读请求。这样你消费的机器瞬间变成了4台缓存机+3台数据库机=7台机器。相比之前的12台机器,是不是减少了很多资源开销?是的,缓存其实是系统架构中非常重要的一部分。很多时候,对于很少变化但有大量高并发读的数据,使用缓存集群来抵抗高并发读是非常合适的。下面我们通过这张图来感受一下这个过程。需要注意的是,这里所有的机器数和并发请求量都只是示例。你可以主要理解这个意思。目的是给一些不熟悉缓存相关技术的同学做一些背景说明,让同学们理解什么是在系统中使用缓存集群来承载读请求。(2)20万用户同时访问一个热点缓存。背景已经给大家解释清楚了。下面给大家说说今天要讨论的重点问题:热点缓存。我们假设现在有10个缓存节点来抵抗大量的读请求。一般情况下,读请求应该均匀的落在这10个缓存节点上吧!这10个缓存节点每秒承载10000个请求,差不多。那我们再做一个假设。一个节点承载20000个请求是极限,所以一般你限制一个节点正常承载10000个请求就可以了,留一点buffer。那么,所谓的热点缓存问题是什么意思呢?这很简单。突然,由于某种莫名其妙的原因,大量用户访问了同一条缓存数据。比如,如果同一天爆出三个热点新闻,会不会导致短时间内每秒有几十万用户查看这些热点新闻?假设3条新闻是3个缓存,对应3个缓存键,这些键都存在于一台缓存机上。那么某条消息一发布,可能瞬间就有几十万个请求涌向那台机器。这时候会发生什么?让我们看看下图来感受一下这种绝望的感觉吧。显然,我们刚才假设的是一个缓存从节点每秒最多可以处理20000个请求。当然,实际缓存单机承载5万到10万个读请求也是有可能的。这是一个假设。结果每秒突然有20万个请求冲到这台机器上,会发生什么?很简单,上图中20万个请求所指向的缓存机器就会超负荷崩溃。那么如果缓存集群开始出现机器停机会怎样呢?这时候读请求发现数据读不出来,会从数据库中取出原来的数据,然后放到剩下的其他缓存机器中。但随之而来的每秒20万个请求又会压垮其他缓存机器。以此类推,最终会导致整个缓存集群崩溃,导致整个系统宕机。让我们看看下图,再次感受一下这恐怖的一幕。(3)基于流计算技术的缓存热点自动发现其实这里的关键点在于对于这种热点缓存,你的系统需要能够在突然出现热点缓存的时候直接发现他,然后瞬间实现毫秒级自动负载均衡。那么我们先来说说,如何自动发现热缓存问题呢?首先你要知道,当有缓存热点的时候,你每秒的并发量肯定是非常高的。它可能是每秒数十万甚至数百万个请求。这个有可能。因此,此时完全可以统计大数据领域基于流计算技术的实时数据访问次数,如Storm、SparkStreaming、Flink。一旦在统计实时数据访问次数的过程中,比如某条数据在一秒内突然被访问超过1000次,则立即判定这条数据为热点数据,并找到的热点数据可以写入到zookeeper等回车。当然你的系统如何判断热点数据可以根据你自己的业务和经验。看看下面的图片,看看整个过程是如何工作的。这里肯定有人会问,你的流计算系统在统计数据访问次数的时候,会不会出现单机每秒请求几十万次的问题?答案是:不会。因为有了流计算技术,尤其是storm这样的系统,他可以请求同一份数据,先分散在很多机器上进行本地计算,最后将本地计算结果聚合到一台机器上进行全局汇总.所以几十万个请求可以分布在比如100台机器上,每台机器统计上千个请求这个数据。然后可以将100个本地计算的结果汇总到一台机器上进行全局计算,这样就不会出现基于流计算技术的统计热点问题。(4)热点缓存自动加载为JVM本地缓存。我们自己的系统可以监控zookeeper指定的热点缓存对应的znode。若是有异动,他第一时间就能察觉到。这时,系统层可以立即从数据库中加载相关的缓存数据,然后直接放到系统内部的本地缓存中。这个本地缓存,可以用ehcache或者hashmap,都可以,看你的业务需要。这里主要讲的是把缓存集群中的集中缓存直接变成每个系统自己的本地缓存。每个系统不可能在本地缓存太多数据。因为这个普通系统的单实例部署机器可能是4核8G的机器,留给本地缓存的空间已经很少了,所以存放这种热点数据的本地缓存是最合适的,恰到好处。假设你的系统层集群已经部署了100台机器,那么,这个时候,你的100台机器瞬间在本地就有了一份热点缓存。那么接下来对热点缓存的读操作直接从系统本地缓存中读取返回,不经过缓存集群。这样的话,就不可能让每秒20万个读请求到达缓存机器去读取一台机器上的热点缓存,而是变成100台机器每台承载几千个请求,那么那几千个请求就直接返回数据来自机器的本地缓存,这没问题。我们再画一张图,一起来看看这个过程:(5)限流熔断保护另外,在每个系统内部,都应该增加一个针对热点数据访问的限流熔断保护措施。在每个系统实例内部,都可以添加熔断器保护机制。假设缓存集群每秒最多可以承载40,000个读取请求,那么您总共有100个系统实例。你应该自己限制它。每个系统实例每秒可以为缓存集群请求不超过400次读取操作。一旦超过,就可以吹掉。缓存集群的请求不会被允许,直接返回空白信息,用户稍后再试。刷新页面等。通过直接在系统层加入限流熔断保护措施,可以很好的保护后续的缓存集群、数据库集群等不被杀掉。这里再放一张图,一起来看看:(6)本文总结了是否在系统中实现了这种复杂的缓存热点优化架构?这个要看你自己的系统有没有这样的场景。如果你的系统存在热点缓存问题,那么就需要实现类似本文的复杂热点缓存支持架构。但如果不是,那就不要过度设计,事实上,你的系统可能根本不需要这么复杂的架构。