iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Build on AWS

AWS架構師的自我修養:30天雲端系統思維實戰指南系列 第 22

Day 11-8 | 資料庫設計哲學:需求解析、技術選型與 Schema 設計策略(八) - 核心設計策略AWS實戰解析:圖資料庫設計思維 - 以Uber Eats Neptune架構為例

  • 分享至 

  • xImage
  •  

8. 圖資料庫設計思維

最後的最後,我們來談談一種有意思的資料庫設計,圖(graph database,GDB)

如果說 SQL 資料庫是把世界整理成一張張整齊的 Excel 表格,設計的核心是「正規化」,目標是資料的完整性與無冗餘,是「為資料本身而設計」。那麼圖資料庫,就是直接把世界的「關係網絡」本身給存了下來 「為你想問的問題而設計」。它的設計思維,更接近我們大腦的直覺,我們必須像個記者,不斷追問: 根據這個資料節點,我們未來最想知道的是什麼?

在圖資料庫的世界裡,「關係」不再是需要透過 JOIN 操作才能間接找到的東西,它本身就是一等公民,和「實體」一樣重要。這讓它在處理高度連接的資料時,擁有無與倫比的優勢。也正因為如此這個資料庫設計結構特別適合 情境(Domain)的連續

關係導向的思考方式

- 不是「這個實體有什麼屬性」
- 而是「這些實體間有什麼關係」

忘掉表格、欄位和主鍵。我們需要用一種新的眼光來看待資料 - 關聯

一個圖資料表最重要的有三個概念: 節點 (Nodes)-邊 (Edges / Relationships)-屬性 (Properties)

  • 節點 (Nodes) - 實體

    • 這就是我們的「名詞」,跟我們之前在 ERD(事件驅動) 中學到的「實體」很像。
    • 例如:人 (Person)、電影 (Movie)、公司 (Company)、帳戶 (Account)。
    • 節點可以有標籤 (Labels) 來分類。一個節點可以有多個標籤,這非常靈活。例如,一個人可以同時是 Person 和 Actor。
  • 邊 (Edges / Relationships) - 關係

    • 這是圖資料庫的靈魂!邊連接了節點才有 意義 ,代表它們之間的 「行為(Conduct)」
    • 邊是 有向 的:(A)-[關係]->(B)(B)-[關係]->(A) 是不同的。
    • 邊是有 類型 的:例如,[:ACTED_IN] (出演了)、[:DIRECTED] (導演了)、[:FRIENDS_WITH] (是...的朋友)。
    • 邊可以有屬性:這是它比 SQL 中間表更強大的地方。例如,在 (Tom Hanks)-[:ACTED_IN]->(Forrest Gump) 這條邊上,可以加上屬性 {role: "Forrest Gump"}。在 (UserA)-[:PURCHASED]->(ProductB) 這條邊上,可以加上屬性 {date: "2025-09-15", rating: 5}。
  • 屬性 (Properties) - 描述

    • 節點和邊都可以有自己的屬性,以鍵值對 (Key-Value) 的形式存在。
    • 例如,Person 節點可以有 {name: "Tom Hanks", born: 1956}。

    當我們遇到的問題,可以被描述為 「尋找...的路徑」「分析...的關聯」「誰是...的中心」「這個群體有什麼特徵」,等具有明顯 指標性關連性 時,就應該把「圖資料庫」這個強大的工具納入我們的考量範圍。

    設計思維的轉變:

    在 SQL 中,我們想知道「A 的朋友的朋友」,我們需要把使用者表格自己跟自己 JOIN 兩次,效能會隨著資料量和關聯深度( 朋友的朋友的朋友... * N )急遽下降。在圖資料庫中,這個問題變成了「從 A 節點出發,沿著 FRIENDS_WITH 的邊走兩步,看看能到達哪些人」。這個操作對圖資料庫來說是原生且極度高效的,我們不再需要為了正規化而建立大量的「中間表」或「關聯表」。多對多的關係,在圖中就是一條直接的邊,非常直觀!

  過去需要 JOIN {N} 個表格才能得到的資訊,現在可能只需要一條帶有豐富屬性的邊就能描述。

了解了設計思維,我們就會發現,圖資料庫特別適合那些「關係」比「實體」本身更重要的場景。

