点击蓝字,关注我们背景
在春节五一等用户出行高峰期,常常存在:1)单一的直达飞机或火车等直达的出行方式无票的情况;2)两地之间没有直达的交通方式,需要利用多种交通方式组合来实现用户出行目的;3)用户长途出行如境外旅行飞机直达的出行方案价格过高,需要采用多程飞机中转的方式来追求性价比。
针对上述问题,用户一般会采用组合多种交通工具的中转出行方式,例如从杭州->大理可以分两段出行:先杭州-(飞机)->昆明,然后昆明-(高铁)->大理。然而对于普通用户来说,由于缺少整个交通网络的全局信息,当用户自行组合多种交通方式时可能无法达到时间或者价格最优,而不满足用户预期,最终可能会导致用户取消出行。
对于飞猪等 OTP 来说,因为有火车、飞机、汽车等多种交通方式的价库数据以及班次路线数据,平台可以基于此大规模数据进行全局路径查询,为用户的出行推荐最合适的出行方案。目前多种交通方式组合出行的推荐方案已经应用在火车票 List 页、机票 List 页、火车抢票推荐等模块,为用户出行提供更多的可选项。

针对出行场景来说,交通网是一个天然的图数据结构,可以将机票、火车票、汽车票等的班次当作图上的边,将站点当作图上的节点。那么针对用户的一次中转出行,我们将其抽象为一个图上的路径规划问题:针对图上的 AB 两点,如何快速的返回最优路径,并且最优路径需要满足一定库存约束。不同于地图等场景,在旅行场景的图上进行路径规划,主要面临几个难点:
图中节点规模不大但边的规模巨大:国内火车站和机场的节点规模在0.5w 左右,而15天的机票&火车票的班次数据有1300w 左右
库存(余票)和价格都是动态且频繁变化的:价库更新 QPS 日常在10w 左右,假期高峰在20w 左右。
两阶段解法不可取:针对背景中提到的问题容易想到的一个简单解法:离线构造路径+线上召回并实时查询价库进过滤,这也是我们目前线上在应用的一个版本,但是这样很难保证召回方案的有效率,很可能召回的大部分方案都因为没有库存而被过滤掉,因此需要将召回路径与查询价库相融合。
图数据调研
首先是集团内部的图数据库:
图数据库 | 优势&不足 |
iGraph | +基于的底层表为 iGraph 表,所以支持实时数据 +User Defined Function,支持用户自定义比较函数,不一定支持的复杂的路径寻找(主要是简单的过滤) 不足: - 度限制 - 不支持自定义路径 |
TuGraph (分布式图数据库GeaBase) | + 有查询服务 + 支持流、动态图(GeaFlow) + 支持 ODPS 写入&回流 + 支持制定搜索路径 |
GRAPE (GraphScope) | + 基于 ODPS 进行开发,可利用 ODPS 节点,也可基于 PyODPS + 支持 gremlin 语法 + 支持自定义算法 + GraphLearn 的算法集成到 GRAPE 不足: - 内置算法-仅支持无向图 - 自定义算法需了解 PIE 编程规范 |
GDB | + 数据基于 oss,需要申请预算 https://www.aliyun.com/product/gdb?spm=ata.21736010.0.0.2bc3609a5CJS2D |
此外还有 Neo4j 等开源图数据解决方案,但是需要较多的开发资源介入,暂时不在我们的考虑范围之内。
我们日常开发中常会使用 iGraph 进行商品 Summary 或者召回表的查询,对 iGraph 相对较熟悉,并且 iGraph 可以支持几十万的的查询和更新 QPS,所以最开始的我们先从 iGraph 构图开始进行尝试。最终我们经过 MVP 的尝试,得出以下结论:
因为 iGraph 的边表仅支持 KKV 表(K:出发站,K:到达站,V:车次),所以两个站点之间有多少个车次就需要维护多少张边表,以火车为例:两个站点间最多的车次有180趟,那么15天的出行范围就需要考虑180 * (T+15)张边表,维护成本巨大。
采用 iGraph 构图 RT 较大,我们仅尝试火+火中转,在第二程过滤时,RT 已不满足要求查询出错。
TuGraph 是蚂蚁针对特殊巨型复杂网络和超大实时更新数据而研发的一款简单易用高性能图数据库产品,具有以下五大特点:高易用性、高性能、高稳定性和可用性、高扩展性、实时更新的特点,最终我们选择在 TuGraph 上进行我们的尝试。
TuGraph 实践
针对飞猪场景,在 TuGraph 中,我们将火车站、机场、汽车站等作为节点,包括两种类型的边:车次边(火车、飞机、汽车的每天的班次数据)、中转边(火车<->飞机、火车<->汽车、飞机<->汽车,飞机<->飞机、火车<->火车等不同交通方式进行接力出行时两个站点之间用户需要步行/城市交通/打车的中转时长距离)。接下来介绍我们的详细实践,以及说明 TuGraph 如何解决我们的问题的。
节点建模
在 TuGraph 中 node 为节点的唯一标识,一个 node 会有不同的类型(如火车站、飞机场、汽车站等),并带有节点的基础属性,如节点的名称、城市名称、城市编码等。以机场为例,我们的节点建模情况如下:

