网站建设属于劳务吗,wordpress 头部不显示,做暧暧视频网站,美丽寮步网站建设哪家好0 前言
机票查询系统#xff0c;日均亿级流量#xff0c;要求高吞吐#xff0c;低延迟架构设计。提升缓存的效率以及实时计算模块长尾延迟#xff0c;成为制约机票查询系统性能关键。本文介绍机票查询系统在缓存和实时计算两个领域的架构提升。
1 机票搜索服务概述
1.1 …0 前言
机票查询系统日均亿级流量要求高吞吐低延迟架构设计。提升缓存的效率以及实时计算模块长尾延迟成为制约机票查询系统性能关键。本文介绍机票查询系统在缓存和实时计算两个领域的架构提升。
1 机票搜索服务概述
1.1 机票搜索的业务特点
机票搜索业务输入目的地然后点击搜索后台就开始卷了。基本1~2s将最优结果反给用户。这个业务存在以下业务特点。
1.1.1 高流量、低延时、高成功率
超高流量同时对搜索结果要求也很高——成功率要高不能查询失败或强说成功希望能反给用户最优最新数据。
1.1.2 多引擎聚合SLA不一
机票搜索数据来源哪很大一部分来源自己的机票运价引擎。为补充产品丰富性还引入国际一些GDS、SLA如联航。将外部引擎和自己引擎结果聚合后发给用户。
1.1.3 计算密集IO密集
大家可能意识到我说到我们自己的引擎就是基于一些运价的数据、仓位的数据还有其他一些航班的信息我们会计算、比对、聚合这是一个非常技术密计算密集型的这么一个服务。同时呢外部的GDS提供的查询接口或者查询引擎对我们来说又是一个IO密集型的子系统。我们的搜索服务要将这两种不同的引擎结果很好地聚合起来。
1.1.4 不同业务场景的搜索结果不同要求
作为OTA巨头还支持不同应用场景。如同样北京飞上海由于搜索条件或搜索渠道不一返回结果有所不同。如客户是学生可能搜到学生特价票其他用户就看不到。
2 公司基建
为应对如此业务有哪些利器
2.1 三个独立IDC
互相做灾备实现其中一个IDC完全宕机业务也不受影响。
2.2 DataCenter技术栈
SpringCloudK8s云服务海外感谢Netflix开源支撑国内互联网极速发展。
2.3 基于开源的DevOps
我们基于开源做了整套的DevOps工具和框架。
2.4 多种存储方案
公司内部有比较完善可用度比较高的存储方案包括MySQLRedisMangoDB……
2.5 网络可靠性
注重网络可靠性做了很多DR开发SRE实践广泛推动熔断限流等以保证用户得到高质量服务。
3 机票搜索服务架构
3.1 架构图 IDC有三个先引入GateWay分流前端服务前端服务通过服务治理和后端聚合服务交互。聚合服务再调用很多引擎服务。
聚合服务结果通过Kafka推到AI数据平台做大数据分析、流量回放等数据操作。云上部署数据的过滤服务使传回数据减少90%。
4 缓存架构
4.1 缓存的挑战和策略
4.1.1 为啥大量使用缓存应对流量高峰
提高效率、速度的首选技术手段。
虽使用很多开源技术但还有瓶颈。如数据库是分片、高可用的MySQL但和一些云存储、云数据库比其带宽、存储量、可用性有差距通常需用缓存保护我们的数据库不然频繁读取会使数据库很快超载。
很多外部依赖提供给我们的带宽QPS有限。而公司整体业务量是快速增长的而外部的业务伙伴给我们的带宽要么已达技术瓶颈要么开始收高费用。此时使用缓存就可保护外部的一些合作伙伴不至于击穿他们的系统也可帮我们降本。
4.1.2 本地缓存 VS 分布式缓存
整个架构的演进过程一开始本地缓存较多后来部分用到分布式缓存。
本地缓存的主要问题
启动时有个冷启动过程对快速部署不利与分布式缓存相比本地缓存命中率太低
对海量的数据而言单机提供命中率非常低5%甚至更低。对此现已全面切向Redis分布式缓存。本着对战failure的理念不得不考虑失败场景。万一集群挂掉或一部分分片挂掉这时需要通过限流客户端、熔断等防止雪崩效应。
4.1.3 TTL
命中率新鲜度动态更新
TTL生命周期跟业务强相关。买机票经常遇到刚在报价列表页看到一个低价机票点进报价详情页就没了why航空公司低价舱位票一次可能只放几张若在热门航线可能同时几百人在查它们都可能看到这几张票它就会出现在缓存里。若已有10人订了票其他人看到缓存再点进去运价就已失效。对此就要求权衡不能片面追求高命中率还得兼顾数据新鲜度。为保证新鲜度、数据准确性还有大量定时任务去做更新和清理。
4.2 缓存架构演进
4.2.1 多级缓存
架构图的三处缓存
引擎级缓存L1分布式聚合缓存基本上就是用户看到的最终查询结果L2二级缓存分布式的子引擎结果
若聚合服务需多个返回结果很大程度都是先读一级缓存一级缓存没有命中的话再从二级缓存里面去读中间结果这样可以快速聚合出一个大家所需要的结果返回。
4.2.2 引擎缓存
引擎缓存
查询结果缓存中间产品缓存基础数据缓存
使用一个多级缓存模式。如下图最顶部是指引前的结果缓存储存在Redis引擎内部根据产品、供应商有多个渠道的中间结果所以对子引擎来说会有个中间缓存。 这些中间结果计算需要数据这数据就来自最基础的一级缓存。 4.2.3 基于Redis的一级缓存
Pros
读写性能高水平扩展
Cons
固定TTL命中率和新鲜度的平衡
结果
命中率20%高新鲜度(TTL5m动态刷新读写延迟3ms 一级缓存使用Redis是考虑其读写性能好快速水平扩展性能能提高存储量及带宽。但当前设计局限性
为简单使用固定TTL这是为保证返回结果的相对新鲜为命中率和新鲜度还在不断提高
目前解决方案还不能完美解决这俩问题。
分析下返回结果一级缓存命中率小于20%某些场景更低就是为保证更高准确度和新鲜度。高优先度一级缓存的TTL肯定低于5min有些场景可能只有几十s支持动态刷新整体延迟小于3ms。整个运行过程可用性较好。
4.2.4 基于Redis的二级缓存架构升级
Pros
读写性能进一步提升服务可靠性提升成本消减
Cons
增加复杂性替代二级索引
结果
成本降低90%读写性能提升30% 4.2.5 基于MongoDB的二级缓存
二级缓存一开始用MongoDB
高读写性能支持二级缓存方便数据清理多渠道共用子引擎缓存TTL通过ML配置
会计算相对较优TTL保证特定数据
有的可缓存久点有的可快速更新迭代 二级缓存基于MongoDB也有局限性
架构是越简单越好多引入一种存储会增加维护代价强依赖由于MongoDB的license模式使得费用非常高Ops
结果
但二级缓存使查询整体吞吐量提高3倍通过机器学习设定的TTL使命中率提升27%各引擎平均延时降低20%
都是可喜变化。在一个成熟的流量非常大的系统能有个10%提升就是个显著技术特点。
针对MongoDB也做了提升最后将其切成Redis通过设计方案虽增加部分复杂性但替代了二级索引改进结果就是成本降低90%读写性能提升30%。
5 LB演进
系统首要目标满足高可用其次是高流量支撑
可通过多层的均衡路由实现把这些流量均匀分配到多个IDC的多个集群。
5.1 目标
高可用 高流量支撑 低事故影响范围 提升资源利用率 优化系统性能长尾优化 如个别查询时间特长需要我们找到调度算法问题一步步解决。
5.2 LB架构
负载均衡
GatewayLBIP直连 DC路由规则 IP直连Pooling 计算密集型任务计算时长权重部分依赖外部查询 Set化
LB的演进 公司的路由和负载均衡的架构非常典型有GateWay、load、balance、IP直连在IP基础上实现了一项新的Pooling技术。也实现了Set化在同一IDC所有的服务都只和该数据中心的节点打交道尽量减少跨地区网络互动。
5.3 Pooling
为啥做 Pooling有些计算密集的引擎存在耗时长耗CPU资源多的子任务这些子任务可能夹杂一些实时请求所以这些任务可能会留在线程里边阻塞整个流程。
Pooling就负责我们把这些子任务放在queue里边将节点作为worker总是动态的去取每次只取一个计算完了要么把结果返回要么把中间结果再放回queue。这样的话如果有任何实时的外部调用我们就可以把它分成多次放进queue进行task的整个提交执行和应用结果的返回。 5.4 过载保护 有过载保护
扔掉排队时间超过T的请求T为超时时间所有请求均超时系统整体不可用扔到排队时间超过X的请求X为小于T的时间平均响应时间为Xm系统整体可用。m为平均处理时间
Pooling设计需要一个过载保护当流量实在太高可用简单的过载保护把等待时间超过某阈值的请求全扔掉。当然该阈值肯定小于会话时间就能保证整个Pooling服务高可用。
虽可能过滤掉一些请求但若没有过载保护易发生滚雪球效应queue里任务越来越多当系统取到一个任务时实际上它的原请求可能早已timeout。 压测结果可见达到系统极限值前有无Pooling两种case的负载均衡差异。如80%负载下不采用Pooling的排队时间比有Pooling高10倍 所以一些面临相同流量问题厂家可考虑把 Pooling 作为一个动态调度或一个control plan的改进措施。
如下图实现Pooling后平均响应时间基本没大变化还是单层查询计算普遍60~70ms。但实现Pooling后显著的键值变少键值范围也都明显控制在平均时间两倍内。这对大体量服务来说比较平顺曲线正是所需。 6 AI赋能
6.1 应用场景
6.1.1 反爬
在前端我们设定了智能反爬能帮助屏蔽掉9%的流量。
6.1.2 查询筛选
在聚合服务中我们并会把所有请求都压到子系统而是会进行一定的模式运营找出价值最高实际用户然后把他们的请求发到引擎当中。对于一些实际价值没有那么高的更多的是用缓存或者屏蔽掉一些比较昂贵的引擎。
6.1.3 TTL智能设定
整个TTL设定使用ML技术。
6.2 ML技术栈和流程
ML技术栈和模型训练流程 6.3 过滤请求 开销非常大的子引擎多票会拼接多个不同航空公司的出票返给用户。但拼接计算昂贵只对一部分产品开放。通过机器学习找到哪些查询可通过多票引擎得到最好结果然后只对这一部分查询用户开放结果也很不错。
看右上角图片整个引擎能过滤掉超过80%请求流量高峰时能把曲线变得平滑效果显著。整个对于查询结果、订单数都没太大影响且节省80%产品资源。这种线上模型运算时间也短普遍低于1ms。
7 总结
使用了多层灵活缓存从而能很好的应对高流量的冲击提高反应速度。
使用可靠的调度和负载均衡这样就使我们的服务保持高可用状态并且解决了长尾的查询延迟问题。最后内部尝试了很多技术革新将适度的AI技术推向生产从目前来看机器学习发挥了很好的效果。带来了ROI的提升节省了效率另外在流量高峰中它能够起到很好的削峰作用。以上就是我们为应对高流量洪峰所采取了一系列有针对性的架构改善。
多层灵活的缓存 - 流量速度可靠的调度和负载均衡 - 高可用适度的AI - ROI削峰
8 QA
Q啥场景用缓存
A所有场景都要考虑缓存。高流量时每级缓存都能带来很好的保护系统提高性能的效果但要考虑到缓存失效的应对措施。
Q缓存迭代过程咋样的
A先有L1又加L2主要因为流量越来越大引擎外部依赖逐渐撑不住不得不把中间结果也高效缓存此即L1到L2的演进。二级缓存用Redis替代MongoDB是出于高可用考虑费用节省也是一个因素但更主要是发现自运维的MongoDB比Redis整体可用性要差很多所以最后决定切换。
Q分布式缓存的设计方式
A分布式缓存的关键在于它的KV怎么设定须根据业务场景如有的KV里加入IP地址即Pooling基于Redis建立了它的队列所以我们queue当中是把这种请求方的IP作为建设的一部分放了进去就能保证子任务能知道到哪查询它相应的返回结果。
Q为什么redis的读写延迟能做到3ms以内呢
A读写延时低其实主要指读延时读延时3ms内没问题。
Q这队列是内存队列还是MQ
A互联队列用Redis主要是为保证其高可用性。
Q缓存失效咋刷新涉及分布式锁
A文章所提缓存失效并非指它里边存的数据失效主要指整个缓存机制失效。无需分布式锁因为都是单独的KV存储。
Q缓存数据一致性咋保证
A非常难保证常用技巧缓存超过阈值强行清除。然后若有更精确内容进来要动态刷新。如本可存5min但第2min有位用户查询并下单这时肯定是要做一次实时查询顺便把还没过期的内容也刷新一遍。
Q热key大key咋监控
A对我们热区没那么明显因为一般我们的一个key对应一个点一个出发地和一个目的地中间再加上各种渠道引擎的限制。而不像分片你分成16或32片可能某一分片逻辑设计不合理导致那片过热然后相应硬件直接到瓶颈。
Q详解Pooling
A原理子任务耗时间不一若完全基于SOA进行动态随机分肯定有的计算节点分到的子任务较重有的较轻加入Pooling就好像加入一个排队策略特别是对中间还会实时调用离开几s的case排队策略能极大节省计算资源。
Q监控咋做的
A基于原来用了时序数据库如ClickHouse和Grafana然后现在兼容Promeneus的数据收集和API。
Q二级缓存采用Redis的啥数据类型
A二级缓存存储中间结果应该是分类型的数据类型。
QTTL计算应该考虑啥
A最害怕数据异常如系统总返回用户一个已过期低票价用户体验很差所以这方面牺牲命中率然后缩短TTL只不过TTL控制在5min内有时还要微调所以还得用机器学习模型。
QIP直连和Pooling没明白是AGG中涉及到的计算进行拆分将中间结果进行存储其他请求里若也需要这中间计算可直接获取吗
AIP直连和PoolingIP直连其实把负载均匀分到各节点Pooling只不过你要计算的子任务入队然后每个运算节点每次取一个计算完再放回去这样计算效率更高。中间结果没有共享中间结果存回是因为有的子任务需要中间离开再去查其他实时系统所以就相当于把它分成两个运算子任务中间的任务要重回队列。
Q下单类似秒杀发现一瞬间票抢光了相应缓存咋更新
A若有第1个用户选择了一个运价没通过要把缓存数据都给杀掉然后尽量防止第2个用户还会陷入同样问题。
Q多级缓存数据咋保证一致
A因为我们一级缓存存的是最终的结果二级缓存是中间结果所以不需要保持一致。
Q一级、二级、三级缓存请求过来咋提高吞吐量按理说每个查询过程都消耗时间吞吐量应该下降
A是的若无这些缓存几乎所有都要走一遍。实时计算时间长而且部署的集群能响应的数很有限然后一、二、三级每级都能拦截很多请求。一级约拦截20%二级差不多40%~50%。此时同样的集群吞吐量显然明显增加。
Q如何防止缓存过期时刻产生的击穿问题目前公司是定时任务主动缓存还是根据用户请求进行被动的缓存
A对于缓存清除我们既有定时任务也有被动的更新。比如说用户又取了一次或者购票失败这些情况我们都是会刷新或者清除缓存的。
Q搜索结果会根据用户特征重新计算运价和票种吗
A为啥我的运价跟别人不一致是不是被大数据杀熟其实不是的那为啥同样查询返回结果不一呢有一定比例是因为缓存数据异常如前面缓存的到后面票卖光了然后又推给了不幸用户。公司有很多引擎如说国外供应商尤其联航他们系统带宽不够可用性不高延时也高所以部分这种低价票不能及时返回到我们的最终结果就会出现这种“杀熟”这并非算法有意只是系统局限性。
QPooling 为啥用 Redis
A为追求更高读写速度其他中间件如内存队列很难用在分布式调度。若用message queue由于它存在明显顺序性不能基于KV去读到你所写的如你发了个子任务这时你要定时取其结果但你基于MQ或内存队列没法拿到这也是一个限制。
Q多级缓存预热咋保证MySQL不崩
A冷启动问题更多作用在本地缓存因为本地缓存发布有其他的情况需要预热在这之间不能接受生产流量。对多级缓存、分布式缓存预热不是问题因为它本就是分布式的可能有部分节点要下线之类但对整个缓存机制影响很小然后这一部分请求又分散到我们的多个服务器不会产生太大抖动。但若整个缓存机制失效如缓存集群完全下掉还是要通过熔断或限流对实时系统作过载保护。
QRedis对集合类QPS不高咋办
ARedis多加些节点减少它的存储使用率把整体throughput提上即可。若你对云业务有了解就知道每个节点都有throughput限制。若单节点throughput成为瓶颈那就降低节点使用率。
关注我紧跟本系列专栏文章咱们下篇再续 作者简介魔都技术专家兼架构多家大厂后端一线研发经验各大技术社区头部专家博主编程严选网创始人。具有丰富的引领团队经验深厚业务架构和解决方案的积累。 负责 中央/分销预订系统性能优化 活动优惠券等营销中台建设 交易平台及数据中台等架构和开发设计 目前主攻降低软件复杂性设计、构建高可用系统方向。 参考
编程严选网 本文由博客一文多发平台 OpenWrite 发布