入門閱讀時間 5 分鐘

如何把 DynamoDB 表匯出為 CSV(4 種方法)

DynamoDB 沒有原生的"匯出為 CSV"按鈕。每個值回來時都裹在 DynamoDB 的 marshalled JSON裡——{"S": "..."}{"N": "123"}{"M": {...}}——而一張表可以裝著巢狀的 map、list 和 set,沒有一目瞭然的扁平列表示。所以"把 DynamoDB 匯出為 CSV"其實是兩個問題:把項取出來,然後把帶型別的 JSON 壓平成行。無論主控台還是託管匯出,都不會替你做第二步。

本指南先講兩步都替你做完的方式,再覆蓋三條 AWS 工具鏈路線,以及各自何時才是正確選擇。

如何把一張 DynamoDB 表匯出為 CSV?

最快的方式:在 DynoTable 裡開啟表,篩選到你要的行,一鍵把結果匯出為 CSV——型別描述符已解開、巢狀值已替你壓平(方法 1)。改用 AWS 工具自己搭的話:小表用 CLI 掃描再用 jq 重塑(方法 2),大表用託管的 S3 匯出(方法 3),需要自定義整形時寫一段短指令碼(方法 4)。

  • 篩選過 / 整形過的 CSV(一部分列、只要某些項):圖形介面匯出(方法 1)或指令碼。託管的 S3 匯出給你的是_整張_表,不帶篩選。
  • 小表、臨時需求、只有終端:AWS CLI scan + jq方法 2)。巢狀屬性一出現就不夠用了。
  • 大表(GB 級以上):DynamoDB 匯出到 S3方法 3),然後轉換那份轉儲。它非同步執行且不消耗讀取容量——但它輸出的是 DynamoDB JSON,不是 CSV。

方法 1:DynoTable 中的一鍵匯出

DynoTable 把匯出當作瀏覽的一部分:執行或篩選一個查詢,按 ⌘⇧E(或標籤頁工具欄裡的 Export(匯出)按鈕),然後挑選要輸出什麼:

  • 格式:CSV(每個項一行,帶表頭——set 在單元格內序列化為 JSON 陣列)、JSONNDJSON。JSON/NDJSON 可以是解開後的純形式(unmarshalled,直接 "count": 3),也可以是 marshalled DynamoDB-JSON,用於需要保留大數字的無損往返。
  • 範圍:當前已載入的行、只是你的選擇,或完整的篩選匹配——你的查詢匹配到的每一個項,直接從 DynamoDB 流式取出,而不只是螢幕上那些。最後這個正是託管 S3 快照做不到的篩選匯出。
  • 目的地:剪貼簿或檔案。要一次性抓取,右鍵點一行然後 Copy as…(複製為…)就能把 CSV、JSON、NDJSON 或 DynamoDB-JSON 直接放上剪貼簿,不用彈窗。
DynoTable 的匯出對話方塊:格式(CSV / JSON / NDJSON)、從選擇到完整篩選匹配的範圍,以及剪貼簿或檔案目的地。
DynoTable 的匯出對話方塊:格式(CSV / JSON / NDJSON)、從選擇到完整篩選匹配的範圍,以及剪貼簿或檔案目的地。

那些擊穿下面各條 DIY 路線的壓平問題都已被處理:型別描述符被解開、巢狀的 map 和 list 被壓平、重新命名後的列會流入 CSV 表頭。大型匯出會脫離頁面在後臺執行——逐行流式寫到磁碟,扛得住切換標籤頁甚至應用過載——所以一次幾 GB 的匯出永遠不必塞進記憶體。

它是一個桌面 DynamoDB 用戶端,就是你已經在用來瀏覽表的那個工具;看看它與其他 DynamoDB 圖形介面的對比。什麼時候_不_該用它?當匯出必須在流水線裡無人值守地執行時——那是指令碼路線(方法 4)的地盤。

方法 2:AWS CLI scan + jq

對小表,你可以掃描它並用 jq 重塑輸出。一次 Scan 讀取表中的每一個項,按最多 1 MB 一頁返回;CLI 會自動替你跟完分頁(AWS 文件:掃描表)。

aws dynamodb scan --table-name MyTable --output json \
  | jq -r '.Items[] | [.id.S, .name.S, .price.N] | @csv' \
  > out.csv

坑就在那行 jq 裡:你得手寫 .id.S.name.S.price.N——伸手越過每個屬性的型別描述符SNBBOOLMLSSNSBS)去拿原始值。對一張只有三個字串列的扁平表還應付得來。但一旦出現下面這些就散架了:

  • 巢狀的 map/list——{"M": {...}}{"L": [...]} 沒有可以壓進去的單一列;@csv 會噎住,否則就得你手動把單元格 JSON 編碼。
  • Set——{"SS": ["a","b"]} 是陣列,不是標量。
  • 稀疏屬性——DynamoDB 是無 schema 的,項 A 可能有 price 而項 B 沒有。你寫死的列清單會悄悄丟列或錯位。