**常見應用場景:社交網路/金融風控/推薦系統/網路與 IT 維運 **

  1. 社交網絡 (Social Networks) - 經典場景
  • 模型: (Person)-[:FRIENDS_WITH]->(Person)(Person)-[:LIKES]->(Post)(Person)-[:MEMBER_OF]->(Group)

  • 應用:

    • 好友推薦: 「尋找我朋友的朋友,但我們還不是朋友的人」。這是一個簡單的兩步遍歷。
    • 影響力分析: 在網絡中,誰是關鍵意見領袖 (KOL)?(誰的連接最多,或在網絡中心)。
    • 資訊傳播路徑: 一則假新聞是如何在人群中傳播開來的?
  1. 金融風控與反詐欺 (Fraud Detection)
  • 模型: (Person)-[:HAS_ACCOUNT]->(BankAccount)(Person)-[:USES_DEVICE]->(Device)(BankAccount)-[:SENT_TO]->(BankAccount)
  • 應用:
    • 詐欺團夥識別: 幾個看似無關的帳戶,是否都使用了同一個設備或 IP 地址進行註冊或登入?是否在短時間內互相轉帳,形成一個不尋常的閉環?
    • 洗錢模式分析: 資金是否通過多層複雜的轉帳路徑,最終又回到了源頭附近?圖的環路檢測演算法能輕易發現這種模式。
  1. 推薦引擎 (Recommendation Engines)
  • 模型: (Customer)-[:PURCHASED]->(Product)(Customer)-[:VIEWED]->(Product)(Product)-[:IN_CATEGORY]->(Category)
  • 應用:
    • 協同過濾: 「購買了商品 A 的顧客,還購買了哪些其他商品?」圖資料庫可以快速找到 (CustomerA)-[:PURCHASED]->(ProductA)<-[:PURCHASED]-(CustomerB)-[:PURCHASED]->(ProductB) 這樣的路徑,然後將 ProductB 推薦給 CustomerA。
    • 基於內容的推薦: 「推薦給這個用戶和他過去喜歡的電影有相同演員或導演的其他電影」。
  1. 網路與 IT 維運 (Network and IT Operations)
  • 模型: (Server)-[:CONNECTED_TO]->(Switch)(Application)-[:RUNS_ON]->(Server)(Database)-[:DEPENDS_ON]->(Server)
  • 應用:
    • 根因分析 (Root Cause Analysis): 當一個應用程式變慢時,它依賴的資料庫、伺服器、網路交換機的狀態是什麼?圖可以讓我們快速看到整條依賴鏈,找到問題的根源。

我們來視覺化看看關於政黨傾向集群網絡分析系統

graph TD
    %% --- 定義區塊 ---
    subgraph "個人 (Individuals)"
        P1(個人A)
        P2(個人B)
        P3(個人C)
        P4(個人D)
    end

    subgraph "政黨 (Political Parties)"
        style PartyA fill:#89cff0,stroke:#333,stroke-width:2px
        style PartyB fill:#ffb3ba,stroke:#333,stroke-width:2px
        PartyA(進步黨)
        PartyB(保守黨)
    end

    subgraph "個人特質 (Traits)"
        subgraph "核心信仰"
            BeliefX(重視社會公平)
            BeliefY(重視傳統價值)
        end
        subgraph "教育程度"
            Edu1(大學)
            Edu2(碩士/博士)
        end
        subgraph "年收入 (萬)"
            Inc1(50-100)
            Inc2(150-300)
        end
        subgraph "職業"
            Job1(科技業/工程師)
            Job2(金融業/律師)
            Job3(教育/公部門)
        end
    end

    %% --- 關係連結 ---

    %% 個人A & B -> 歸屬於進步黨
    P1 -->|屬於| PartyA
    P2 -->|屬於| PartyA

    %% 個人C & D -> 歸屬於保守黨
    P3 -->|屬於| PartyB
    P4 -->|屬於| PartyB

    %% --- 描繪每個人的特質網絡 ---

    %% 個人A: 高學歷、高收入、金融業、重視社會公平
    P1 -->|持有| BeliefX
    P1 -->|學歷| Edu2
    P1 -->|年收| Inc2
    P1 -->|職業| Job2

    %% 個人B: 大學學歷、中等收入、科技業、重視社會公平
    P2 -->|持有| BeliefX
    P2 -->|學歷| Edu1
    P2 -->|年收| Inc1
    P2 -->|職業| Job1

    %% 個人C: 大學學歷、中等收入、教育工作者、重視傳統價值
    P3 -->|持有| BeliefY
    P3 -->|學歷| Edu1
    P3 -->|年收| Inc1
    P3 -->|職業| Job3

    %% 個人D: 高學歷、高收入、金融業、重視傳統價值
    P4 -->|持有| BeliefY
    P4 -->|學歷| Edu2
    P4 -->|年收| Inc2
    P4 -->|職業| Job2

