進階閱讀時間 3 分鐘

DynamoDB Streams 完整指南(含範例)

DynamoDB Streams 是一個變更資料擷取(change-data-capture)日誌:一個表格上的 每一次插入、更新和刪除,都會被有序地擷取成一串你可以據以反應的記錄。 它是你不必輪詢一個表格就能把它變成一個事件來源的方法。

在那個稽核日誌的情境裡,你想在一個敏感事件落地的那一刻就反應 — 在有人匯出一張發票或授予一個管理員角色時觸發一個警報 — 而不必 定時掃描表格。Streams 就是那件事的推送端。

DynamoDB Streams 是怎麼運作的?

DynamoDB Streams 把一個表格上的每一次插入、更新和刪除,擷取成一個按時間排序、去重的記錄日誌,保留最長達 24 小時。你用 StreamViewType(鍵、新影像、舊影像,或兩者)選擇每一筆記錄攜帶什麼,然後用一個 Lambda 觸發器消費這個串流,不必輪詢就能對項目變更做出反應。

  • Streams 把項目層級的變更擷取成一個按時間排序、去重的日誌, 保留最長達 24 小時
  • 你選擇每一筆記錄攜帶什麼,透過 StreamViewType:只有鍵、新 影像、舊影像,或舊與新兩者。
  • 記錄是每個項目有序的 — 對一個項目的變更會以它們被寫入的順序 抵達 — 而一個串流會沿著和 相同的方式分片。
  • 原生的消費者是 Lambda — 一個對每一批新記錄執行的觸發器, 而 Kinesis Data Streams 則是需要更豐富扇出時的替代方案。

問題所在:不輪詢就反應

你需要「在一個 role.granted 事件被寫入時提醒我」。天真的做法是 一個每分鐘掃描新事件的排程工作 — 這每次都會讀取整個近期 partition、花費容量,而且永遠至少晚一分鐘。

你真正想要的是一個推送:DynamoDB 在一個項目變更的那一刻告訴 你。那正是 Streams 所提供的,變更記錄會被送到你的程式碼裡,而不是 你去追它。

Streams 如何運作

按照 AWS 的文件,DynamoDB Streams 為變更保留一個去重、按時間 排序的日誌,最長達 24 小時,並有原生的 Lambda 整合 (DynamoDB 的變更資料擷取)。 每一筆記錄描述一次項目層級的修改。

當你啟用一個串流時,你挑一個 StreamViewType,它控制每一筆記錄 攜帶多少變更後的項目:

StreamViewTypeeach record contains
KEYS_ONLYonly the key attributes of the changed item
NEW_IMAGEthe entire item as it looks after the change
OLD_IMAGEthe entire item as it looked before the change
NEW_AND_OLD_IMAGESboth the before and after images

記錄是每個項目有序的 — 對單一項目的變更以它們被寫入的順序 出現 — 而這個串流沿著和表格相同的 partition 結構分片。保留時間 是 24 小時 — Streams 是一個反應緩衝區,不是一份永久的歷史。要有 耐久的歷史,你就儲存那些事件本身(那正是我們的稽核日誌表格 本來就是的東西)。

原生的消費者是一個 Lambda 觸發器:DynamoDB 在新的串流記錄 抵達時,用一批它們來呼叫你的函數。

LambdaStream"DynamoDB"AppLambdaStream"DynamoDB"App"寫入 EVENT role.granted""變更記錄(NEW_IMAGE)""一批記錄""若動作屬敏感 →發出警示"

一個實作範例:對敏感的稽核事件發警報

稽核日誌表格獲得一個帶 NEW_IMAGE 的串流,所以每一筆記錄攜帶 完整的新事件。一個 Lambda 消費那一批,並只轉發那些 重要的記錄:

stream record (NEW_IMAGE)consumer action
TENANT#acmeEVENT#…#a2action=invoice.exportsend to SIEM
TENANT#globex EVENT#…#b9 action=role.grantedpage on-call
TENANT#acmeEVENT#…#a1action=login.successignore

這個函數從不碰觸表格 — 它純粹對串流交給它的東西做出反應。 沒有輪詢、沒有掃描,而且警報在寫入後幾秒內就觸發。串流 記錄是每個項目有序的,所以對同一個事件項目的接連變更會以 它們被寫入的順序抵達。

這也是維護一份下游副本的標準做法:一個串流 消費者可以把每一個事件投影進 OpenSearch 以做全文稽核搜尋,或 聚合計數 — 全都衍生自同一份變更日誌。

在 DynoTable 中操作

在你接上一個串流消費者之前,你需要知道你的 Lambda 將收到的 項目的確切樣貌 — 哪些屬性存在、巢狀的 map 和 list 長什麼樣、 一個 NEW_IMAGE 記錄實際會包含什麼。

要在純 JSON 和一個串流記錄所用的 attribute-value 樣貌之間轉換一個 樣本項目,DynamoDB JSON 轉換器 會在你的 瀏覽器裡完成。而在 DynoTable 裡,你可以檢視完整的項目 — 包括它的 DynamoDB-JSON 形式 — 這樣你就對著真實資料為 NEW_IMAGE 記錄建模, 而不是猜測欄位樣貌。

在 DynoTable 中檢視一個稽核事件項目,以便為它的 Lambda 消費者將收到的 NEW_IMAGE 串流記錄建模。
在 DynoTable 中檢視一個稽核事件項目,以便為它的 Lambda 消費者將收到的 NEW_IMAGE 串流記錄建模。

如果你在本地測試一個消費者,就對著 DynamoDB Local 執行這個表格並 以同樣的方式檢視它 — 見 連接到 DynamoDB Local

陷阱與後續步驟

  • 24 小時不是一個積壓佇列。 如果你的消費者停擺一天,記錄 就會老化並消失。Streams 是為了接近即時的反應,而非耐久的重播 — 歷史請保留那些事件本身。
  • 挑你需要的最小 StreamViewType NEW_AND_OLD_IMAGES 會讓 酬載加倍;如果你只需要那個鍵去重讀項目,KEYS_ONLY 更便宜。
  • 順序是每個項目的,不是每個 partition key 或全域的。 DynamoDB 只 對同一個項目的接連變更保證順序;跨不同項目之間沒有順序保證, 即使在同一個 partition key 內也是如此。
  • TTL 刪除會以串流記錄的形式出現,帶著那個系統屬性標記,這 就是你歸檔到期項目的方法 — 見 DynamoDB TTL

Streams 把稽核日誌變成一個事件來源。下一個操作上的顧慮是一個 項目生命週期的另一端 — 用 DynamoDB TTL 自動 讓舊事件到期。

下載 DynoTable,在你寫下一行 Lambda 程式碼之前,先 檢視你的串流消費者將收到的確切項目樣貌。

已更新