而且根本沒有 --output csv——CLI 的輸出格式是 jsonyamltexttableoff,沒有一個理解 DynamoDB 型別。所以你仍然需要 jq(或指令碼)來剝掉型別標籤。這就是"用 AWS CLI 把 DynamoDB 表匯出為 CSV"越過最簡單場景後就永遠不是一行命令的根本原因。

要用這種方式匯出一張更大的整表而不至於花上一整天,用 --segment / --total-segments 把掃描並行化(AWS 文件:並行掃描——DynamoDB"透過對每個項的分割區索引鍵套用雜湊函式來把項分配到_段_",所以各段可能不均勻),並讀一讀分頁,免得停在第一個 1 MB 頁。

方法 3:DynamoDB 匯出到 S3(大表)

對任何真正有規模的表,託管的匯出到 Amazon S3 才是正確工具。它從你時間點恢復(PITR)視窗內的任意時刻匯出一份快照——所以表上必須先啟用 PITR,否則匯出會以 PointInTimeRecoveryUnavailableException 失敗——非同步執行,且不消耗讀取容量單位,因此對錶的吞吐量和可用性零影響(AWS 文件:"匯出是非同步的,不消耗讀取容量單位(RCU),對表的效能和可用性沒有影響";"你需要在表上啟用 PITR 才能使用匯出功能")。這也是主控台的 Exports to S3 操作在底層觸發的東西:主控台只是同一個 API 的前端,所以它帶著同樣的 PITR 要求和同樣的 JSON 輸出。

aws dynamodb export-table-to-point-in-time \
  --table-arn arn:aws:dynamodb:us-east-1:123456789012:table/MyTable \
  --s3-bucket my-export-bucket \
  --export-format DYNAMODB_JSON

唯一的坑:S3 匯出不輸出 CSV。它只寫 DynamoDB JSONAmazon Ion,以 gzip 壓縮檔案的形式、按 JSON-lines 格式(每行一個項)落盤,外加清單檔案(AWS 文件:匯出輸出格式——資料檔案寫成 .json.gz,"格式是 JSON lines",隨附 manifest-summary.json / manifest-files.json)。之後你仍然需要一個轉換步驟:

  • Athena / Glue 可以直接讀取匯出的 DynamoDB JSON——把一張表指向那個 S3 字首,然後從一條 SELECT 寫出 CSV(這就是常見的"DynamoDB 匯出到 S3 再到 CSV"流水線)。AWS 指出"許多 AWS 服務,如 Athena 和 AWS Glue,會自動解析這種格式"(匯出輸出格式)。
  • 自己動手——解壓那些 .gz 檔案,逐行解析 JSON,然後把它壓平(和其他所有方法面對的是同一個壓平問題)。

它還是一份全表快照:沒有伺服器端篩選可以只匯出部分項。如果你需要一個子集,要麼事後在 Athena 裡篩,要麼改用圖形介面(方法 1)/ 指令碼。

方法 4:一段小指令碼(boto3 / Node)

當匯出必須無人值守地執行時——夜間任務、CI 步驟——一段小指令碼勝過上面的一切。它的優勢在於 AWS SDK 會替你解開(unmarshall)帶型別的 JSON:boto3 的 resource 介面和 JS SDK 的 DynamoDBDocumentClient 返回的是純的 {"price": 2000} 而不是 {"price": {"N": "2000"}}(boto3 的 resource 介面讓"資料型別處理變得隱式",見 AWS Python 指南;JS 的 DocumentClient"將帶註解的響應資料轉換為原生 JavaScript 型別",見 @aws-sdk/lib-dynamodb)。

import boto3, csv

table = boto3.resource("dynamodb").Table("MyTable")
rows, resp = [], table.scan()
rows += resp["Items"]
while "LastEvaluatedKey" in resp:                  # paginate to the end
    resp = table.scan(ExclusiveStartKey=resp["LastEvaluatedKey"])
    rows += resp["Items"]

