Graph 1 — Cơ sở dữ liệu đồ thị & khi nào dùng

13 thg 7, 2026 2 lượt xem
#fraud
#sql
#property-graph
#graph-database
#neo4j

Graph 1 — Cơ sở dữ liệu đồ thị & khi nào dùng

Bộ phận giám sát gian lận của một ngân hàng nhận cảnh báo: một tài khoản mới mở nhận 480 triệu đồng rồi trong 40 phút chuyển đứt qua 6 tài khoản khác nhau, mỗi tài khoản lại chia nhỏ ra 5–7 tài khoản nữa, và tất cả cuối cùng đổ về hai ví rút tiền mặt. Đây là hình mẫu kinh điển của money mule network — mạng lưới tài khoản trung gian rửa tiền. Câu hỏi nghiệp vụ rất đơn giản: "Từ tài khoản nghi vấn này, đi tối đa 5 bậc chuyển tiền, có tồn tại một vòng tròn hay một điểm hội tụ đáng ngờ nào không?"

Câu hỏi đó dễ nói bằng tiếng Việt nhưng cực khó viết bằng SQL. Nó là truy vấn multi-hop (nhiều bậc) với độ sâu thay đổi — bạn không biết trước cần bao nhiêu bậc JOIN. Đây chính là loại bài toán mà cơ sở dữ liệu đồ thị (graph database) sinh ra để giải. Bài này là nền móng của cả series: graph database là gì, mô hình property graph, vì sao nó thắng RDBMS ở truy vấn mạng lưới, khi nào nên và không nên dùng, và các tình huống ngân hàng thực tế.

Lưu ý về sandbox: SQL sandbox của Knowledge Base chạy trên PostgreSQL (chỉ đọc). Bài này có đúng một khối SQL đánh dấu "▶ Chạy được" — để minh hoạ cách RDBMS làm truy vấn 2-bậc bằng JOIN, làm điểm đối chiếu. Mọi khối Cypher (ngôn ngữ truy vấn của Neo4j) đều không chạy được ở đây, chỉ mang tính minh hoạ.

Graph database là gì

Cơ sở dữ liệu đồ thị là loại database mà quan hệ giữa các thực thể là công dân hạng nhất (first-class citizen), được lưu trực tiếp chứ không phải suy ra khi truy vấn. Trong RDBMS, quan hệ tồn tại ngầm — qua khoá ngoại (foreign key) và phải được "vật chất hoá" lại mỗi lần chạy JOIN. Trong graph database, quan hệ được lưu tường minh như một con trỏ vật lý từ thực thể này sang thực thể kia. Chính khác biệt về cách lưu quan hệ này quyết định mọi thứ.

Một đồ thị gồm hai thành phần cơ bản:

  • Node (đỉnh): thực thể — một khách hàng, một tài khoản, một giao dịch, một thiết bị, một địa chỉ.
  • Relationship (cạnh): mối liên hệ giữa hai node — "sở hữu", "chuyển tiền tới", "đăng nhập từ", "cùng số điện thoại với".

Nghe qua thì giống mô hình quan hệ (bảng cha–con nối bằng khoá). Khác biệt cốt lõi nằm ở chỗ: trong graph, đi từ một node sang node láng giềng là thao tác O(1) — theo con trỏ có sẵn — bất kể database có bao nhiêu tỷ node. Trong RDBMS, đi từ hàng này sang hàng liên quan là một lần JOIN, chi phí phụ thuộc kích thước bảng.

Mô hình Property Graph

Có hai mô hình đồ thị phổ biến. RDF triple biểu diễn mọi thứ thành bộ ba (subject, predicate, object) — ví dụ (TàiKhoản_A, chuyểnTiềnTới, TàiKhoản_B); đây là mô hình của Semantic Web/SPARQL, mọi dữ liệu kể cả thuộc tính đều là triple, phù hợp cho ontology và liên kết dữ liệu chuẩn hoá nhưng dài dòng. Mô hình còn lại — và là mô hình Neo4j cùng đa số graph database thương mại dùng — là Property Graph (đồ thị thuộc tính). Series này tập trung vào property graph.

Trong property graph, cả node lẫn relationship đều có thể mang thuộc tính (property) và nhãn/loại:

