首页 > 其他分享 >如何应对热点数据的查询?

如何应对热点数据的查询?

时间:2025-01-10 21:12:44浏览次数:1  
标签:缓存 应对 数据 前置 查询 热点 节点

在前面的文章中,我们介绍了基于 Binlog 实现的全量缓存的读服务,以及如何实现一个低延迟、可扩展的同步架构。通过这两篇文章,可以构建出一个无毛刺且平均性能在 100ms 以内的读接口。对缓存进行分布式部署后,抗住秒级百万的 QPS 毫无压力。不管是在面试还是在实战中,关于“如何架构一个高性能的读服务”,相信大家都能够轻松应对。

但上述的“百万 QPS”有一个非常重要的限制条件,即这百万的 QPS 都是分属于不同用户的。我们先不讨论是否可能,试想一下如果这百万 QPS 都属于同一个用户,系统还扛得住吗?

如果采用前面讲的架构,必然抗扛不住的!因此本篇文章将站在一个全新的视角,分析此架构待改善的方向,并探寻新的架构优化方案来应对百万 QPS的流量。

一、为什么扛不住相同用户百万的流量

当百万的 QPS 属于不同用户时,因缓存是集群化的,所有到达业务后台的请求会根据一定路由规则(如 Hash),分散到请求缓存集群中的某一个节点,具体架构如下图所示:

假设一个节点最大能够支撑 10W QPS,我们只需要在集群中部署 10 台节点即可支持百万流量。但当百万 QPS 都属于同一用户时,即使缓存是集群化的,同一个用户的请求都会被路由至集群中的某一个节点,整体架构如图 2 所示:

即使此节点的机器配置非常好,当前能够支持住百万 QPS。但随着流量上涨,它也无法满足未来的流量诉求。
原因有 2 点:

1、单台机器无法无限升级;

2、缓存程序本身也是有性能上限的。

此类并发次数非常大、数据完全相同的请求称为热点查询。类似的场景还有热点写,区别在于它在请求后会对数据进行写入或变更,如何应对热点写,我们将在后面的文章进行详细介绍。

1.1 有哪些热点查询场景

除了在本篇文章开始介绍的,一个用户并发百万次请求产生热查询的案例外,在实际的日常生活中,会导致热查询的案例也非常多。

1、微博热点吃瓜事件,百万用户同一时间查询某条微博内容。对此条微博的查询就是热查询。

2、电商里的秒杀或者低价薅羊毛活动,为了第一时间抢到心仪的商品,成百上千万的用户会不断地刷新商品页面,等待秒杀倒计时。对此商品的查看就是热查询。

二、主从复制进行垂直扩容

虽然单机的机器配置和程序的性能是有上限的,但我们可以利用节点间的主从复制功能来进行节点间的扩容。主从复制开启后,一个主节点可以挂一至多个从。升级后的架构如下图所示:

在查询时,将应用内的缓存客户端开启主从随机读。此时,包含一个从的分片的并发能力,可以提升至原来的一倍。随着从节点的增加,单分片的并发性能会不断翻倍。这对于所有请求只会命中某一个固定单分片的热点查询能够很好地应对。

但此方案存在一个较大的问题,就是浪费资源。

主从复制除了有应对热点的功能,另外一个主要作用是为了高可用。当集群中的某一个主节点发生故障后,集群高可用模块会自动对该节点进行故障迁移,从该节点所属分片里选举一个从节点为主节点。为了高可用模块在故障转移时的逻辑能够简单清晰并做到统一,会将集群的从节点数量设置为相同数量。

相同从节点数量也带来了较大的资源浪费。为了应对热点查询,需要不断扩容从分片。但热点查询只会命中其中一个分片,这就导致所有其他分片的从节点全部浪费了。为了节约资源,可以对高可用模块进行改造,不强制所有分片的从节点必须相同,但这个代价也是非常高昂的。另外,热点查询很多时候是随时出现的,并不能提前预测,所以提前扩容某一个分片意义并不大。

总的来说,主从复制能够解决一定流量的热点查询且实施起来较简单。但不具备扩展性,在应对更大流量的热点时会有些吃力。

三、利用应用内的前置缓存

热点查询是对相同的数据进行不断重复查询的一种场景。特点是次数多,但需要存储的数据少,因为数据都是相同的。

针对此类业务特性,我们可以将热点数据前置缓存在应用程序内来应对热点查询,并解决前面提到的主从复制方案扩展性问题。使用了前置缓存的架构如下图所示:

