背景 随着互联网世界产生的数据越来越多,数据之间的联系越来越复杂层次越来越深,人们希望从这些纷乱复杂的数据中探索各种关联的需求也在与日递增。为了更有效地应对这类场景,图技术受到了越来越多的关注及运用。

DB-ENGINES 趋势报告显示图数据库趋势增长遥遥领先

在携程,很早就有一些业务尝试了图技术,并将其运用到生产中,以 Neo4j 和 JanusGraph 为主。2021 年开始,我们期望规范业务的使用,并适配携程已有的各种系统,更好地服务业务方。经过调研,我们选择分布式图数据库 NebulaGraph 作为管理的对象,主要基于以下几个因素考虑:

NebulaGraph 开源版本即拥有横向扩展能力,为大规模部署提供了基本条件; 使用自研的原生存储层,相比 JanusGraph 这类构建在第三方存储系统上的图数据库,性能和资源使用效率上具有优势; 支持两种语言,尤其是兼容主流的图技术语言 openCypher,有助于用户从其他使用 Cypher 语言的图数据库(例如 Neo4j)中迁移; 拥有后发优势(2019 年起开源),社区活跃,且主流的互联网公司都有参与(腾讯,快手,美团,网易等); 使用技术主流,代码清晰,技术债较少,适合二次开发; NebulaGraph 架构及集群部署 NebulaGraph 是一个分布式的计算存储分离架构,如下图:

NebulaGraph 架构图

其主要由 graphd,metad 和 storaged 三部分服务组成,分别负责计算,元数据存取,图数据(点,边,标签等数据)的存取。在携程的网络环境中,我们提供了三种部署方式来支撑业务,分别是:三机房部署、单机房部署和蓝绿双活部署。

三机房部署 携程数据库的运维治理实践的三机房部署

用于满足一致性和容灾的要求,优点是任意一个机房发生机房级别故障,集群仍然可以使用,适用于核心应用。但缺点也是比较明显的,数据通过 raft 协议进行同步的时候,会遇到跨机房问题,性能会受到影响。

单机房部署 携程数据库的运维治理实践的单机房部署

集群所有节点都在一个机房中,节点之间通讯可以避免跨机房问题(应用端与服务端之间仍然会存在跨机房调用),由于机房整体出现问题时该部署模式的系统将无法使用,所以适用于非核心应用进行访问。

蓝绿双活部署 在实际使用中,以上两种常规部署方式并不能满足一些业务方的需求,比如:性能要求较高的核心应用,三机房的部署方式所带来的网络损耗可能会超出预期。根据携程酒店某个业务场景真实测试数据来看,本地三机房的部署方式延迟要比单机房高 50%+,但单机房部署无法抵抗单个 IDC 故障。此外,还有用户希望能存在类似数据回滚的能力,以应对应用发布,集群版本升级可能导致的错误。

考虑到使用图数据库的业务大多数据来自离线系统,通过离线作业将数据导入到图数据库中,数据一致的要求并不高,在这种条件下使用蓝绿部署能够在灾备和性能上得到很好的满足。

携程数据库的运维治理实践的蓝绿双活部署

与此同时我们还增加了一些配套的辅助功能,比如:

分流:可以按比例分配机房的访问,也可以主动切断对某个机房的流量访问 灾备:在发生机房级故障时,可自动切换读访问的流量,写访问的流量切换则通过人工进行操作 蓝绿双活方式是在性能、可用性、一致性上的一个折中的选择,使用此方案时应用端架构也需要有更多的调整以配合数据的存取。

生产上的一个例子:

三机房情况

上图为三机房情况,下图为蓝绿部署情况:

蓝绿部署

中间件及运维管理 我们基于 K8s CRD 和 Operator 来进行 NebulaGraph 的部署,同时通过服务集成到现有的部署配置页面和运维管理页面,来获得对 Pod 的执行和迁移的控制能力。基于 sidecar 模式监控、收集 NebulaGraph 的核心指标并通过 Telegraf 发送到携程自研的 Hickwall 集中展示,并设置告警等一系列相关工作。

此外,我们集成了跨机房的域名分配功能,为节点自动分配域名用于内部访问(域名只用于集群内部,集群与外部连通是通过 IP 直连的),这样做是为了避免节点漂移造成 IP 变更,影响集群的可用性。