with open("out.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=["id", "name", "price"])
    w.writeheader()
    for r in rows:
        w.writerow({k: r.get(k) for k in w.fieldnames})

有兩個 SDK 無法替你做的決定仍歸你管:如何把巢狀的 map/list 壓平成列(把單元格 JSON 編碼?還是把鍵展開成點路徑?),以及稀疏屬性怎麼辦(這裡缺失的鍵經由 r.get(k) 變成空單元格)。還有別丟掉 LastEvaluatedKey 迴圈——單次 scan() 呼叫只返回第一個 1 MB 頁,沒有它你會悄無聲息地只匯出表的一部分。

和方法 2 同樣的告誡:這裡的全表 scan 仍然消耗讀取容量、仍然與線上流量競爭。大表請優先用方法 3,然後重塑那份轉儲。

坑:DynamoDB JSON 與扁平 CSV

無論選哪種方法,DynamoDB 資料模型與扁平 CSV 之間的這幾處錯位都會咬你一口:

  • 型別描述符。原始 API / CLI / S3 匯出的輸出把每個值都包起來({"S": "..."}{"N": "123"})。你要麼經由 SDK 解開它,要麼自己剝掉描述符。完整集合是 SNBBOOLNULLMLSSNSBS——參見 DynamoDB 資料型別
  • 巢狀的 map 和 listML)最深可以巢狀 32 層AWS 文件:資料型別——list 和 map"可以相互巢狀,以表示最深 32 層的複雜資料結構"),並且沒有天然的單列形式。提前決定:把單元格 JSON 編碼,還是把巢狀鍵展開成點路徑列(address.city)。
  • SetSS/NS/BS)是無序集合,不是標量——AWS 警告"集合內各值的順序不會被保留"(資料型別)——所以壓平成帶分隔符的字串,並且不要依賴元素順序。
  • 稀疏屬性。DynamoDB 是無 schema 的,兩個項可以有不同的屬性。不存在固定列集;對所有項的鍵做並集,否則列會錯位。這是單表設計的直接後果——一張表裝著多種實體形狀。
  • 分頁。Scan(和 Query)每次呼叫最多返回 1 MB。不迴圈處理 LastEvaluatedKey,你就會悄無聲息地只匯出第一頁。參見分頁
  • 數字精度。DynamoDB 的數字最多攜帶 38 位精度,並以字串形式在網路上傳輸(AWS 文件:資料型別:"數字最多可以有 38 位精度";"所有數字都以字串形式透過網路傳送到 DynamoDB");電子表格軟體可能把長數字或 ID 強轉成浮點數並丟失位數。讓它們保持為文字。

常見問題

把 DynamoDB 表匯出為 CSV 最快的方式是什麼? 一個替你做壓平的圖形介面:在 DynoTable 裡篩選表,按 ⌘⇧E,選 CSV,再挑一個範圍——從選中的行到篩選匹配到的每一個項,從 DynamoDB 流式取出。型別描述符和巢狀值都會自動處理。

如何用 AWS CLI 把 DynamoDB 表匯出為 CSV? 掃描表並用 jq 重塑輸出(方法 2):aws dynamodb scan → 用 jq 剝掉每個值的型別描述符 → @csv。不存在理解 DynamoDB 的 --output csv,所以剝型別標籤永遠得自己來,而且它在巢狀 map、list 和 set 上會崩。

能從 AWS 直接把 DynamoDB 表匯出為 CSV 嗎? 一步到位不行。主控台和託管的 S3 匯出產出的都是 DynamoDB JSON 或 Amazon Ion,絕不是 CSV。你永遠需要一個轉換步驟——CLI + jq、指令碼、在 S3 轉儲之上跑 Athena/Glue,或者一個替你壓平的圖形介面。

如何在不影響生產的情況下匯出整張 DynamoDB 表?匯出到 S3 功能(方法 3)。它非同步執行且不消耗讀取容量單位,所以不與線上流量競爭——不像 Scan 會計入你表的吞吐量(AWS 文件)。它要求啟用 PITR,並且匯出的是整張表,不是篩選後的子集。

如何把 DynamoDB 以 CSV 形式匯出到 S3? 託管匯出只會把 DynamoDB JSON / Ion 寫到 S3,所以"到 CSV"是第二跳:把匯出字首註冊為一張 Athena(或 Glue)表,然後從一條 SELECT 寫出 CSV。不存在 --export-format CSV

如何把 DynamoDB 匯出到 Excel? 先匯出為 CSV(上面任一方法),然後在 Excel 裡開啟這份 CSV——注意讓長的數字 ID 保持為文字,免得被強轉成浮點數。DynamoDB 沒有直接的 .xlsx 匯出;DynoTable 可以把當前檢視直接儲存為可供電子表格使用的 CSV。

為什麼我匯出的 JSON 裡到處都是 {"S": ...}{"N": ...} 那是 DynamoDB 的 線上格式——每個值都被打上型別描述符標籤。寫 CSV 之前,用 SDK、DynamoDB JSON 轉換器或圖形介面把它解開。無論資料來自 API、CLI 還是 S3 匯出,線上格式都是同一個。

DynoTable 瀏覽、篩選並把你自己的表匯出為 CSV,或者先在 JSON 轉換器裡解開一份 DynamoDB JSON 樣本。

已更新