进阶阅读约 3 分钟

在一个会变化(可变)的属性上排序 DynamoDB

你围绕一个属性建模排序键,以便能按它的顺序查询项 —— 然后这个属性变了。工单的状态、订单的 状态、任务的优先级。这里有 DynamoDB 抛给你的难题:你不能原地更新一个键属性。 主键在项的 整个生命周期内都是不可变的。改变一个属于键的值,你做的就不是编辑一个项 —— 而是移动它,而 DynamoDB 要你显式地这样做。

能更改 DynamoDB 排序键吗?

不能。排序键是主键的组成部分,DynamoDB 的键属性是不可变的——UpdateItem 无法修改分区键或排序键的值,也没有"移动项"这一操作。要更改排序键,需要删除旧项并写入新项,或者改为将易变的值放在 GSI 的排序键上。

  • 键属性不可变。 你不能 UpdateItem 一个分区键或排序键的值 —— DynamoDB 没有「移动项」 这个操作。
  • 要改变一个键值,你得删除旧项再放入一个新项 —— 最好放在一个 事务里,让它具备原子性。
  • 更好的做法:让易变的值脱离基础表的键,把它放到一个 GSI 的排序键 上 —— GSI 的键可以变,因为更新基础项只会重新传播索引条目。
  • 只要访问模式允许,就选择不会变化的排序键(时间戳、不可变 id)。

问题所在:一个你想用来排序、却一直在变的状态

假设你运营一个客服台,想按状态列出某个团队的工单,于是你把状态放进排序键:

PK: TEAM#7   SK: STATUS#open#TICKET#8842

现在工单转为 pending。你想直接 UpdateItem 把排序键改成 STATUS#pending#TICKET#8842 —— 但 DynamoDB 会拒绝任何改变键属性的写入。键是项的地址; 你不能原地编辑地址。你选来排序的那个状态,恰恰是坐不住的那个东西。

选项 1:删除并重建(原子地)

如果这个值必须存在于基础表的键里,那么改变它就意味着删除旧项、写入新项:

1. DeleteItem  PK=TEAM#7  SK=STATUS#open#TICKET#8842
2. PutItem     PK=TEAM#7  SK=STATUS#pending#TICKET#8842  (same attributes)

把它放进一个 TransactWriteItems 里,让删除和放入要么都成功 要么都失败 —— 否则两步之间的一次崩溃会丢失或重复这张工单。这行得通,但每次状态变更现在都是两次 写入外加一个事务;偶尔变更尚可,热点变更则代价高昂。

选项 2:让可变的值脱离基础键(首选)

更干净的设计:让基础表的键是某个不可变的东西(工单 id),把易变、可排序的值放到一个 GSI 的排序键上。

Base:  PK: TICKET#8842   status: "open"   teamId: TEAM#7
GSI:   GSI1PK: TEAM#7    GSI1SK: STATUS#open#TICKET#8842

现在改变状态就是对基础项的 status 属性做一次普通的 UpdateItem —— 这是 DynamoDB 允许的, 因为 status 不是基础表的键。随后 DynamoDB 会自动把 GSI 条目重新传播到它新的排序位置。一次 写入,没有事务,没有删除-重建的折腾。

否,它是 GSI 排序键状态从 open 变为 pending这个值在基础表的键里吗?在事务中删除并重建普通 UpdateItem;GSI 重新传播

代价是:GSI 是最终一致的,并会带来额外的存储/写入 成本 —— 但对于一个频繁变化的值来说,这远比每次变更都删除-重建便宜。

在 DynoTable 中设计键

DynamoDB 表达式构建器为基础读取和 GSI 读取分别构建 并预览键条件。

在 DynoTable 中,你可以选择查询走哪个索引,并实时看到易变的值在 GSI 上排序,而基础项 保持其不可变的键 — 两种读取并排显示在真实数据上。

在 DynoTable 中查询一个按状态排序的 GSI,同时基础项保持不可变的键。
在 DynoTable 中查询一个按状态排序的 GSI,同时基础项保持不可变的键。

陷阱与下一步

  • 绝不要尝试 UpdateItem 一个键属性 —— 那会被拒绝;键值在项的生命周期内是固定的。
  • 如果你必须移动它,就在一个事务里做删除+放入 —— 绝不要做成两次无保护的写入。
  • 优先采用不可变的基础键 + 一个 GSI,用于任何你既要排序又要改变的属性。
  • 别忘了 GSI 是最终一致的 —— 重新排序后的条目会在短暂的传播延迟后出现。
  • 相关: 排序键策略GSI 与 LSI事务

想看看一个可变属性在 GSI 上与在基础表上分别如何排序吗? 下载 DynoTable,直接探索你的索引。

更新于