本文转自 | 携程技术

作者简介

Chaplin,携程资深PMO,平时喜欢解决系统相关的问题,包括但不限于分布式/大数据量/性能/体验等,不畏复杂但更喜欢简单。本文旨在分享携程机票后服务订单处理团队,在构建机票订单缓存系统过程中的一些思考总结,希望能给大家一些启发或帮助。通篇分为以下七大部分:背景,瓶颈,选型,架构,方案,优化,总结,文章概要如下图:

一、背景

近些年随着携程机票业务的不断发展,用户量和订单量也稳定地增长,再加上用户访问入口的多样性、机票的有效期特别长等特征,导致查询流量不断增长。这些,给基于强依赖订单数据库的订单查询系统带来了不小的压力。不仅业务上带来的流量压力,技术改造也带来了更多的流量,如微服务化的推进、机票前后台订单业务解耦,不可避免地使得订单查询系统被依赖程度进一步提高。而且我们没有使用类似GraphQL 的技术,之前的前后台服务直连数据库,现在使用查询API,没有高度定制化产生了数据冗余,带来额外的查询压力。作为典型的用户订单详情查询服务,承受着越来越重的压力,峰值时期订单详情查询QPS数倍于同期订单TPS。为了保障用户的使用体验,支持机票业务的持续发展,保证机票订单查询系统的稳定高效,构建和不断升级机票订单查询系统,自然成为重要且紧急的事情。

二、瓶颈

大部分应用系统的瓶颈都会出在比较慢的地方,如外部资源及磁盘IO或数据库,订单系统的瓶颈显而易见,重复高频的订单数据访问,最终带来的是订单数据库访问的压力。尽管之前通过数据库主从复制、读写分离的手段,一定程度上缓解数据库的压力。但是从节点的增加无疑会带来一些不得不考虑的问题,包括数据同步时的AG延迟问题,数据库服务器的运维成本等。不仅局部优化有瓶颈,而且我们一直认为局部的优化,不如总线级别的优化来的更有效率,数据库总线跟内存总线对于订单系统数据存取速度的提升并不是一个级别。基于以上种种,机票订单后处理团队,决定构建一套相对完整的基于内存数据缓存体系,不仅用于缓解机票订单数据库的访问压力,也用来提高订单查询服务的稳定性和容灾能力。

三、选型

目前各种类型的缓存都活跃在成千上万的应用服务中,还没有一种缓存方案可以解决一切的业务场景或数据类型,我们需要根据自身的特殊场景和背景,选择最适合的缓存方案。目前业内用来做缓存的,通常主要有以下几种:Memcache、Redis,MongoDB。关于几者的优劣对比网络上也有不少的相关文章,这里不再详细列举。选型并非是一个固定不变的标准,虽然系统设计没有银弹,但是路就在脚下,我们必须要结合自己的业务场景及运维能力做出综合判断(取舍),这里说明一下我们关注的点。(1)故障快速恢复,机票订单的查询服务面临的QPS较高,又是许多上层应用服务的依赖项,由于业务比较核心,所以数据安全问题必须考虑。因此第一要求故障快速恢复,携程的Redis部署是多组多实例多机柜,完全满足需求。(2)性能要好,机票订单数据的查询,超过92%的场景都是根据订单号的查询,场景比较单一,很少有使用索引条件的缓存查询需求。单一机票订单的数据量并不多,所以要求的缓存性能越高,用于查询集群机器线程池效率越高,整个查询集群机器越少,硬件成本及维护成本才会变低。(3)输出稳定,缓存性能稳定,查询集群的线程池运行越稳定,尖刺就会少,整个系统稳定性会提高。我们综合机票订单业务场景,采用了Redis作为机票订单缓存的存储介质。因为Redis的部署特性,要求我们的单个实例不能太大,为了避免实例太大,我们前期做了很多工作,比如容量评估及预测,通过拆分+压缩来规避容量问题。

四、架构

