入门阅读约 4 分钟

如何将 DynamoDB 表导出为 CSV(4 种方法)

DynamoDB 没有原生的"导出为 CSV"按钮。每个值返回时都包裹在 DynamoDB 的整理后 JSON中——{"S": "..."}{"N": "123"}{"M": {...}}——而一张表可以容纳嵌套的映射、列表和 集合,没有明显的扁平列表示。所以"把 DynamoDB 导出为 CSV" 其实是两个问题:把项取出来,然后把带类型的 JSON 扁平化成 行。控制台和托管导出都不会为你做第二步。

本指南按表大小和你的项携带多少嵌套,对四种方法 排序。

如何将 DynamoDB 表导出为 CSV?

DynamoDB 没有原生的 CSV 导出功能,因此你需要扫描或快照表,再将其带类型的 JSON 扁平化为行。小表可使用 AWS CLI scan + jq 或短脚本;大表可导出到 S3 后转换;如需过滤且开箱即用的 CSV,可使用 DynoTable 等 GUI 工具。

  • 小表、临时: AWS CLI scan + jq,或一段 20 行的脚本 (方法 1 / 方法 3)。 在嵌套属性出现之前都没问题。
  • 大表(数 GB+): DynamoDB 导出到 S3方法 2),然后转换导出文件。 它异步运行且不消耗读容量——但它输出 DynamoDB JSON,而非 CSV。
  • 过滤 / 整形后的 CSV(列的子集,仅部分项): GUI 导出或 脚本。托管的 S3 导出给你的是整张表,不带过滤。

方法 1: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)去取原始值。对于 带三个字符串列的扁平表,这还可控。一旦你有了以下情况它就崩溃:

  • 嵌套映射/列表——{"M": {...}}{"L": [...]} 没有单一列可 扁平化进去;@csv 卡壳,或者你得手动把单元格 JSON 编码。
  • 集合——{"SS": ["a","b"]} 是数组,不是标量。
  • 稀疏属性——DynamoDB 无 schema,所以项 A 可能有 price,而 项 B 可能没有。你固定的列列表会静默丢列或错位。

也没有理解 DynamoDB 类型的 --output csv。CLI 的 csv 输出会把原始响应连同描述符一起扁平化——所以你仍然需要 jq(或 脚本)来剥离类型标签。这正是"通过 AWS CLI 把 DynamoDB 表导出为 CSV"在最简单情况之外永远不是一行命令的核心原因。

要用这种方式导出一张较大的整表而又不耗一整天,用 --segment / --total-segments 并行化扫描 (AWS 文档:并行扫描—— DynamoDB "通过对每个项的分区键应用哈希函数来把项分配到", 所以段可能不均匀),并读 分页,这样你就不会停在第一个 1 MB 页。

方法 2:DynamoDB 导出到 S3(大表)

对于任何有实际规模的表,托管的导出到 Amazon S3 是正确的工具。 它从你的时间点恢复(PITR)窗口内的任意时间点导出快照—— 所以必须先在表上启用 PITR——它异步运行, 并不消耗读容量单位,因此对你表的吞吐量或可用性零影响 (AWS 文档: "导出是异步的,它们不消耗读容量单位(RCU),对表性能和可用性无 影响";"要使用导出功能,你需要在表上启用 PITR")。这也是控制台的导出到 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 中过滤,要么 改用脚本 / GUI。

方法 3:一段快速脚本(boto3 / Node)

