進階閱讀時間 3 分鐘

DynamoDB Streams

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

在稽核記錄的情境中,你希望在敏感事件落地的瞬間就做出反應 — 當有人匯出發票或授予管理員角色時觸發警示 — 而不必定時掃描表格。Streams 就是其中「推送」的那一側。

DynamoDB Streams 是如何運作的?

DynamoDB Streams 將表格上的每一次插入、更新與刪除,擷取為一份時間排序、去重的記錄,保留最長 24 小時。你透過 StreamViewType 選擇每筆記錄攜帶的內容(鍵、新影像、舊影像或兩者),再以 Lambda 觸發器消費 stream,即可在不輪詢的情況下對項目變更做出反應。

  • Streams 擷取項目層級的變更,形成一份時間排序、去重的記錄,保留最長 24 小時
  • 你透過 StreamViewType 選擇每筆記錄攜帶什麼:僅鍵、新影像、舊影像,或新舊兩者。
  • 記錄在單一 partition key 內是有序的,而 stream 的分片方式與表格相同。
  • 原生的消費端是 Lambda — 一個對每批新記錄執行的觸發器,而 Kinesis Data Streams 則是用於更豐富扇出的替代方案。

問題:不靠輪詢就能反應

你需要「當有 role.granted 事件被寫入時通知我」。最直覺的做法是一個排程工作,每分鐘掃描尋找新事件 — 但這每次都會讀取整個近期的分割、耗用容量,而且永遠至少慢一分鐘。

你真正想要的是一個推送:DynamoDB 在某項目變更的瞬間告訴你。這正是 Streams 提供的,變更記錄會被送到你的程式碼裡,而不是你去翻找它。

Streams 如何運作

根據 AWS 文件,DynamoDB Streams 儲存一份「最長 24 小時、去重且時間排序的變更記錄」,並原生整合 Lambda (DynamoDB 的變更資料擷取)。每筆記錄描述一次項目層級的修改。

啟用 stream 時你會挑選一個 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 key 內是有序的,而 stream 沿著與表格相同的分割結構分片。保留期為 24 小時 — Streams 是一個反應緩衝區,而非永久歷史。若要持久的歷史,你就儲存事件本身(這正是我們的 audit-log 表格已經在做的事)。

原生的消費端是一個 Lambda 觸發器:DynamoDB 會在新的 stream 記錄抵達時,以一批記錄叫用你的函式。

LambdaStream"DynamoDB"AppLambdaStream"DynamoDB"App"Put EVENT role.granted""change record (NEW_IMAGE)""batch of records""if action is sensitive →alert"

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

audit-log 表格設定了一個 NEW_IMAGE 的 stream,所以每筆記錄都攜帶完整的新事件。一個 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

該函式從不碰觸表格 — 它純粹對 stream 交給它的內容做反應。沒有輪詢、沒有掃描,而警示在寫入後數秒內就觸發。因為記錄在每個 partition key 內是有序的,某個租戶的所有事件都會依其被寫入的順序抵達。

這也是維護下游副本的標準做法:一個 stream 消費端可以把每個事件投影到 OpenSearch 以進行全文稽核搜尋,或彙總計數 — 全都衍生自同一份變更記錄。

在 DynoTable 中實作

在你接上一個 stream 消費端之前,你需要知道 Lambda 將收到的項目的確切結構 — 有哪些屬性、巢狀的 map 與 list 長什麼樣、一筆 NEW_IMAGE 記錄實際會包含什麼。

若要在純 JSON 與 stream 記錄所用的屬性值結構之間轉換一個範例項目,DynamoDB JSON 轉換器 會在你的瀏覽器中完成。而在 DynoTable 中,你可以檢視完整的項目 — 包括它的 DynamoDB-JSON 形式 — 從而對著真實資料建模 NEW_IMAGE 記錄,而不必猜測欄位結構。

在 DynoTable 中檢視一個稽核事件項目,以對其 Lambda 消費端將收到的 NEW_IMAGE stream 記錄建模。
在 DynoTable 中檢視一個稽核事件項目,以對其 Lambda 消費端將收到的 NEW_IMAGE stream 記錄建模。

如果你在本機測試消費端,就對著 DynamoDB Local 執行該表格,並以相同方式檢視它 — 請見 連接 DynamoDB Local

陷阱與後續步驟

  • 24 小時不是待辦佇列。 若你的消費端停擺了一天,記錄就會逾時消失。Streams 用於近即時反應,而非持久重播 — 歷史請保留事件本身。
  • 挑選你所需最小的 StreamViewType NEW_AND_OLD_IMAGES 會讓酬載加倍;若你只需要鍵以便回去重讀項目,KEYS_ONLY 較便宜。
  • 排序是每 partition key 的,不是全域的。 兩個不同租戶的事件之間沒有排序保證 — 只在單一租戶的分割之內才有。
  • TTL 刪除會以 stream 記錄的形式出現,並帶有系統屬性標記,這就是你封存到期項目的方式 — 請見 DynamoDB TTL

Streams 把稽核記錄變成一個事件來源。下一項維運考量是項目生命的另一端 — 用 DynamoDB TTL 自動讓舊事件到期。

下載 DynoTable 來檢視你的 stream 消費端將收到的確切項目結構,再動筆寫任何 Lambda 程式碼。

已更新