在這個資料結構中,我們可以很簡單的發現幾個要點:

  1. 引力中心與集群:
  • 進步黨 和 保守黨 像兩個引力中心,將各自的支持者(P1, P2 和 P3, P4)拉向自己。
  • 重視社會公平 (BeliefX) 成為一個次級中心,將 P1 和 P2 緊密地連接在一起。同樣地,重視傳統價值 (BeliefY) 也將 P3 和 P4 綁定。
  1. 橋接節點 (Bridge Nodes):
  • 金融/法律 (Job2)、碩士/博士 (Edu2) 和 高年收 (Inc2) 這三個節點變得極為關鍵。它們像一座座橋樑,直接連接了分屬不同政黨的 P1 和 P4。
  • 在視覺上,您會看到 P1 和 P4 雖然被各自的政黨和信仰拉開,但又被這三個共享的社經背景特質強力地拉近。這生動地展示了他們之間「既對立又相似」的複雜關係。
  1. 網絡密度與社群邊界:
  • 在 P1 和 P2 之間,以及 P3 和 P4 之間,連線非常密集,形成了兩個清晰的社群。
  • 而社群之間的連結(如 P1 和 P4 的連結)則相對稀疏,這也幫助我們識別社群的邊界。

發現了嗎? 這是圖資料庫效用與威力最大的地方,根據 邊(關係) 我們可以輕而易舉的解讀資料意涵,我們不再需要思考如何透過中間表來「模擬」關係,而是可以直接地「描述」關係。圖資料庫,就是將人類在白板上描繪複雜關係的直觀思考過程,直接轉化為一種可儲存、可查詢的資料結構。它是一種對現實世界網絡關係的直接映射,而非表格化的抽象。我們在應用上的目標是設計一個「路網」,讓我們的核心查詢能像在高速公路上開車一樣順暢。不斷思考如何用最少的「步數」(遍歷)來回答我們的問題,並據此來調整資料結構中的節點、邊和屬性。

在應用上,當出現 關係優先 (Relationship-First) 的關鍵字時,他就代表著這個常態資料取用的情境是 關注情境(Domain)的連鎖 ,這時候就特別適合用圖思維去進行資料庫設計。

這時候其實我們其實發現了一件事,在這個 查找需求絕對大於寫入需求 的情境中,是不是很符合 CQRS(讀寫分離) 的應用? 我們是不是可以利用這個盡可能減少 查找 步數的設計脈絡,來盡可能地放大我讀取效能?

絕對可以,而且這正是許多資深架構師在處理高效能系統時,腦中所運行的「心法」之一。

這觸及到一個核心觀念:圖資料庫的設計思維,本質上就是一種極致的、以查詢為導向的「反正規化」哲學。

以下我們用 Uber Eats 來進行實戰範例

AWS 實現 : Uber Eats 的餐點推薦關係網絡推導系統

當我們打開 Uber Eats App 時,後端會執行類似這樣的查詢:

  • 點過 A(可能是我們上一次點的達美樂 Pizza) 的人還點了什麼?
  • 我們(當前帳戶資訊)可能喜歡的菜系

我們先依循 情境脈絡 來逐步想想我們可以怎麼設計節點 (Nodes)與邊 (Edges / Relationships)

點過 A(達美樂 Pizza + 百事可樂 真的很推) 的人還點了什麼?

  1. 找到我們 ( User A ) 最近訂購過的餐廳 (Restaurant X )
  2. 找出所有也訂購過 Restaurant X 的其他用戶 (User B, User C)
  3. 遍歷這些用戶還訂購過哪些我們從未訂購過的餐廳 ( Restaurant Y, Restaurant Z)
  4. Restaurant YRestaurant Z 作為推薦結果
  5. 因為 Restaurant Z 有付錢購買企業推送方案,所以將 Restaurant Z 放置到推薦結果列表首位(index=0)