在客户端上,相比原生客户端,我们主要做了以下几个改进和优化:

Session 管理功能 原生客户端 Session 管理比较弱,尤其是 v2.x 早期几个版本,多线程访问 Session 并不是线程安全的,Session 过期或者失效都需要调用方来处理,不适合大规模使用。同时,虽然官方客户端创建的 Session 是可以复用的,并不需要 release,官方也鼓励用户复用,但是却没有提供统一的 Session 管理功能来帮助用户复用。因此,我们增加了 Session Pool 的概念来实现复用。

其本质上是管理一个或多个 Session Object Queue,通过 borrow-and-return 的方式(下图),确保了一个 Session 在同一时间只会由一个执行器在使用,避免了共用 Session 产生的问题。同时通过对队列的管理,我们可以进行 Session 数量和版本的管理,比如:预生成一定量的 Session,或者在管理中心发出消息之后变更 Session 的数量或者访问的路由。

Session 管理功能

蓝绿部署(包括读写分离) 上面章节中介绍了蓝绿部署,相应的客户端也需要改造以支持访问 2 个集群。由于生产中,读和写的逻辑往往不同,比如:读操作希望可以由 2 个集群共同提供数据,而写的时候只希望影响单边,所以我们在进行蓝绿处理的时候也增加了读写分离(下图)。

蓝绿部署

流量分配 如果要考虑到单边切换以及读写不同的路由策略,就需要增加流量分配功能。我们没有采用携程内广泛使用的 Virtual IP 作为访问路由,希望有更为强大的定制管理能力及更好的性能。

通过直连而不是 Virtual IP 中转可以减少一次转发的损耗; 在维持长连接的同时也能实现每次请求使用不同的链路,平摊 graphd 的访问压力; 完全自主控制路由,可以实现更为灵活的路由方案; 当存在节点无法访问的时候,客户端可以自动临时排除有问题的 IP,在短时间内避免再次使用。而如果使用 Virtual IP 的话,由于一个 Virtual IP 会对应多个物理 IP,就没有办法直接这样操作。 通过构造面向不同 IDC 的 Session Pool,并根据配置进行权重轮询,就可以达到按比例分配访问流量的目的(下图)。

流量分配

将流量分配集成进蓝绿模式,就基本实现了基本的客户端改造(下图)。

流量分配

结构化语句查询 图 DSL 目前主流的有两种,Gremlin 和 Cypher,前者是过程式语言而后者是声明式语言。NebulaGraph 支持了 openCypher(Cypher 的开源项目)语法和自己设计的 nGQL 原生语法,这两种都是声明式语言,在风格上比较类似 SQL。尽管如此,对于一些较为简单的语句,类似 Gremlin 风格的过程式语法对用户会更为友好,并且有利用监控埋点。基于这个原因,我们封装了一个过程式的语句生成器。

例如:

系统调优实践

系统调优实践 由于建模,使用场景,业务需求的差异,使用Nebula Graph的过程中所遇到的问题很可能会完全不同,以下以携程酒店信息图谱线上具体的例子进行说明,在整个落地过程我们遇到的问题及处理过程(文中以下内容是基于Nebula Graph 2.6.1进行的)。

关于酒店该业务的更多细节,可以阅读《信息图谱在携程酒店的应用》这篇文章。

酒店集群不稳定 起因是酒店应用上线后发生了一次故障,大量的访问超时,并伴随着 “The leader has changed” 这样的错误信息。稍加排查,我们发现 metad 集群有问题,metad0 的 local ip 和 metad_server_address 的配置不一致,所以 metad0 实际上一直没有工作。

但这本身并不会导致系统问题,因为 3 节点部署,只需要 2 个节点工作即可。后来 metad1 容器又意外被漂移了,导致 IP 变更,这个时候实际上 metad 集群已经无法工作(下图),导致整个集群都受到了影响。

酒店集群不稳定

在处理完以上故障并重启之后,整个系统却并没有恢复正常,CPU 的使用率很高。此时,外部应用并没有将流量接入进来,但整个 metad 集群内部网络流量却很大,如下图所示: