全部產品
Search
文件中心

PolarDB:基於PolarDB的圖分析:銀行金融領域圖分析實踐

更新時間:Jun 14, 2025

本文以公開的金融交易領域資料集為例,基於雲原生資料庫PolarDB的圖分析,針對金融交易欺詐檢測情境進行圖查詢,以發現欺詐交易的關聯關係,計算交易間的Jaccard相似性,從而進行欺詐預警。

關於圖資料庫引擎

圖分析是資料科學中的一個重要領域,專註於通過圖結構表示資料,並執行各種計算與分析任務。圖結構由節點(或稱為頂點)和邊組成,節點通常代表實體,而邊則表示實體之間的關係。圖計算廣泛應用於社交網路分析、推薦系統、知識圖譜、路徑最佳化等多個領域。

PolarDB PostgreSQL版高度相容Apache AGE的圖引擎,支援對知識圖譜的儲存和查詢檢索,能夠在同一個資料庫叢集上同時使用標準的ANSI SQL和圖查詢語言openCypher進行查詢。

  • 完全相容PolarDB PostgreSQL版

    AGE是PolarDB PostgreSQL版的一個擴充,可以在現有的PolarDB資料庫中使用,且無需重新構建資料庫。AGE繼承了PolarDB所有強大功能,包括事務、並發控制、以及多種索引和最佳化技術。

  • 統一的圖形和關係型查詢

    AGE允許同時處理關係型資料和圖形資料,支援在同一個查詢中混合使用SQL和圖查詢語言,使得處理複雜的資料模型更加容易和高效。

  • 支援Cypher查詢語言

    AGE支援使用Cypher查詢語言。Cypher查詢語言專為圖資料庫設計,文法簡單且靈活,提供了一種直觀的方式來進行圖資料的查詢和操作。

  • 高效能

    結合PolarDB的最佳化技術和專為圖資料設計的索引,AGE能夠高效地處理大規模圖形資料和複雜的圖形查詢。

綜上, 藉助於AGE強大的能力,PolarDB可以簡單、高效地處理各類圖查詢。

業務情境

情境描述

在現代欺詐及各類金融犯罪中,欺詐者通過改變自身身份等手段以實現逃避風控規則的目的。通過構建圖資料庫以追蹤使用者行為的圖結構,即時分析欺詐行為的離散資料,識別欺詐環,從而能夠快速有效地防範和解決欺詐行為。

資料和模型

資料來源於金融交易領域的公開資料集。該資料是為電商平台Vesta提供的交易記錄,包括交易相關的裝置、地址、郵箱等資訊。通過該資料集可以識別欺詐交易,並進行風險預測。

未經處理資料為CSV格式,包含交易資訊(交易號,地址,郵箱等)以及交易識別資訊(裝置資訊,裝置類型等),資料集包含了眾多的資訊,本文中將資料模型抽象為下圖,實際業務情境可根據實際情況進行調整:

    • transaction(交易)

    • product(交易產品)

    • addr1(交易地址1)

    • addr2(交易地址2)

    • emaildomain(交易時使用的郵箱網域名稱)

    • deviceinfo(交易裝置資訊)

    • devicetype(交易裝置類型)

    • transaction_product(交易與產品關係)

    • transaction_addr1(交易與地址1關係)

    • transaction_addr2(交易與地址2關係)

    • transaction_emaildomain(交易與郵箱網域名稱關係)

    • transaction_deviceinfo(交易與裝置資訊關係)

    • transaction_devicetype(交易與裝置類型關係)

以上模型以交易為中心,通過交易號(transactionid)進行關聯。

最佳實務

說明

本案例使用AGE專案提供圖資料視覺化工具,將查詢結果進行圖形化的表達。詳情請參考視覺化檢視

建議配置

為了得到良好的體驗,PolarDB叢集建議使用以下配置:

  • 產品版本:標準版

  • 資料庫引擎:PostgreSQL 14

  • 核心版本:>=14.12.23.1

  • CPU:>= 4 核

  • 記憶體:>= 16 GB

  • 磁碟:>= 100 GB

資料庫準備

  1. 使用高許可權帳號安裝外掛程式。

    CREATE EXTENSION age;
  2. 將外掛程式加入需要使用此外掛程式的資料庫或使用者的搜尋路徑和預先載入庫中。

    • 會話級載入外掛程式:

      說明

      使用Data Management(Data Management)用戶端設定search_path時,可能會存在相容性問題,您可使用PolarDB-Tools執行相關語句。

      SET search_path = ag_catalog, "$user", public;
      SELECT * FROM get_cypher_keywords() limit 0;
    • 永久載入外掛程式:

      ALTER DATABASE <dbname> SET search_path = ag_catalog, "$user", public;
      ALTER DATABASE <dbname> SET session_preload_libraries TO 'age';

資料入庫

  1. 建立圖。使用位於ag_catalog命名空間中的create_graph函數建立圖(此處以fraud_graph為例)。

    SELECT create_graph('fraud_graph');
  2. 插入節點和邊。由於下載的資料為CSV檔案,無法直接作為點邊結構的圖資料進行入庫。請聯絡我們擷取將資料轉換為PolarDB中的vertex和edge的Python指令碼,轉換結果如下所示:

    • 部分點資料

      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:transaction {transactionid : 2990783, isfraud : 0 } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:product {productid : 158945 } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:addr1 {addr1 : '299.0' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:addr2 {addr2 : '87.0' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:emaildomain {emaildomain : 'gmail.com' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:deviceinfo {deviceinfo : 'SM-G920V Build/NRD90M' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:devicetype {devicetype : 'mobile' } ) RETURN v $$ ) as (n agtype);
      ...
    • 部分邊資料

      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:product) WHERE a.transactionid = 2990783 AND b.productid = 158945 MERGE (a)-[e:transaction_product]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:addr1) WHERE a.transactionid = 2990783 AND b.addr1 = '299.0' MERGE (a)-[e:transaction_addr1]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:addr2) WHERE a.transactionid = 2990783 AND b.addr2 = '87.0' MERGE (a)-[e:transaction_addr2]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:emaildomain) WHERE a.transactionid = 2990783 AND b.emaildomain = 'gmail.com' MERGE (a)-[e:transaction_emaildomain_p]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:deviceinfo) WHERE a.transactionid = 2999403 AND b.deviceinfo = 'SM-G920V Build/NRD90M' MERGE (a)-[e:transaction_deviceinfo]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:devicetype) WHERE a.transactionid = 2999404 AND b.devicetype = 'mobile' MERGE (a)-[e:transaction_devicetype]->(b) RETURN e$$) as (e agtype);
      ...

    將轉換後的結果儲存為SQL檔案,配合用戶端工具,如psql等可完成資料匯入。

使用樣本

簡單查詢

資料統計

  • 統計各種類型節點數量。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n)
        RETURN count(*)
    $$) as (number_of_vertex agtype);

    返回結果如下:

     number_of_vertex
    ----
     1076004
  • 統計transaction節點數量。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)
        RETURN count(*)
    $$) as (number_of_transaction agtype);

    返回結果如下:

    number_of_transaction 
    ----
    545591
  • 資料中標識為欺詐交易的數量。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)
        WHERE n.isfraud = 1
        RETURN count(*)
    $$) as (number_of_fraud_transaction agtype);

    返回結果如下:

    number_of_fraud_transaction 
    ----
     18919
  • 統計邊數量。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH ()-[r]->()
        RETURN count(*)
    $$) as (number_of_edge agtype);

    返回結果如下:

    number
    ------
     2131254

過濾查詢、排序查詢

查詢ID為2988706的交易資訊及所有的關聯資訊。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)
    WHERE n.transactionid = 2988706
    RETURN v
$$) as (e agtype);

返回結果如下:

    e
---------
 {"id": 2251799813685249, "label": "addr2", "properties": {"addr2": "87.0"}}::vertex
 {"id": 2533274790395906, "label": "emaildomain", "properties": {"emaildomain": "gmail.com"}}::vertex
 {"id": 2533274790395906, "label": "emaildomain", "properties": {"emaildomain": "gmail.com"}}::vertex
 {"id": 1970324836974595, "label": "addr1", "properties": {"addr1": "325.0"}}::vertex
 {"id": 1125899906844295, "label": "product", "properties": {"productid": 137934}}::vertex

在視覺化檢視age-viewer中使用該SQL預覽結果如下:

image

通用情境

K階鄰居

K階鄰居(k-nearest neighbors,KNN)方法利用資料節點之間的相似性,來識別潛在的欺詐行為。K階鄰居可以協助評價資料節點之間的相似性。通過尋找與該資料節點特徵相似的K個鄰居,可以判斷該節點是否正常。例如,一個交易如果與大多數鄰居(交易節點)在金額、地點、時間等特徵上存在顯著差異,可能會被標記為可疑。

查詢和交易記錄2988706有相同地址的其他交易(2階鄰居)記錄。後續可以根據這些鄰居的相關資訊來對這個交易的可疑性進行判別。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[:transaction_addr1]->(:addr1)<-[:transaction_addr1]-(t:transaction)
    WHERE n.transactionid = 2988706
    RETURN t
$$) as (e agtype);

返回結果如下:

  e
-----
 {"id": 844424930131972, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987001}}::vertex
 {"id": 844424930131978, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987007}}::vertex
 {"id": 844424930132041, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987070}}::vertex
 {"id": 844424930132053, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987082}}::vertex
....

在視覺化檢視age-viewer中使用以下SQL預覽結果:

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r:transaction_addr1]->(a:addr1)<-[r2:transaction_addr1]-(t:transaction)
    WHERE n.transactionid = 2988706
    RETURN [n,r,a,r2,t]::path
    LIMIT 50
$$) as (e agtype);
說明

僅返回50條記錄,過多的資料量可能導致前端系統崩潰。

image

路徑檢索

在欺詐交易識別中,路徑的概念主要體現在利用圖論和網路分析的方法來識別潛在的欺詐行為。在金融交易和網路中,交易往往可以視為一個圖,節點代表賬戶或客戶,邊代表交易活動。通過分析這個圖的結構,可以發現一些異常模式和欺詐行為。通過計算交易之間的路徑,可以快速識別從一個可疑賬戶到其他賬戶的交易鏈,有助於揭示看似不相關的賬戶之間的隱秘聯絡。

查詢交易記錄2987000和交易記錄2987172基於某個虛假交易的關聯路徑,從兩個看似沒有直接關係的交易中找出關聯的所有虛假交易鏈路。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)<-[r1]-(t:transaction)-[r2]->(v2)<-[r3]-(k:transaction)
    WHERE n.transactionid = 2987000 
        and k.transactionid=2987172
        and t.isfraud = 1
    RETURN t 
$$) as (e agtype);

返回結果如下:

 e
----
 {"id": 844424930618281, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3473312}}::vertex
 {"id": 844424930626886, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3481917}}::vertex
 {"id": 844424930649640, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3504671}}::vertex
 {"id": 844424930631805, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3486836}}::vertex
 {"id": 844424930641980, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3497011}}::vertex
 {"id": 844424930644942, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3499973}}::vertex

在視覺化檢視age-viewer中使用以下SQL預覽結果:

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)<-[r1]-(t:transaction)-[r2]->(v2)<-[r3]-(k:transaction)
    WHERE n.transactionid = 2987000 
        and k.transactionid=2987172
        and t.isfraud = 1
    RETURN [n,r,v,r1,t,r2,v2,r3,k]::path
    LIMIT 50
$$) as (e agtype);
說明

僅返回50條記錄,過多的資料量可能導致前端系統崩潰。

image

共同鄰居

在欺詐交易識別中,共同鄰居判斷(Common Neighbor Judgment)是一種基於社交網路或交易網路分析的技術,用於識別可能的欺詐行為。該方法主要基於圖論的基礎,通過分析交易參與者之間的關係網路,尋找可能的可疑交易模式。如果兩個交易有多個共同鄰居,而這些鄰居在交易行為上存在異常(例如高頻率的交易、異常金額等),則這兩個交易者之間的交易可能存在欺詐風險。

查詢交易記錄2987000和交易記錄2987172的共同鄰居,從而找到具備相同屬性(地址、裝置)的交易記錄。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[]->(v)<-[]-(t:transaction)
    WHERE n.transactionid = 2987000 and t.transactionid=2987172
    RETURN v
$$) as (e agtype);

返回結果如下:

  e
-----
 {"id": 2251799813685249, "label": "addr2", "properties": {"addr2": "87.0"}}::vertex
 {"id": 1970324836974594, "label": "addr1", "properties": {"addr1": "315.0"}}::vertex

在視覺化檢視age-viewer中使用該SQL預覽結果如下:

image

Jaccard相似性

Jaccard相似性用于衡量兩個集合之間的相似性,其公式為:

其中:

  • J(A,B)是集合A和B的Jaccard相似性。

  • ∣A∩B∣是集合A和B的交集。

  • ∣A∪B∣是集合A和B的並集。

在欺詐識別中,Jaccard相似性可以有多種用途,如:

  • 模式識別:欺詐行為往往表現出一些共性特徵。通過計算使用者交易或行為的Jaccard相似性,可以識別出相似的交易模式,從而協助檢測潛在的欺詐活動;

  • 客戶群體分析:在分析客戶的行為時,可以利用Jaccard相似性來比較不同客戶之間的相似性。相似性較高的客戶可能存在相似的風險特徵。