我們( User A )可能喜歡的菜系

  1. 找到我們最常訂購的幾種菜系 (Cuisine A, Cuisine B)
  2. 透過情境關聯進行查找(地理位置 / 菜系連鎖)
    1. 在我們的地理位置附近,找出其他也提供這些菜系的餐廳Restaurant W
    2. 找出和 Cuisine A 經常被一同喜愛的 Cuisine C,並推薦提供 Cuisine C 的餐廳 (ex: 美系餐廳經常被推送墨系餐廳)

但在 Uber Eats 這樣的平台上,有數百萬的用戶、數十萬的餐廳和數千萬的餐點,至少必須被滿足上述兩種情境,更別說來還有像是常見的「附近最熱門的餐廳」或「最多人點的餐點」

單單只有 General 的查找結果是無法滿足個人化推送的行銷利益最大化需求的,就像是:

  • 「推薦給這位用戶一些他 可能喜歡但從未點過 的餐廳。」
  • 「喜歡 『日式拉麵』『麻辣鍋』 的用戶,通常還會喜歡 哪種菜系 ?」
  • 「找出和這位用戶 口味相似另一群人,看看他們最近點了什麼 新東西 。」

這些問題的共同點是,它們都極度依賴 「關係」 ——用戶與餐廳的關係、用戶與菜系的關係、用戶與用戶之間的隱含關係。用傳統 SQL 來處理這些問題,會需要極其複雜且緩慢的 JOIN 查詢,根本無法滿足即時推薦的需求。同時,為了這樣子的需求特別根據每種不同情境去設計一個預測模型也是成本-效益極大不符合比例原則的。

那麼我們來進行我們的 圖譜模型設計 (The Blueprint)

  • 節點 (Nodes):

    • User (用戶):屬性包含用戶 ID、地理位置等。
    • Restaurant (餐廳):屬性包含餐廳名稱、地址、菜系標籤。
    • MenuItem (餐點):屬性包含餐點名稱、價格、食材。
    • Cuisine (菜系):例如「日式」、「義式」、「川菜」。
    • Ingredient (食材):例如「牛肉」、「起司」、「辣椒」。
  • 邊 (Edges / Relationships):

    • (User) -[ :ORDERED {date, rating} ]-> (Restaurant):用戶訂購過某餐廳,邊上可以有日期和評分等屬性。
    • (User) -[ :FAVORITED ]-> (Restaurant):用戶收藏了某餐廳。
    • (Restaurant) -[ :SERVES ]-> (Cuisine):餐廳提供某種菜系。
    • (MenuItem) -[ :CONTAINS ]-> (Ingredient):餐點包含某種食材。
graph TD
    subgraph "使用者端 (Client)"
        UserDevice[📱<br>使用者手機 App]
    end

    subgraph "AWS 雲端架構"
        APIGateway[🌐<br>API Gateway]

        subgraph "即時交易路徑 (Real-time Transaction Path)"
            LambdaOrder[λ<br>下單/評分 Lambda]
            DynamoDB[(🗄️<br>DynamoDB)]
        end

        subgraph "非同步圖譜更新路徑 (Async Graph Update Path)"
            Kinesis[🌊<br>Kinesis Data Streams]
            LambdaGraph[λ<br>圖譜更新 Lambda]
            Neptune[g<br>Amazon Neptune]
        end

        subgraph "推薦查詢路徑 (Recommendation Query Path)"
            LambdaRecommend[λ<br>推薦服務 Lambda]
        end
    end

    %% --- 資料流定義 ---
    UserDevice -- "1. 下單/評分請求" --> APIGateway
    APIGateway -- "2. 觸發" --> LambdaOrder
    LambdaOrder -- "3a. 寫入訂單/評分" --> DynamoDB
    LambdaOrder -- "3b. 發送事件" --> Kinesis
    Kinesis -- "4. 觸發" --> LambdaGraph
    LambdaGraph -- "5. 更新圖譜資料" --> Neptune

    UserDevice -- "6. 請求推薦" --> APIGateway
    APIGateway -- "7. 觸發" --> LambdaRecommend
    LambdaRecommend -- "8. 查詢圖譜" --> Neptune
    Neptune -- "9. 返回推薦 ID" --> LambdaRecommend
    LambdaRecommend -- "10. (可選)從 DynamoDB 獲取細節" --> DynamoDB
    LambdaRecommend -- "11. 返回推薦結果" --> UserDevice

    %% --- 樣式 ---
    style Neptune fill:#ff9900,stroke:#333,stroke-width:2px
    style DynamoDB fill:#2c73d2,stroke:#333
    style Kinesis fill:#c0392b,stroke:#333