节点配置
边建模
在 TuGraph 中一条边,由边的起点(SRCID)、 终点(DSTID)、边时间戳(TIMESTAMP)唯一标识,这样唯一标识一条边的好处是:针对飞猪图节点规模不大但边的规模巨大的问题,天然的可以得到解决。中转边无时间属性,对于每天是一样的,所以可以理解为车次边的简化版,这里我们以一个具体的航班为例说明车次边的建模过程:
待建模航班:2025-05-20北京(大兴机场)-杭州(萧山机场),MF8130,21:50-23:55。
那么:大兴机场->SRCID,萧山机场->DSTID, 2025-05-20 21:50 ->TIMESTAMP,此外车次边还包括一些的基础属性:站类型、站名称、站所在的城市、车次的出发时间、到达时间、车次状态(是否还在运行)、车次号等。

有两点需要特别注意:
对于车次边来说有两个重要的属性:价格、库存,因此我们接受交通行业的价库 Metaq 消息利用 TuGraph 的更新接口进行价库消息的实时更新。同时查询接口也可参考该文档。
TTL 配置:如上所述我们讲航班的具体出发时间作为 TIMESTAMP 的 value,必须要考虑时间过期之后边的删除问题,不然数据库会变的越来越臃肿,值得注意的是 TuGraph 提供了边的 TTL 配置能力,支持边的自动删除。
数据回流
在我们建好节点/边的数据 Sachema 后,就需要回流具体的数据,将整个图构建起来了,TuGraph 支持离线导入和实时导入数据。由于我们节点&边会每天存在变化,所以我们采用了离线 ODPS 导入的形式,每天更新节点&边的数据。

数据回流
查询
TuGraph 采用 ISO GQL 进行路经查询,简单易用。可以在进行 GQL 的中指定节点或者边的属性,这样我们可以天然的将库存的价库的约束放到路经查询的过程中,将召回路径与查询价库相融合。以例为我们进行飞机+火车中转路经查询的的 GQL 实例:
MATCH(tswhere@idin[ & quot;+ depGeabaseCode +& quot; ])- [r1:flight_flightwherer1.@timestamp>="+startTime1+"andr1.@timestamp<="+endTime1+"andstock_info>0ANDupdatetime>="+updateTime+"ANDstatus=false] -& gt;(tm1)- [s:f2tWHEREs.arr_city_code!=ts.city_code] -& gt;(tm2)- [r2:train_trainwherer2.@timestamp>="+startTime2+"andr2.@timestamp<="+endTime2+"andstock_info>0ANDstatus=falseandr2.dep_time>=(r1.arr_time+s.cost_time+3600)ANDr2.dep_time<(r1.arr_time+"+transferGap+")] -& gt;(te where@idin[ & quot;+ arrGeabaseCode +& quot; ]) RETURN r1.@src_id,r1.@dst_id,r1.traffic_code,r1.stock_info,r1.price_info,r1.stock_type,s.cost_tim e,r2.@src_id,r2.@dst_id,r2.traffic_code,r2.stock_info,r2.price_info,r2.stock_type,r2.arr_t ime - r1.dep_timeastotaltime,r1.dep_time,r1.arr_time,r2.dep_time,r2.arr_time,ts.city_codeORDERbytotaltimelimit & quot;+ geabaseLimitCnt
监控&运维

监控后台
如上图可以看出,TuGraph 在20w 更新 QPS 的情况下更新 RT 只有3ms,查询 RT 在40ms 左右,满足我们的业务预期。
效果
我们主要从以下几个方面验证了 TuGraph 的效果:
性能指标:对比基准桶 RT 下降20ms
效率指标:有结果率绝对提升+2%,价库新鲜度+90%

RT 效果

·END·

欢迎关注TuGraph代码仓库✨
https://github.com/tugraph-family/tugraph-db
https://github.com/tugraph-family/tugraph-analytics