在本樣本中,兩個交易可以根據其關聯的地址,郵箱以及地址等資訊計算其Jaccard相似性,如果關聯的資訊重疊度很大,則說明交易的相似性很高。

  1. 建立以下函數。

    • 用於擷取特定交易的所有關聯節點,返回所有點的ID數組。

      CREATE OR REPLACE FUNCTION find_ids(transactionid integer)
      RETURNS bigint[]
      LANGUAGE plpgsql
      AS $function$
      DECLARE 
          sql VARCHAR;
          ids bigint[];
      BEGIN
        sql := 'SELECT array_agg(cast(e as bigint)) FROM (
      SELECT *
      FROM cypher(''fraud_graph'', $$
      MATCH (n:transaction)-[]->(v)
          WHERE n.transactionid = ' || text($1) || 
      'RETURN id(v) 
      $$) as (e agtype)) as t;';
          EXECUTE sql INTO ids;
          return ids;
      END
      $function$;
    • 兩個函數,用於對數組進行UnionIntersection的操作。

      CREATE OR REPLACE FUNCTION array_union(anyarray, anyarray)
      RETURNS anyarray 
      LANGUAGE sql immutable 
      AS $$
      SELECT array_agg(a ORDER BY a)
      FROM (
          SELECT DISTINCT unnest($1 || $2) AS a
      ) s;
      $$;
      
      CREATE OR REPLACE FUNCTION array_intersection(anyarray, anyarray)
      RETURNS anyarray 
      LANGUAGE sql immutable 
      AS $$
      SELECT array_agg(e)
      FROM (
          SELECT unnest($1)
          INTERSECT
          SELECT unnest($2)
      ) AS dt(e)
      $$;
    • 用於計算兩個交易的Jaccard相似性。

      CREATE OR REPLACE FUNCTION jaccardSimilarity(tid1 integer, tid2 integer)
      RETURNS float8
      LANGUAGE plpgsql
      AS $function$
      DECLARE 
          sql VARCHAR;
          ids1 bigint[];
          ids2 bigint[];
          union_list bigint[];
          intersection_list bigint[];
      BEGIN
        ids1 = find_ids($1);
        ids2 = find_ids($2);
        union_list = array_union(ids1, ids2); -- union
        intersection_list = array_intersection(ids1, ids2); -- intersection
        RETURN 
          CASE 
              WHEN array_length(union_list,1) = 0 THEN 0
              ELSE array_length(intersection_list,1) * 1.0/ array_length(union_list,1) 
          END AS jaccardSimilarity;
      END
      $function$;
  2. 執行函數進行Jaccard相似性計算。

    • 對指定的兩個交易ID進行相似性對比:

      SELECT jaccardSimilarity(2987000, 2987172);

      返回結果如下:

      jaccardsimilarity 
      ----
       0.4
  3. 如果需要對所有的交易進行相似性對比,可以充分利用PolarDB的預存程序能力,完成更為複雜的相似性計算任務,從而滿足檢測欺詐交易的需求。

    例如,可以使用以下SQL找出與交易2987002具有相同地址1、地址2和郵箱網域名稱的所有交易並按照jaccard相似性進行排序,找出相似性最大的50個交易:

    WITH tmp AS
    (SELECT cast(e as integer) as transactionid
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)-[:transaction_addr2]->(:addr2)<-[:transaction_addr2]-(t:transaction)
        WHERE n.transactionid = 2987002
        MATCH (n:transaction)-[:transaction_addr1]->(:addr1)<-[:transaction_addr1]-(t:transaction)
        WHERE n.transactionid = 2987002 
        MATCH (n:transaction)-[:transaction_emaildomain_p]->(:emaildomain)<-[:transaction_emaildomain_p]-(t:transaction)
        WHERE n.transactionid = 2987002 
        RETURN t.transactionid
    $$) as (e agtype) )
    SELECT transactionid, jaccardSimilarity(2987002, transactionid) as jaccardSimilarity
    FROM tmp
    ORDER by jaccardSimilarity DESC
    LIMIT 50;

    返回結果如下:

     transactionid | jaccardsimilarity 
    ---------------+-------------------
           3323911 |               0.6
           3328911 |               0.6
           3009043 |               0.6
           3039416 |               0.6
           3039425 |               0.6
           2993652 |               0.6
           3045027 |               0.6
           3037644 |               0.6
           3045041 |               0.6
    ...

    在視覺化檢視age-viewer中使用以下SQL預覽結果如下(結果已在之前的計算中完成,現僅取前10項。如有需要,您可以自行將其放入list中):

    SELECT * 
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)-[r]->(v)<-[r2]-(t:transaction)
        WHERE n.transactionid = 2987002
            AND t.transactionid IN [3323911, 3328911,3009043,3039416,3039425,2993652,3045027,3037644,3045041,3045049,3045279]
        RETURN [n,r,v,r2,t]::path
    $$) as (e agtype);

    image

總結

利用PolarDB PostgreSQL版的圖分析能力進行圖資料分析。PolarDB結合AGE擴充,提供圖資料計算分析的功能,包括使用Cypher查詢語言,高效處理查詢圖資料,為企業的統一資料管理和分析,提供強有力的支撐。

試用體驗

您可以訪問PolarDB免費試用頁面,選擇試用“雲原生資料庫PolarDB PostgreSQL版”,體驗GanosBase的圖計算能力。