實戰模擬場景:為「小名 (User_A)」推薦他可能喜歡的新餐廳。

  1. 觸發推薦: 小名打開 App,滑到推薦區塊。手機 App 發送一個「請求推薦」的 API 請求 (步驟 6)。

  2. API 閘道與觸發: API Gateway 收到請求,驗證身分後,觸發 推薦服務 Lambda (LambdaRecommend) (步驟 7)。

  3. 核心圖查詢: LambdaRecommend 的核心任務是向 Amazon Neptune 發送一個圖查詢 (步驟 8)。這個查詢的邏輯是「協同過濾」:

  • "尋找與小名 (User_A) 口味相似的人,看看他們還喜歡什麼小名沒吃過的餐廳。"

用圖查詢的語言來描述這個邏輯:

  • a. 找到小名 (User_A) 訂購過的餐廳 (Rest_1 - 一蘭拉麵)。
  • b. 找到也訂購過 Rest_1 的其他人 (User_B - 小字)。
  • c. 看看小字還訂購過哪些餐廳 (Rest_3 - 瓦城)。
  • d. 檢查小名是否訂購過 Rest_3。發現沒有。
  • e. 因此,Rest_3 (瓦城) 成為一個強力的推薦候選。
  1. 返回結果: Neptune 高效地完成這個遍歷,將 Rest_3 的 ID 返回給 LambdaRecommend (步驟 9)。

  2. 豐富化資料 (可選): LambdaRecommend 拿到 Rest_3 的 ID 後,可能會再去 DynamoDB 查詢這家餐廳的詳細資訊,如完整名稱、地址、圖片 URL 等 (步驟 10)。DynamoDB 非常適合這種 Key-Value 形式的快速查找。

  3. 呈現給使用者: LambdaRecommend 將完整的推薦結果(包含餐廳名稱、圖片等)打包成 JSON,透過 API Gateway 返回給小名的手機 App (步驟 11)。小名於是看到了「瓦城」出現在他的推薦列表上。

這個推薦能夠成功,前提是圖譜裡的資料是即時的。這就是「非同步圖譜更新路徑」的作用。
資料如何進入圖譜?(非同步路徑)

  1. App 發送「下單請求」(步驟 1),API Gateway 觸發 下單 Lambda (步驟 2)。
  2. 下單 Lambda 會做兩件事:
  • 3a: 將訂單的詳細記錄寫入 DynamoDB,作為永久的交易存根。
  • 3b: 同時,發送一個簡化的事件,如 { "userId": "User_A", "restaurantId": "Rest_2", "action": "ORDER" } 到 Kinesis Data Streams。
  1. Kinesis 收到事件後,自動觸發 圖譜更新 Lambda (步驟 4)。
  2. 圖譜更新 Lambda 解析事件,並在 Neptune 中執行一條更新語句:找到 User_A 和 Rest_2 之間的 ORDERED 邊,將其 count 屬性加 1 (步驟 5)。

這個架構實現了完美的 讀寫分離關注點分離

  • 交易路徑 (DynamoDB) 快速、穩定,負責處理核心交易
  • 分析/推薦路徑 (Neptune) 專注於複雜的關係查詢,提供深度洞察
  • Kinesis 作為兩者之間的非同步橋樑,確保系統的彈性和解耦

當我們遇到的問題,可以被描述為「尋找...的路徑」、「分析...的關聯」、「誰是...的中心」、「這個群體有什麼特徵」,這些特別強調 讀取絕對大於寫入 且重視 情境(Domain)的連續 ,就應該把 「圖資料庫」 這個強大的工具納入我們的考量範圍。


上一篇
Day 11-7 | 資料庫設計哲學:需求解析、技術選型與 Schema 設計策略(七) - 核心設計策略AWS實戰解析:微服務資料庫模式
下一篇
Day 11-9 | 資料庫設計哲學:需求解析、技術選型與 Schema 設計策略(end) - 核心設計策略AWS實戰解析: DB設計策略的trade-off Guideline
系列文
AWS架構師的自我修養:30天雲端系統思維實戰指南26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言