Practical Experience of Nebula Graph in Ctrip Finance: Architecture, Use Cases, and Optimizations
This article describes how Ctrip Finance built a large‑scale Nebula Graph platform for financial risk control, data lineage, and fraud detection, detailing the system architecture, real‑world applications, performance challenges, and the engineering optimizations applied to achieve sub‑15 ms query latency.
Background – Since the establishment of Ctrip Finance in September 2017, many financial and risk‑control scenarios required real‑time graph analysis that relational databases could not efficiently support. Starting in 2020, Ctrip Finance introduced Nebula Graph to build a billion‑node graph storage and analysis platform.
1. Graph Fundamentals – A graph consists of vertices and edges; in Nebula Graph an edge is represented as (source, edge_type, rank, destination). Vertices and edges can carry attributes such as phone numbers, IDs, transaction amounts, etc. The article also lists typical graph use cases in finance, including fraud call detection, transfer interception, equity penetration, data lineage, knowledge graphs, security, recommendation, code dependency, and supply‑chain analysis.
2. Graph Platform Construction – The platform evolved from a single 3‑node Nebula cluster to a multi‑cluster architecture supporting offline graph (Spark‑connector, batch algorithms), online graph (real‑time updates via QMQ, daily Hive increments), full‑data consistency checks, and large‑scale hardware (e.g., three 64‑core machines with 320 GB RAM for online, twelve 64‑core machines for offline).
3. Internal Application Cases
3.1 Data Lineage Graph – Models data processing pipelines as vertices (database + table) and edges (ETL jobs), enabling quick upstream/downstream tracing and replacing complex SQL joins.
3.2 Risk‑Control Relationship Graph – Connects users, devices, and other identifiers to detect fraud rings; supports time‑bounded graph backtracking by using edge rank as a timestamp.
3.3 Real‑Time Anti‑Fraud Graph – Provides sub‑15 ms two‑hop queries for user‑centric fraud detection, combining graph‑based rules with model features.
4. Pain Points and Optimizations
4.1 Query Performance – Improved by sacrificing write performance (storing edge count in vertex properties), connection pooling (custom SessionPool to reuse Nebula sessions), and query‑side concurrency (parallel one‑hop and two‑hop queries).
4.2 Edge Hotspot Handling – Filters vertices with excessive connections to prevent result explosion.
4.3 Consistency – Since Nebula lacks transactions, periodic full‑data checks against Hive are performed to reconcile inconsistencies.
Code Example – Custom SessionPool for Connection Reuse
public class SessionPool {
/**
* 创建连接池
* @param maxCountSession 默认创建连接数
* @param minCountSession 最大创建连接数
* @param hostAndPort 机器端口列表
* @param userName 用户名
* @param passWord 密码
*/
public SessionPool(int maxCountSession, int minCountSession, String hostAndPort, String userName, String passWord) throws UnknownHostException, NotValidConnectionException, IOErrorException, AuthFailedException {
this.minCountSession = minCountSession;
this.maxCountSession = maxCountSession;
this.userName = userName;
this.passWord = passWord;
this.queue = new LinkedBlockingQueue<>(minCountSession);
this.pool = this.initGraphClient(hostAndPort, maxCountSession, minCountSession);
initSession();
}
public Session borrow() {
Session se = queue.poll();
if (se != null) {
return se;
}
try {
return this.pool.getSession(userName, passWord, true);
} catch (Exception e) {
log.error("execute borrow session fail, detail: ", e);
throw new RuntimeException(e);
}
}
public void release(Session se) {
if (se != null) {
boolean success = queue.offer(se);
if (!success) {
se.release();
}
}
}
public void close() {
this.pool.close();
}
private void initSession() throws NotValidConnectionException, IOErrorException, AuthFailedException {
for (int i = 0; i < minCountSession; i++) {
queue.offer(this.pool.getSession(userName, passWord, true));
}
}
private NebulaPool initGraphClient(String hostAndPort, int maxConnSize, int minCount) throws UnknownHostException {
List
hostAndPorts = getGraphHostPort(hostAndPort);
NebulaPool pool = new NebulaPool();
NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig();
nebulaPoolConfig = nebulaPoolConfig.setMaxConnSize(maxConnSize);
nebulaPoolConfig = nebulaPoolConfig.setMinConnSize(minCount);
nebulaPoolConfig = nebulaPoolConfig.setIdleTime(1000 * 600);
pool.init(hostAndPorts, nebulaPoolConfig);
return pool;
}
private List
getGraphHostPort(String hostAndPort) {
String[] split = hostAndPort.split(",");
return Arrays.stream(split).map(item -> {
String[] splitList = item.split(":");
return new HostAddress(splitList[0], Integer.parseInt(splitList[1]));
}).collect(Collectors.toList());
}
private Queue
queue;
private String userName;
private String passWord;
private int minCountSession;
private int maxCountSession;
private NebulaPool pool;
}5. Summary and Outlook – Nebula Graph has successfully supported data lineage, relationship‑person networks, and anti‑fraud scenarios in Ctrip Finance. Future work includes hot‑upgrade support, resource isolation, richer algorithm packages, and a more user‑friendly studio.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.