在携程机票的微服务化架构体系之下,按照业务层级的不同,后台服务呈现层次化调用的树状结构。比如订单详情服务输出机票订单的状态、价格、航班、乘客等各类基本信息,同时依赖于机票的改签业务、退票业务、航变业务,支付业务等提供的查询服务,而这些独立的子业务与订单详情服务本身一样需要订单基本信息数据作为业务支撑。为了避免循环依赖,需要把订单基本数据查询业务剥离出来,单独提供底层数据访问服务,同时支撑不同业务模块。所以机票订单后服务团队依据服务的层次设计了两级缓存的体系,如下图所示。(1)一级缓存一级缓存是订单查询服务响应结果级别的缓存。订单查询服务作为一系列核心服务,支撑了不同前端渠道的订单查询请求,包括APP,Online、H5等,同时也支持后台订单处理的各个环节业务对订单数据的查询需求。订单查询服务的请求QPS很高,对同一个订单的请求在1秒之内有时会达到十几次。对于许多业务场景来说,1秒的时间内订单的数据基本不会发生变化,因此完全可以将该订单的整个订单查询服务响应结果缓存起来,设置秒级别的过期时间。这样做一方面能够提高服务的响应速度,另一方面能够减少对底层数据库服务的访问,降低数据库的压力,预防意外的或者恶意的流量冲击。(2)二级缓存二级缓存是针对订单数据库热点数据表的数据的底层缓存。根据对机票订单业务的梳理和对数据库数据表的访问频率的统计,我们筛选出了80+的热点机票订单数据表,对每个表的数据建立缓存,并提供数据查询服务接口。这样一来,将不同业务方与订单数据库解耦,统一的数据访问服务层更有利于统一建立和管理数据缓存系统,提高缓存数据的使用效率。同时对底层数据库的垂直拆分和水平拆分,做到数据使用的业务方无感,提高了系统的扩展性。

五、方案

缓存数据一致性的实现方案,一般可以分为两大类:主动式和被动式缓存。主动式缓存就是提前将可能访问到的数据加载到缓存中,然后设置过期时间,周期性刷新。等到需要查询数据的时候,可以优先命中缓存。这种方式比较适合于数据量不大,变化不频繁的场景。被动式缓存是指每次查询时先从缓存中查询数据,没有则查询底层数据库,然后把数据库的查询结果加载到缓存,并设置一定的过期时间。数据过期后,当再次遇到查询请求时重复前面所说的过程。被动式缓存具有延迟加载的特性,也就是只有真正被访问过的数据才会被加载到缓存中,能够更有效的利用缓存数据。机票订单的数据热度具有阶段性,用户订单成交的前后阶段订单查询频率较高,用户的历史订单查询频率就很低了。因此我们采用被动式加载的方案,随着时间的推移,老的历史订单数据会自动逐渐过期,新的订单数据被逐渐加载。缓存数据的总量保持平稳,同时避免了老旧数据的清理。分布式缓存架构的数据一致性是个难点,这也是订单处理团队一直在努力思考并解决的重中之重。缓存数据意味着数据的变更在过期时间之内是被忽略的,可能与实际的数据库数据有一定差异。一般情况下,一种方案是尽量设置较短的过期时间,以使缓存数据尽快与数据源同步。但是较短的过期时间带来的是缓存命中率的大大降低,削弱了缓存数据的效果。为了解决数据一致性问题,我们设计了两套方案来更新缓存数据,保证缓存数据的实时性。两套方案相互补充,避免其一短暂失效而产生一致性问题。方案一的思路是扫描数据库的数据变化记录,比如数据表里面的数据更新时间戳,或者订阅数据库的binlog记录。当扫描到数据变化时,删除对应的缓存数据。方案二的思路是借助于携程的消息通知平台服务。在订单处理流转的各个重要环节,会有消息事件产生。通过订阅这些消息,能够感知到订单数据的变化,并且通过对不同消息的影响数据范围可以精准地配置缓存数据更新。两套方案的并行实施,保证了缓存数据的延迟控制在了100毫秒以内。缓存的构建过程中,我们也遇到几个必须慎重处理的关键点:缓存穿透、缓存击穿、缓存雪崩。(1)缓存穿透的解决方案(空标记)缓存穿透是指,在数据存储系统中不存在的记录,不会被存储到缓存中。这种记录每次的查询流量都会穿透到数据存储层。在高流量的场景下,不断查询空结果会大量消耗数据查询服务的资源,甚至在恶意流量攻击下可能拖垮数据库系统。以机票订单为例,有些订单购买了保险,也有的订单没有购买保险的记录。没有保险产品的订单每次查询时都会从数据库中查到空的结果,命中不了缓存,消耗了大量的Batch Request。针对这种情况,我们采取了设置空标记的措施,针对查询正常返回但是记录为空的数据,将特定的空标记写入缓存,避免了这部分流量穿透到数据库。(2)缓存击穿的解决方案(分布式锁)缓存击穿是指,对于一个热点KEY,在其失效的瞬时,如果有大量的对这个KEY的请求,都会到达数据库。我们通过分布式锁的方式,在缓存数据失效的时刻,仅允许单一请求读取数据库数据并加载到缓存。(3)缓存雪崩的解决方案(随机时间差)缓存雪崩是指,在同一时间有大量缓存一起失效。我们使用了一个简单有效的方法,对不同的缓存数据设置过期时间时,采用不同的时间,比如增加一个小的随机值。此外,由于不同订单处理过程中的消息发生时间不同,而订单处理过程一般会有等待队列,处理时间会有差异,也降低了缓存同时失效的风险。