应用内的缓存存储的均是热点数据。当应用扩容后,热点缓存的数量也随之增加。在采用了前置缓存后,在面对热查询时只需扩容应用即可。因为所有应用内均存储了所有的热点数据,且前端负载均衡器(如 Nginx 等)会将所有请求平均地分发到各应用中去。

使用应用内的前置缓存应对热点查询时,仍有以下几个问题需要重点关注。

3.1 首先是应用内缓存需要设置上限

应用所属宿主机的内存是有限的,且其内存还要支持业务应用使用。固在使用应用内的前置缓存时,必须设置容量的上限且设置容量满时的逐出策略。逐出策略可以是 LRU,将最少使用的缓存在容量满时清理掉,因为热点缓存需要存储的是访问次数多的数据。

此外,前置缓存也需要设置过期时间,毕竟太久无访问的缓存也肯定是非热点数据,所以可以及时清理掉,提前释放内存空间。

3.2 其次是根据业务对待延迟的问题

前置缓存的延迟问题的解决方案和前面文章的思路基本类似。要么采用定期刷新,要么采用主动刷新。

如果业务上可以容忍一定时间的延迟,可以在缓存数据上设置一个刷新时间即可。实现起来非常简单。

如果想要实时感知变化,可以采用 Binlog 的方式,在变更时主动刷新。但前置缓存的主动感知不能在前置缓存的应用里实现,因为应用代码也运行在此机器上,通过 MQ 感知变更会消耗非常多的 CPU 和内存资源。另外,前置缓存里数据很少,很多变更消息都会因不在前置缓存中而被忽略掉。为了实现前置缓存的更新,可以将前置缓存的数据异构一份出来用作判断,升级的方案如下图所示:

通过异构前置缓存用作判断,可以过滤出需要处理的数据,并实时调用对应机器更新即可。此方案实现起来较复杂且异构本来也导致了延迟,实际上大部分场景设置刷新时间即可满足。

3.3 再者要把控好瞬间的逃逸流量

应用初始化时,前置缓存是空的。假设在初始化时,瞬间出现热点查询,所有的热点请求都会逃逸到后端缓存里。可能这个瞬间热点就会把后端缓存打挂。

其次,如果前置缓存采用定期过期,在过期时若将数据清理掉,那么所有的请求都会逃逸至后端加载最新的缓存,也有可能把后端缓存打挂。这两种情况对应的流程图如下图所示:

对于这两种情况,可以对逃逸流量进行前置等待或使用历史数据的方案。不管是初始化还是数据过期,在从后端加载数据时,只允许一个请求逃逸。这样最大的逃逸流量为部署的应用总数,量级可控。架构如下图所示:

对于数据初始化为空时,其他非逃逸的请求可以等待前置缓存的数据并设置一个超时时间。对于数据过期需要更新时,并不主动清理掉数据。其他非逃逸请求使用历史脏数据,而逃逸的那一个请求负责把数据取回来并刷新前置缓存。

四、最后如何发现热点缓存并前置

除了需要应对热点缓存,另外一个重点就是如何发现热点缓存。对于发现热点有两个方式,一种是被动发现,另外一种是主动发现。

被动发现是借助前置缓存有容量上限实现的。在被动发现的方案里,读服务接受到的所有请求都会默认从前置缓存中获取数据,如不存在,则从缓存服务器进行加载。因为前置缓存的容量淘汰策略是 LRU,如果数据是热点,它的访问次数一定非常高,因此它一定会在前置缓存中。借助前置缓存的容量上限和淘汰策略,即实现了热点发现。

但此方式也存在一个问题——所有的请求都优先从前置缓存获取数据,并在未查询到时加载服务端数据到本地的前置缓存里,此方式也会把非热点数据存储至前置缓存里,导致非热点数据产生非必要的延迟性。

主动发现则需要借助一些外部计数工具来实现热点的发现。外部计数工具的思路大体比较类似,都是在一个集中的位置对于请求进行计数,并根据配置的阈值判断某请求是否会命中数据。对于判定为热点的数据,主动的推送至应用内的前置缓存即可。下图为在缓存服务器进行计数的架构方案:

采用主动发现的架构后,读服务接受到请求后仍然会默认的从前置缓存获取数据,如获取到即直接返回。如未获取到,会穿透去查询后端缓存的数据并直接返回。但穿透获取到的数据并不会写入本地前置缓存。数据是否为热点且是否要写入前置缓存,均由计数工具来决定。此方案很好地解决了因误判断带来的延迟问题。

五、降级兜底不可少

在采用了前置缓存并解决了上述四大类问题之后,当再次遇到百万级并发时,基本没什么疑难问题了。但这里还存在一个前置条件,即当热点查询发生时,你所部署的容器数量所能支撑的 QPS 要大于热点查询的 QPS。

