DynamoDB 索引投影
当你创建一个二级索引时,DynamoDB 并不会自动把整个项复制进去。你要选择哪些被复制进去——也就是索引的投影。选得太少,你的查询就要为取回其余部分多付一次读取;把一切都选上,则每次更新都要付出额外的存储和写入成本。这是一个你在创建索引时定下一次、之后就得一直承受的权衡。
(别把这个和投影表达式搞混了,后者裁剪的是单次读取返回的属性。本页讲的是索引在物理上存储了什么——另一个见投影表达式。)
什么是 DynamoDB 索引投影?
投影是 DynamoDB 从基表复制进二级索引的那组属性。你从三种类型里选一种:KEYS_ONLY(只有键)、INCLUDE(键加上一份命名的属性列表)或 ALL(整个项)。投影越多,回基表取数越少,但存储和写入成本越高。
- 投影是被复制进二级索引的那组属性。
KEYS_ONLY—— 只有表和索引的键。最小、最便宜。INCLUDE—— 键,加上一份你自己挑选的、命名的额外属性列表。ALL—— 项的每一个属性。最大;查询永远用不着基表。- 一个没被投影的属性,从 GSI 就是拿不到的——你的应用必须自己去发起基表读取。(只有 LSI 会替你去取回没被投影的属性,代价是额外的读取成本。)
- 投影越多 = 存储越多 + 写入成本越高,因为每一次基表写入都会传播到索引。
问题所在:让你读两次的那个索引
假设你运营一个客服台,用一个 GSI 让你能按优先级列出未关闭的工单。你投影 KEYS_ONLY 好让它精简。查询返回很快——但它只给你工单 ID,而你的队列界面需要每个工单的主题、负责人和存在时长。
于是现在你的代码要对基表做第二轮读取,去填充每一条结果。你设计的那“一次查询”,实际上是一次查询外加 N 次取数,你原本想省下的延迟和成本又原封不动地回来了。投影对这个访问模式来说太单薄了。
每种投影类型复制什么
KEYS_ONLY只存基表键和索引键。当查询只需要知道_哪些_项匹配、而你会在别处(或者根本不)取回细节时用它。INCLUDE存键,加上一份你指定的、固定的属性列表。甜蜜点:恰好投影你的查询用来渲染所需的字段,一个不多。ALL复制整个项。查询完全由索引自给自足,代价是把整个项的存储和写入吞吐都复制进索引。
对于客服台队列,用 INCLUDE 带上 subject、assignee 和 age 是正确选择——队列仅凭索引就能渲染,无需第二次取数,也不必把工单那个庞大的 body 复制进索引。
你在交换的成本
你投影的每一个属性都会被存储第二份,并且在基础项每次变化时都会在索引里被重写一遍。所以在一张频繁更新的表上,一个慷慨的 ALL 投影会让存储和写入容量都翻倍。要守的投影查询所_读取的_,而不是“把一切都投上,以防万一”。
一个值得知道的细微之处:对于稀疏索引,投影仍然只保存那些携带了索引键的项——所以在稀疏索引上用 INCLUDE/ALL 依然很小,因为索引本身就很小。用 DynamoDB 定价计算器 掂量你那个投影的存储和写入倍数,并用 DynamoDB 表达式构建器 组装索引查询本身。
在 DynoTable 里看一个投影
DynoTable 会列出一张表的每一个二级索引,并让你直接透过其中一个来查询。把同一个访问模式分别跑在基表和某个 GSI 上,再对比结果——索引结果里缺失的那些属性,恰好就是它没有投影的那些,所以一个投影的效果无需重读表定义就能看到。

陷阱与后续步骤
- GSI 上一个没被投影的属性意味着一次基表取数——围绕查询所渲染的内容来设计投影。
ALL很少是免费的——它让存储和写入成本翻倍;默认用INCLUDE,除非索引真的需要每一个字段。- 投影基本是固定的。你事后没法随意编辑一个 GSI 的投影而不重建索引——一开始就要慎重选择。
- 相关:GSI 对比 LSI 和稀疏索引决定了一个投影实际上会存储多少。
想在重设计之前看看你每个索引实际返回什么?下载 DynoTable,直接查询你的表。
水合消耗:KEYS_ONLY + N 次
返回支持台队列示例:50 个开放票证显示为主题、受让人和年龄。
| 投影 | 索引查询 | 后续阅读 | EC RCU 草图(2 KB 基本项目) |
|---|---|---|---|
KEYS_ONLY | 已退回 50 把钥匙 | 50 × GetItem | ~50 索引 RCU + ~50 基础 RCU |
INCLUDE 主题、受让人、年龄 | 50 行独立 | 无 | 仅约 50 个指数 RCU |
ALL | 50 份完整版 | 无 | ~50 指数 RCU;更高的存储+写放大器 |
确切的数字取决于预计的属性大小 - 将示例票证粘贴到
item-size calculator 并相乘通过队列深度。仅列出 UI 字段的 INCLUDE 通常优于 ALL
body属性很大,很少在列表视图中显示。
LSI 投影获取行为
只有 LSI 可以选择从基表中获取非投影属性在查询期间(有额外的读取成本)。 GSI 从来不这样做 — 缺失属性要求你的应用程序在基表上调用 GetItem。那差异将许多 GSI 设计推向稍宽的 INCLUDE 投影在前面。
稍后更改预测
GSI 投影在创建时是固定的。加宽KEYS_ONLY至INCLUDE
需要创建新索引、回填、截断流量、删除旧索引 - 在启动前规划字段。 LSI 也有同样的限制。当评估新的访问模式时,查询DynoTable中的候选索引并列出出现的属性 - 间隙以 1:1 的比例映射到缺失的投影条目。
与稀疏索引配对
仅索引 status = open 票证的稀疏 GSI 存储预测仅开放行。即使基表在该索引上的 INCLUDE 仍然很便宜持有数百万张已关闭的票据——指数从未复制它们。与 sparse index patterns 结合,当过滤后的子集相对于表来说很小。
首先构建访问模式
使用 query builder 制作 GSI 原型查询 - 关键条件、投影表达式和过滤器 - 在更改之前云阵。通过询问哪个来交换设计讨论中的投影类型 UI 呈现的列;其他一切都保留在基表上。


