入门阅读约 2 分钟

DynamoDB 中的 Query 对比 Scan

Query 按分区键读取单个项集合(可选地用排序键进一步缩小范围);Scan 读取整张表然后再做筛选。它们在 API 里看起来相似,但在计费 —— 和扩展性 —— 上完全不同。

在 DynamoDB 中,什么时候应该用 Query,什么时候用 Scan?

只要能指定所需分区,就用 Query——它只读取单个项集合,且仅对匹配的项计费。只有一次性导出或极小的表才考虑 Scan;Scan 会读取每一个项,并在任何 FilterExpression 运行之前对整张表计费。在真实数据量下,Query 始终更优。

  • Query 是有针对性的:你只为匹配分区中的项付费。
  • Scan 是穷尽式的:你为读取每一个项付费,然后用一个在读取被计量 之后 才运行的 FilterExpression 把大部分丢弃。

在任何有规模的表上,带筛选条件的 Scan 都是那个经典的“为什么我的账单巨大、延迟还比 RDS 差”的暗坑。

并排对比

QueryScan
读取一个分区(按 PK)表中的每一个
计费容量分区中匹配的项整张表,在筛选之前
FilterExpression在读取之后应用 —— 仍按读取计费同样 —— 筛选从不削减成本
延迟随表增长保持平稳随表大小增长
分页1 MB/页 → LastEvaluatedKey1 MB/页;可并行化
适用于已知的访问模式一次性导出、极小的配置表

关键陷阱:FilterExpression 在两种操作上都是在 DynamoDB 计量读取之后才运行。一个“返回 10 行”的 Scan 可能为读取一百万行而计费 —— 筛选是一种便利,绝非成本控制手段。

使用 Query

Query  PK = "USER#42"  AND  SK begins_with "ORDER#"

如果你发现自己为了回答某个常见访问模式而伸手去用 Scan,那是一个建模信号:添加一个全局二级索引,让该模式变成一次 Query

归根结底就是一个问题 —— 你能说出你需要的那个分区吗?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

如果键已知,就用 Query;如果未知,就加一个 GSI 把它变成已知,只有在没有键能匹配时才退回到 Scan

Scan 何时没问题

一次性导出、极小的配置表,以及刻意翻遍整张表的后台作业。当你确实必须读取一切时,用 Segment/TotalSegments 把一个 Scan 拆分到多个 worker 上(一次并行 Scan)。

对 DynamoDB 反射式地写 SELECT * FROM table 是同一种反模式披上了 PartiQL 的外衣 —— 它会编译成一次 Scan。当你确实需要跨项分析(一个 GROUP BY、一个 JOIN、一次聚合)时,DynoTable 的 SQL Workbench 会在客户端基于一个有界的结果集运行它们,而不是猛击你的表。

容量计算器估算任一模式将花费的读取单元,并试用 DynoTable 针对你自己的表运行并查看这些查询。

更新于