但实际情况并非如此,你所部署的机器能够支持的 QPS 未必都能够大于当次的热点查询。对于可能出现的超预期流量,可以使用前置限流的策略进行应对。在系统上线前,对于开启了前置缓存的应用进行压测,得到单机最大的 QPS。根据压测值设置单机的限流阈值,阈值可以设置为压测值的一半或者更低。设置为压测阈值的一半或更低,是因为压测时应用 CPU 基本已达到 100%,为了保证线上应用能够正常运转,是不能让 CPU 达到 100% 的。架构如下图所示:

根据此方案可以看到,在做架构设计时,即使已经做了非常多的应对方案,最后的兜底降级还是必不可少,因为超出预期的事情说来就来。

文章(专栏)将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发。

标签:缓存,应对,数据,前置,查询,热点,节点
From: https://www.cnblogs.com/o-O-oO/p/18564374

相关文章

  • C api简单查询需要的几步
    Capi查询简单需要的几步1.准备sql语句2.绑定变量通过sqlstatement类bindout方法将查询出来的值与存储他们的变量绑定。这里绑定是为了以后从结果集中提取一行时只需要一行就可以完成将提取出来的变量赋值。3.执行execute在这一步将sql语句执行。在这里有几个重点mysql_qu......
  • 中考英语优秀范文-热点话题-传统文化-001 Chinese Treasures: Opera & Paper Art 国宝
    1写作要求坚定文化自信,弘扬中华优秀传统文化,做中华文明的传播者是新时代中国青少年的责任与使命。假如你是李华,近期,一批外国朋友要到你所在的学校参观访问,请用英语写一篇发言稿,向他们介绍中华优秀传统文化。内容包括:1欢迎朋友们的到来;2总体介绍中国传统文化种类多、历史悠......
  • 【Java开发】面对一个访问量比较高的API,我们应该如何去应对突然暴涨的流量呢?
    一、流量管理与限流1.流量限制和速率限制:例如,当请求频率超过预设阈值时,系统可以自动限制或拒绝额外的请求,从而保护后端服务免受过多请求的影响。通过API网关或负载均衡器进行配置,以控制每个用户或IP的请求速率。2.使用限流算法:令牌桶算法:适合应对瞬时突发流量,同时维持长期......
  • mysql查询表注释、字段、备注、大小、存储过程
    查询字段注释查询表注释字段注释SELECTa.TABLE_SCHEMA库名,a.table_name表名,a.table_comment表说明,b.COLUMN_NAME字段名,b.column_comment字段说明,b.column_type字段类型,b.column_key约束FROMinformation_schema.TABLESaLEFTJOINinformation_schema.COLUMNSb......
  • Java 实现 Elasticsearch 查询当前索引全部数据
    Java实现Elasticsearch查询当前索引全部数据需求背景通常情况Java实现查询Elasticsearch全部数据写在最后需求背景通常情况下,Elasticsearch为了提高查询效率,对于不指定分页查询条数的查询语句,默认会返回10条数据。那么这就会有一种情况,当你需要一次性返回Ela......
  • 修改 PbootCMS 中“未查询到任何数据”的提示文字
    在PbootCMS中,默认情况下,当查询没有返回任何数据时,系统会显示“未查询到任何数据”的提示信息。如果您希望修改这个提示文字,可以通过编辑 Paging.php 文件来实现。以下是详细的步骤:一、定位文件找到 Paging.php 文件打开PbootCMS的源码目录,找到 core/view/Paging.ph......
  • 卷王公开查询设置(1.6版本)
    1、公开查询设置页面说明创建的流程为:问卷->设置->公开查询设置->新增查询页面页面标题,设置查询页面的标题页面查询描述信息,设置查询页面的描述查询条件,点击设置可以选择多个查询条件,必须满足所有的查询条件才能输出结果查询结果,可以控制每一个字段的权限,目前支持三......
  • WiFi+4G摄像头拍照图传模块(夜视2K高清1080P)-基础功能-连接wifi路由器和开无线热点
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/product/audioCamera.html"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 说明这节测......
  • MyBatis 动态 SQL、多表查询与注解开发详解
    MyBatis动态SQL、多表查询与注解开发详解1.MyBatis动态SQLMyBatis提供了强大的动态SQL功能,允许我们根据不同的条件拼接SQL语句,避免了手动拼接SQL的繁琐和错误。常见的动态SQL标签包括:if:用于条件判断,根据条件是否成立来决定是否拼接SQL片段。choose(when,ot......
  • MySQL索引原理及慢查询优化12
    背景MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等......