六、优化

做完以上这些,应该算个及格的缓存系统,但是我们相信更优秀的系统是通过一步步的迭代及演进出来的。机票订单缓存系统的优化,必然要与机票订单的自身特征深度吻合,高度定制化,才能发挥更加有效的作用。我们通过对携程机票订单以及机票用户行为的大量数据分析,制定了一些针对性的优化措施。(1)全天候分段缓存过期策略(按时间)根据对机票订单系统每日实时订单量数据的分析,实时订单量呈现双驼峰的形态,上午9点到11点以及下午1点到4点之间这两个时间段内,订单量处于峰值状态,中午处于相对的低谷。而夜间订单量为逐渐下降的趋势,直至凌晨1点到凌晨3点达到最低点。这与人们的出行作息时间是非常吻合的。在订单量的峰值时段,数据库访问压力比较大,反之低谷时段则压力较小。对此我们为订单查询服务的一级缓存设置了分时段的过期时间策略,业务高峰时间段缓存时间相对调整变长,低峰时段调整变短。每个查询服务可以单独配置这套规则,随时动态调整。这样对于数据存储端压力来说也就是起到了削峰填谷的作用。(2)订单状态分类策略(按业务)机票订单根据业务定义有各种不同的状态,表示着订单处理流转的各个阶段。基于对订单状态及其后续变更行为数据的分析,对于已退票或取消的终态订单,其订单数据基本不会再发生变化。这类订单的缓存过期时间可以设置的相对较长;对于已过起飞时间的订单,用户再查看访问订单数据的概率也大大降低,其订单数据缓存时间也可以相对长一些;而订单处理中未出票等过程态订单,短时间状态变更的可能性很高,因此不应该放入缓存之中。(3)用户行为的订单访问预测(预加载)根据统计,用户乘机前的几个小时内,出于关注出发时间、机场航站楼、航班变动,办理登机手续等需要,会频繁查看订单数据。数据访问的压力集中在了白天的峰值时间段内。基于这种对用户行为的预测,我们考虑了在前一天的凌晨业务低峰阶段,将第二天业务高峰时间段内起飞的订单数据,提前加载到缓存中预热,将高峰时间段的一部分缓存构建压力,转移到了低峰时间段,既提高了低峰时间段的系统资源利用,也缓解了高峰时间段数据库的压力。不仅如此,更大的好处是,第二天订单数据库即使有短暂的故障,用户查询当天出行订单也不会受到影响,因为数据都已经提前构建在缓存里了。

七、总结