当你需要一份整形过的 CSV——特定的列、过滤后的子集、对嵌套 字段的自定义处理——一段小脚本胜过与 jq 搏斗。其优势在于 AWS SDK 为你反整理带类型的 JSON:boto3 的资源接口和 JS SDK 的 DynamoDBDocumentClient 返回普通的 {"price": 2000} 而非 {"price": {"N": "2000"}}(boto3 的资源接口让"数据类型 隐式化",依据 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:                  # 分页到结尾
    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 替你做不了的两个决定:如何把嵌套的 映射/列表扁平化成列(把单元格 JSON 编码?把键做点路径?),以及如何处理 稀疏属性(这里缺失的键会通过 r.get(k) 变成空单元格)。 而且别丢掉 LastEvaluatedKey 循环——单次 scan() 调用只返回 第一个 1 MB 页,所以没有它你会静默地只导出表的一部分。

与方法 1 相同的告诫:这里的整表 scan 仍会消耗读容量并 与实时流量竞争。对于大表,优先用 方法 2并重塑导出文件。

方法 4:在 DynoTable 中一键导出

脚本和 CLI 路线可行,但你每次都要重建同样的扁平化和分页 逻辑。DynoTable 为你完成它:运行或过滤一个查询, 然后把可见的行直接导出为 CSV(或 Excel)——类型描述符 被解包、嵌套的映射和列表被扁平化、集合被处理,且输出中 只有你真正想要的项和列。

因为你导出的是当前视图,你得到的是 方法 2的整表快照无法 给你的那份过滤/整形 CSV——而无需写扫描循环。它是一款桌面 DynamoDB 客户端,正是你已经用来浏览表的同一个工具;看看它与其他 DynamoDB GUI 的对比。

坑:DynamoDB JSON 与扁平 CSV

无论你选哪种方法,DynamoDB 数据模型与扁平 CSV 之间的 同一批不匹配都会咬你:

  • 类型描述符。 原始 API / CLI / S3 导出的输出会包裹每个值 ({"S": "..."}{"N": "123"})。你要么通过 SDK 解包它,要么自己剥离 描述符。完整集合是 SNBBOOLNULLMLSSNSBS——参见 DynamoDB 数据类型
  • 嵌套映射和列表ML)可嵌套深达 32 层AWS 文档:数据类型—— 列表和映射"可以相互嵌套,以表示复杂的数据结构 深达 32 层"),且没有自然的单列形式。请提前决定: 把单元格 JSON 编码,或把嵌套键展开成点路径列(address.city)。
  • 集合SS/NS/BS)是无序集合,不是标量——AWS 警告 "集合内值的顺序不会被保留" (数据类型)—— 所以扁平化成一个有分隔符的字符串,且不要依赖元素顺序。
  • 稀疏属性。 DynamoDB 无 schema,所以两个项可以有不同的 属性。没有固定的列集;把所有项的键取并集,否则 列会错位。这是 单表设计的直接后果,在那里一张表容纳了多种 实体形状。
  • 分页。 Scan(和 Query)每次调用最多返回 1 MB。如果你不 在 LastEvaluatedKey 上循环,你会静默地只导出第一页。参见 分页
  • 数字精度。 DynamoDB 数字携带多达 38 位精度,并 以字符串传输(AWS 文档:数据类型: "数字最多可有 38 位精度";"所有数字都以字符串形式通过 网络发送到 DynamoDB");电子表格软件可能把长数字或 ID 强制转成浮点数并丢失位数。请把它们保留为文本。

常见问题

如何用 AWS CLI 把 DynamoDB 表导出为 CSV? 扫描表并用 jq 重塑输出(方法 1):aws dynamodb scanjq 剥离每个值的类型描述符 → @csv。没有 DynamoDB 感知的 --output csv,所以你总要自己做类型剥离,且它在嵌套 映射、列表和集合上会失败。

我能直接从 AWS 把 DynamoDB 表导出为 CSV 吗? 不能一步到位。控制台和托管的 S3 导出都产出 DynamoDB JSON 或 Amazon Ion,从不产出 CSV。你总需要一个转换步骤——CLI + jq、脚本、 对 S3 导出文件的 Athena/Glue,或一个为你做扁平化的 GUI。

如何在不影响生产的情况下导出整张 DynamoDB 表? 使用导出到 S3 功能(方法 2)。它异步运行且消耗 零读容量单位,所以它不与实时流量竞争——不像 Scan 那样针对你表的吞吐量计量 (AWS 文档)。 它要求启用 PITR,并导出整张表,而非过滤后的子集。

如何把 DynamoDB 导出到 S3 为 CSV? 托管导出只把 DynamoDB JSON / Ion 写到 S3,所以"到 CSV"是第二 跳:把导出前缀注册为一个 Athena(或 Glue)表,并从一个 SELECT 写出 CSV。没有 --export-format CSV

如何把 DynamoDB 导出到 Excel? 先导出为 CSV(上面的任意方法),然后在 Excel 中打开 CSV——把长 数字 ID 保留为文本,这样它们不会被强制转成浮点数。没有 从 DynamoDB 直接导出 .xlsx;像 DynoTable 这样的 GUI 能把当前视图 直接保存为可供电子表格使用的 CSV。

为什么我导出的 JSON 到处都是 {"S": ...}{"N": ...} 那是 DynamoDB 整理后的线缆格式——每个值都用一个类型 描述符标记。在写 CSV 之前,用 SDK、 DynamoDB JSON 转换器或一个 GUI 反整理它。 无论数据来自 API、CLI 还是 S3 导出,线缆格式都一样。

DynoTable 浏览、过滤并把你自己的表导出为 CSV,或先在 JSON 转换器中解包一个 DynamoDB JSON 样本。

更新于