Thành phầnCó gìVí dụ ngân hàng
NodeMột hoặc nhiều label (nhãn phân loại) + tập property (cặp key–value)(:Account {account_no:'0011...', balance:480000000, opened:'2026-06'})
RelationshipMột type (loại, bắt buộc) + hướng (bắt buộc, có chiều) + tập property-[:TRANSFER {amount:80000000, at:'2026-06-30 09:14'}]->

Ba điểm cần nhớ về property graph:

  1. Label trên node phân loại thực thể (:Customer, :Account, :Device). Một node có thể mang nhiều label (:Account:HighRisk).
  2. Relationship luôn có hướng và có type. (A)-[:TRANSFER]->(B) khác (B)-[:TRANSFER]->(A). Type mô tả bản chất quan hệ; hướng mô tả chiều. Khi truy vấn, bạn có thể đi ngược hướng nếu muốn, nhưng hướng vẫn được lưu.
  3. Property nằm được trên cả cạnh. Số tiền, thời điểm, kênh của một lần chuyển tiền thuộc về relationship TRANSFER, không phải node. Đây là điểm property graph mạnh hơn hẳn RDF: một quan hệ giàu thuộc tính chỉ là một cạnh, không phải một cụm triple phụ trợ.

Sơ đồ trên là một property graph nhỏ: node có label (Customer, Account, Device) và property; relationship có type (OWNS, TRANSFER, LOGIN_FROM), hướng và property. Chú ý hai điều đáng ngờ mà mắt người nhìn ra ngay nhưng SQL khó bắt: (1) ba tài khoản tạo thành một vòng tròn chuyển tiền A→B→C→A, và (2) hai tài khoản của hai khách khác nhau lại cùng đăng nhập từ một thiết bị fp-9f2c. Đó là tín hiệu mule.

Vì sao graph thắng ở truy vấn multi-hop

Đây là lý do kỹ thuật quan trọng nhất, gói trong một khái niệm: index-free adjacency (kề nhau không cần index).

RDBMS làm việc bằng JOIN lặp

Giả sử ta có ba bảng quan hệ chuẩn: customers, accounts, transactions. Câu hỏi 2-bậc "khách hàng nào nhận tiền từ tài khoản của khách X" viết bằng SQL còn gọn. Khối dưới đây chạy được trên sandbox, minh hoạ RDBMS nối transactions → accounts → customers để trả về một quan hệ 2-bậc:

-- ▶ Chạy được
SELECT c_from.full_name AS nguoi_gui,
       c_to.full_name   AS nguoi_nhan,
       t.amount,
       t.created_at
FROM transactions t
JOIN accounts   a_from ON t.account_id      = a_from.id
JOIN customers  c_from ON a_from.customer_id = c_from.id
JOIN accounts   a_to   ON a_to.id           = t.account_id
JOIN customers  c_to   ON a_to.customer_id  = c_to.id
WHERE t.kind = 'transfer'
ORDER BY t.created_at DESC
LIMIT 50;

Khối này dùng đúng 5 bảng join (transactions, hai lần accounts, hai lần customers) theo đúng khoá transactions.account_id → accounts.idaccounts.customer_id → customers.id. Nó hoạt động tốt vì độ sâu cố định là 2.

Vấn đề xuất hiện khi độ sâu tăng. "Bạn của bạn của bạn" — tức 3 bậc — cần thêm một tầng self-JOIN vào bảng quan hệ. Câu "tìm đường chuyển tiền tối đa 5 bậc từ tài khoản nghi vấn" cần năm tầng self-JOIN nối tiếp. Và tệ nhất: nếu độ sâu không biết trước (đi cho tới khi gặp vòng tròn hoặc điểm rút tiền), SQL thuần không viết nổi — bạn buộc phải dùng WITH RECURSIVE, và hiệu năng của đệ quy trên bảng lớn thường sụp đổ.

Chi phí về mặt lý thuyết: mỗi tầng JOIN quét index của bảng con để tìm hàng khớp. Với đồ thị dày (mỗi tài khoản chuyển tới hàng chục tài khoản khác), số hàng trung gian bùng nổ theo cấp số nhân qua từng bậc. JOIN bậc k phải xử lý số hàng cỡ (fan-out)^k. Truy vấn 5–6 bậc trên bảng giao dịch hàng trăm triệu dòng có thể chạy hàng phút tới hàng giờ, hoặc timeout.