经过查询系统的不断迭代,埋点数据的统计显示,订单缓存数据的命中率达到了90%以上,也就是说对数据库的访问压力降低到了原来的十分之一的水平。从数据库层面的性能统计监控来看,数据库服务器的cpu利用率峰值由60%降低到了15%,均值由20%以上降低到了10%以下。对数据系统的可用性来说改善效果非常显著。在服务响应性能方面,缓存系统的建立也带来了性能的提升。以订单详情服务为例,服务响应时间(PT99)均值由150ms降低到了120ms以下。以下列举一些保证性能及可靠性的具体技术点:1)采用了Redis Pipeline技术,节省了建立链接的时间。2)采用了GZIP压缩技术,节省4-5倍的Redis存储空间。3)采用了NIO技术,让我们的缓存服务支撑更多的链接。4)采用了Hystrix熔断技术,保证了单个Redis实例异常时候,可以放弃该实例,保证其他正常读取,保证高可用。整个缓存系统的构建,没有侵入到复杂的机票业务中,更像一个“外挂”系统,工作量及业务风险可控。除缓存系统外,未来的订单查询业务核心挑战依然是稳定性,包括故障的快速定位,调用链的梳理及简化。中国程序员VS美国程序员,这也太形象了吧...研究主攻数据库和磁盘故障优化,曾经复读才考上三本,他如今让华为开出201万年薪DBA林工:OracleDG 环境主备业务数据不同步备库报 ORA-600 错误怎么整?那个从深圳流水线去了纽约做程序员的女工,最近失业了乘风破浪的中国数据库有个程序员老公有多爽???做一个国产操作系统,有多难?MySQL 的这个 BUG,坑了多少人?为什么不建议把数据库部署在docker容器内?A里正在拆中台,你还在建中台吗?腾讯技术专家:解密小程序云开发数据库 | 干货微服务架构框架?这里为你汇总了15种阿里P8税后收入170万?P8、P9 及以上到底是什么水平?机房生存指南交易、账务系统去 Oracle 经验误执行了 rm -rf /*删库之后,除了跑路还能怎么整?!DTCC中国数据库技术大会2020-2019SACC2019中国系统架构师大会PostgreSQL 2019中国技术大会点击上方文字可以直接进入小程序浏览,下载请在后台分别回复关键词⏬ DTCC、SACC、PG19即可直接收到完整版 PPT 下载链接「在看」吗?

订单失效怎么做的_此招一出,数据库压力降低90%,携程机票订单缓存系统实践...相关推荐

  1. 订单失效怎么做的_数据库压力降低90%,携程机票订单缓存系统实践

    本文旨在分享携程机票后服务订单处理团队,在构建机票订单缓存系统过程中的一些思考总结,希望能给大家一些启发或帮助.通篇分为以下七大部分:背景,瓶颈,选型,架构,方案,优化,总结,文章概要如下图: 一.背 ...

  2. 订单失效怎么做的_携程技术专家:数据库压力降低90%,订单缓存系统架构实践...

    来源:携程技术(ID:ctriptech) 本文旨在分享携程机票后服务订单处理团队,在构建机票订单缓存系统过程中的一些思考总结,希望能给大家一些启发或帮助.通篇分为以下七大部分:背景,瓶颈,选型,架构 ...

  3. 干货 | 支持10X增长,携程机票订单库Sharding实践

    作者简介 初八,携程资深研发经理,专注于订单后台系统架构优化工作:JefferyXin,携程高级后端开发专家,专注系统性能.业务架构等领域. 一.背景 随着机票订单业务的不断增长,当前订单处理系统的架 ...

  4. 订单失效怎么做的_携程旅游系统崩溃?济南一市民遭遇“钱已付,酒店订单不存在”...

    10月2日晚间,有多名网友发布微博,称通过携程APP预订酒店,付款后显示订单不存在,系统疑似崩溃."我在携程APP上预订的酒店,银行卡的钱已经扣了,可携程订单显示'该订单已失效'." ...

  5. 订单失效怎么做的_虾皮Shopee新加坡电商做什么产品好呢?订单少得可怜怎么办呢...

    虾皮Shopee新加坡电商做什么产品好呢?订单少得可怜怎么办呢 我做跨境电商也有六年的时间了,在电商这个行业也有自己的一些经验.经验也许没有其他大卖家丰富,但会将我知道的都进行分享.如果有不懂得亚马逊 ...

  6. tkmybatis 子查询_日均20亿流量:携程机票查询系统的架构升级

    携程机票查询系统,日均20亿流量,且要求高吞吐,低延迟的架构设计.于是,提升缓存的效率以及实时计算模块长尾延迟,成为制约机票查询系统性能的关键.本文是携程集团机票业务技术总监宋涛在「云加社区沙龙onl ...

  7. 干货 | 万字长文详解携程酒店订单缓存 存储系统升级实践

    作者简介 荣华,携程高级研发经理,专注于后端技术项目研发管理. 军威,携程软件技术专家,负责分布式缓存系统开发 & 存储架构迁移项目. 金永,携程资深软件工程师,专注于实时计算,数据分析工程. ...

  8. tp5.1 获取表里的所有数据_携程机票数据仓库建设之路

    一.前言 随着大数据技术的飞速发展,海量数据存储和计算的解决方案层出不穷,生产环境和大数据环境的交互日益密切.数据仓库作为海量数据落地和扭转的重要载体,承担着数据从生产环境到大数据环境.经由大数据环境 ...

  9. 订单失效怎么做的_30 分钟未付款取消订单,怎么做?

    点击上方" 码农编程进阶笔记 ",选择"置顶或者星标" 文末有干货,每天定时与您相约! 第一次亲密接触 问题:我这边有个需求,用户下单后 30 分钟如果没付款就 ...

最新文章

  1. Transformer新型神经网络在机器翻译中的应用 | 公开课笔记
  2. 如何为计算机视觉任务选择正确的标注类型
  3. Linux记起始和结束时间,Linux历史命令的记住执行时间
  4. delphi Oracle导出多个表,Oracle数据库自动备份工具(Delphi源码)
  5. vue 渲染函数处理slot_vue render 渲染函数
  6. 一个黑色全屏的计时器_我入手了一个1000多的智能手环,值吗?|Fitbit Charge 4测评...
  7. java浮点数除以0_为什么用浮点数(或双精度)将数字除以零不会在Java中抛出java.lang.Arithmetic...
  8. 没有找到 AUTOBACKUP
  9. JellyViewPager
  10. Ubuntu 12.04装五笔,同时又可以打拼音。
  11. jsonp和ajax的区别,dataType jsonp和JSON之间的区别
  12. Atitit 个人 企业 政府 等组织 财政收入分类与提升途径attilax总结 v2
  13. 领导力21法则自测题
  14. Unity 自定义Package
  15. vb.net 编写的简易串口调试程序
  16. 设备管理器中英特尔智音技术有黄色感叹号解决办法
  17. 数电常见74系列器件及其功能总结(不要再混乱啦,值得收藏)
  18. RobotFramework学习笔记二:遇到Frame框架
  19. 全局地址池 与接口地址池
  20. 【多元统计分析】均值向量和协方差阵的检验——spss上机实验

热门文章

  1. 【路径规划】基于人工势场法、蝙蝠优化算法、人工鱼群算法、果蝇优化算法的路径规划(Matlab代码实现)
  2. 使用libpcap库用c编写网络嗅探器
  3. 书P60/3:某百货公司为了促销,采用购物打折的方法。消费1000元以上,打九五折;消费2000元以上,打九折;消费3000元以上,打八五折;消费5000元以上,打八折。输入款数,计算并输出优惠价。
  4. Google Glass不属于VR/AR团队,谷歌已确认
  5. 专题一 · 1004
  6. 全新版大学英语四综合教程答案
  7. 陌陌Q3季报图解:净利环比下降28% 沈南鹏退出董事会
  8. java 01 0301_法学专业代码为0301什么意思
  9. c 连接mysql批量存储数据库_C语言连接操作MySQL数据库的方法
  10. 九、从华为HMS快速身份验证能力FIDO2看密码学知识