Graph làm việc bằng traversal

Trong graph database, mỗi node lưu sẵn con trỏ vật lý tới các cạnh của nó. "Đi từ tài khoản A sang các tài khoản A đã chuyển tiền" = dereference một danh sách con trỏ — không tra index toàn cục, không quét bảng. Đây là index-free adjacency: chi phí đi một bước chỉ phụ thuộc số láng giềng của node hiện tại, không phụ thuộc tổng kích thước database.

Hệ quả: chi phí traversal k bậc tỉ lệ với số node và cạnh thực sự chạm tới, không phải với kích thước bảng lũy thừa k. Cùng câu "đi 5 bậc từ tài khoản nghi vấn", graph chỉ duyệt vùng lân cận liên quan.

Bảng so sánh trực giác:

Tiêu chíRDBMS (self-JOIN / RECURSIVE)Graph (traversal)
Chi phí đi 1 bậcTra index bảng con (phụ thuộc kích thước bảng)Theo con trỏ (phụ thuộc số láng giềng)
Độ sâu cố định (1–2 bậc)Tốt, tối ưuTốt
Độ sâu sâu (5–10 bậc)Bùng nổ hàng trung gianỔn định
Độ sâu không biết trướcRất khó (đệ quy, dễ timeout)Tự nhiên (*1..5 trong Cypher)
Tìm đường / chu trình / cộng đồngRất khóBản địa

Đây không phải "graph luôn nhanh hơn SQL". Đó là: graph thắng đúng ở lớp truy vấn theo quan hệ nhiều bậc và độ sâu thay đổi. Với truy vấn khác, RDBMS vẫn thắng — mục sau nói rõ.

Khi nào NÊN và khi nào KHÔNG NÊN dùng graph

Chọn graph là quyết định kiến trúc, không phải theo trào lưu. Nguyên tắc: dữ liệu và câu hỏi có "hình dạng" mạng lưới thì dùng graph; dạng bảng/tổng hợp thì đừng.

NÊN dùng graph khi

  • Dữ liệu giàu quan hệ — giá trị nằm ở cách các thực thể kết nối với nhau nhiều hơn ở bản thân từng thực thể (mạng chuyển tiền, mạng sở hữu công ty, mạng thiết bị–tài khoản).
  • Truy vấn theo đường/mạng lưới — tìm đường ngắn nhất, phát hiện chu trình (vòng tròn giao dịch), tìm cụm/cộng đồng, lan truyền ảnh hưởng, đếm bậc kết nối.
  • Độ sâu thay đổi — không biết trước cần đi bao nhiêu bậc; câu hỏi kiểu "tới khi nào gặp điều kiện X".
  • Quan hệ thay đổi thường xuyên và giàu ngữ cảnh — mỗi quan hệ có thuộc tính riêng (số tiền, thời điểm, độ tin cậy).

KHÔNG NÊN dùng graph khi

  • Tổng hợp trên bảng rất lớnSUM, COUNT, GROUP BY trên hàng trăm triệu dòng giao dịch để lên báo cáo. Đây là địa hạt của OLAP columnar hoặc data warehouse, không phải graph.
  • Giao dịch dạng bảng thuần — CRUD trên bản ghi độc lập, ít quan hệ (bảng sản phẩm, log audit phẳng). RDBMS như PostgreSQL làm tốt hơn và rẻ hơn.
  • Cần tính nhất quán ACID mạnh cho sổ cái tiền — core banking ledger nên ở RDBMS. (Neo4j có ACID, nhưng đây không phải điểm mạnh cạnh tranh của nó.)
  • Truy vấn không dùng quan hệ — nếu câu hỏi hầu như chỉ lọc/tổng hợp thuộc tính một thực thể, graph không mang lại gì thêm.

Trong thực tế ngân hàng, kiến trúc đúng thường là polyglot (đa mô hình): core ledger ở RDBMS, phân tích tổng hợp ở data warehouse/OLAP, và một graph riêng đồng bộ từ nguồn để giải các câu hỏi mạng lưới. Xem thêm tư duy đa mô hình ở NoSQL tổng quan.

Use case ngân hàng của graph database

Đây là những bài toán mà graph tạo giá trị rõ nhất trong ngân hàng:

  • Phát hiện gian lận & AML (chống rửa tiền). Nhận diện mạng lưới mule, vòng tròn giao dịch (circular payment), cấu trúc phân tán tiền (smurfing/structuring), điểm hội tụ tiền mặt. Đây là use case số một; xem giám sát giao dịch AML. Series này có riêng một bài fraud & AML với graph.
  • KYC / UBO — chủ sở hữu hưởng lợi (Ultimate Beneficial Owner). Truy vết chuỗi sở hữu công ty nhiều tầng (công ty mẹ → công ty con → cá nhân) để tìm ai thực sự kiểm soát một pháp nhân — bài toán multi-hop điển hình mà quy định KYC bắt buộc.
  • Entity resolution. Gộp các bản ghi rời rạc (số điện thoại, email, thiết bị, địa chỉ trùng) về cùng một thực thể thật — phát hiện một người mở nhiều tài khoản dưới danh tính khác nhau.
  • Recommendation. Gợi ý sản phẩm tài chính dựa trên mạng lưới hành vi khách hàng tương tự.
  • Knowledge graph. Kết nối dữ liệu nội bộ (khách, sản phẩm, quy định, cảnh báo) thành một mạng tri thức truy vấn được.

Neo4j & hệ sinh thái

Neo4j là graph database property-graph phổ biến nhất và là trọng tâm của series. Ba thành phần cần biết ngay:

  • Cypher — ngôn ngữ truy vấn khai báo của Neo4j, dùng cú pháp "vẽ hình" trực quan: () cho node, -[]-> cho relationship. Ví dụ minh hoạ (Cypher, không chạy trên sandbox) cho câu "đi 1 tới 5 bậc chuyển tiền tạo thành vòng tròn về lại tài khoản nghi vấn":
// Minh hoạ (không chạy trên sandbox PostgreSQL)
MATCH path = (a:Account {account_no:'0011'})-[:TRANSFER*1..5]->(a)
RETURN path
LIMIT 10;

Chỉ một dòng [:TRANSFER*1..5] thể hiện điều mà SQL cần năm tầng self-JOIN hoặc đệ quy: đi từ 1 đến 5 bậc theo cạnh TRANSFER và quay về đúng node xuất phát. Series đi sâu Cypher ở Cypher cơ bảnCypher nâng cao.

  • GDS (Graph Data Science) — thư viện thuật toán đồ thị của Neo4j: PageRank, community detection (Louvain), shortest path, centrality, similarity. Đây là công cụ chấm điểm rủi ro theo cấu trúc mạng; xem thuật toán đồ thị và nền tảng lý thuyết ở thuật toán đồ thị (algorithm).
  • Mô hình lưu trữ & vận hành — Neo4j lưu native graph (index-free adjacency thật sự ở tầng lưu trữ), hỗ trợ ACID. Phần triển khai, đồng bộ dữ liệu và tích hợp ở vận hành & tích hợp.

So sánh nhanh

RDBMSGraph (Neo4j)Graph khác
Mô hìnhBảng + khoá ngoạiProperty graphRDF triple (SPARQL), hoặc column-graph (JanusGraph/Gremlin)
Quan hệNgầm, tính lại qua JOINTường minh, lưu như con trỏTường minh (triple)
Multi-hopYếu (JOIN/đệ quy)Mạnh (traversal)Mạnh
Tổng hợp bảng lớnMạnhYếuYếu
Ngôn ngữSQLCypherSPARQL / Gremlin

RDF hợp cho ontology và liên kết dữ liệu chuẩn hoá; Gremlin/JanusGraph hợp khi cần scale ngang cực lớn trên hạ tầng phân tán. Với đa số bài toán mạng lưới ngân hàng cỡ vừa, property graph + Cypher (Neo4j) cho năng suất phát triển tốt nhất, nên series chọn hướng này.

Use case thực tế

Bối cảnh: Phòng Giám sát giao dịch nhận một tài khoản nghi vấn ACC-0011 — nhận 480 triệu đồng trong ngày 30/06 rồi phát tán. Nghiệp vụ cần trả lời trong vài giây: "Trong tối đa 5 bậc chuyển tiền, có vòng tròn quay về ACC-0011, hoặc có điểm hội tụ nhận từ ≥ 3 nhánh khác nhau không?" — dấu hiệu điển hình của mạng mule.

Nếu làm bằng RDBMS thuần: phải viết WITH RECURSIVE trên bảng transactions (giả định ~200 triệu dòng/tháng). Vì fan-out trung bình mỗi tài khoản là 8, không gian tìm kiếm tới bậc 5 cỡ 8^5 ≈ 32.768 nhánh, mỗi bước lại tra index bảng lớn. Trên thực tế đội đã đo: truy vấn đệ quy 5 bậc chạy ~90 giây và thường timeout khi mở rộng cửa sổ thời gian.

Chuyển sang graph: đồng bộ dữ liệu transactions → accounts → customers sang một Neo4j graph, mỗi tài khoản là node :Account, mỗi lần chuyển là cạnh :TRANSFER {amount, at}. Câu hỏi trên viết một truy vấn Cypher với [:TRANSFER*1..5] (minh hoạ ở mục trên) và một truy vấn đếm bậc hội tụ. Kết quả đo thực nghiệm nội bộ:

RDBMS đệ quy 5 bậcNeo4j traversal 5 bậc
Thời gian truy vấn~90 giây (hay timeout)< 1 giây
Viết truy vấn~40 dòng SQL đệ quy2–3 dòng Cypher
Độ sâu độngRất khóBản địa (*1..5)

Với graph, đội phát hiện ACC-0011 → ACC-0033 → ACC-0055 → ACC-0011 là một vòng tròn 3 bậc (rửa tiền qua tài khoản trung gian), đồng thời ACC-0088điểm hội tụ nhận tiền từ 6 nhánh — hai ví rút mặt. Toàn bộ mạng 14 tài khoản mule bị khoanh vùng và đóng băng trong ngày, thay vì mất nhiều ngày dò tay. (Các con số là minh hoạ dựa trên hình mẫu mule thực tế, không phải dữ liệu ca cụ thể.)

Ghi nhớ

  • Graph database coi quan hệ là công dân hạng nhất — lưu tường minh dưới dạng con trỏ, thay vì tính lại qua JOIN như RDBMS.
  • Property graph = node (có nhiều label + property) và relationship (có type bắt buộc + hướng + property). Property nằm được trên cả cạnh — khác và gọn hơn RDF triple.
  • Index-free adjacency là chìa khoá: đi một bậc = theo con trỏ, chi phí phụ thuộc số láng giềng, không phụ thuộc kích thước database.
  • Multi-hop là điểm graph thắng RDBMS: self-JOIN nhiều tầng bùng nổ theo (fan-out)^k và bất lực khi độ sâu không biết trước; traversal ổn định và xử lý độ sâu động tự nhiên.
  • NÊN graph khi dữ liệu giàu quan hệ, truy vấn theo đường/mạng lưới, độ sâu thay đổi. KHÔNG khi tổng hợp bảng lớn hay giao dịch dạng bảng thuần → dùng RDBMS/OLAP.
  • Use case ngân hàng lõi: gian lận/AML (mạng mule, vòng tròn giao dịch), KYC/UBO, entity resolution, recommendation, knowledge graph.
  • Neo4j = property graph + Cypher + GDS. Cypher viết truy vấn "vẽ hình"; [:TYPE*1..5] thay cho hàng chục dòng SQL đệ quy.
  • Kiến trúc thực tế là polyglot: RDBMS cho ledger, OLAP cho tổng hợp, graph riêng cho câu hỏi mạng lưới.

Bài viết liên quan

Cách index hoạt động (B-Tree), đọc EXPLAIN, seq scan vs index scan và mẫu tối ưu truy vấn.

13 thg 7, 2026 4

Khoá, ràng buộc, quan hệ và chuẩn hoá 1NF/2NF/3NF — thiết kế lược đồ đúng từ đầu.

13 thg 7, 2026 4

Kết nhiều bảng đúng cách: các loại JOIN, bẫy thường gặp và phép hợp tập.

13 thg 7, 2026 3

Truy vấn lồng, CTE (WITH), CTE đệ quy và hàm cửa sổ — vũ khí cho phân tích nâng cao.

13 thg 7, 2026 3