You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带GSI的DynamoDB表设计:满足多场景唯一键查询需求

DynamoDB person 表设计落地方案

主表核心键规则

  • 分区键:uuid(字符串类型),完全匹配按UUID查询单用户全量数据的需求,单条点查性能稳定在毫秒级,无额外开销。
  • 写入约束:所有写入记录必须携带全局唯一的uuid,其余业务字段(name/state/city/bio/age)无强制写入要求。

第一类查询(按UUID查全量)

直接用主表点查即可,不需要走索引,是DynamoDB性能最优、成本最低的查询方式。


第二类查询(按城市、州查人员姓名列表)

配置第一个GSI(记为GSI_Location_Name)即可,不需要做额外的表维护:

  • GSI分区键:state_city(字符串类型,写入时固定拼接为{state}#{city}格式,例如马萨诸塞州波士顿对应值为Massachusetts#Boston)
  • GSI排序键:name(字符串类型)
  • 投影配置:KEYS_ONLY 即可,不需要投影全量字段,最大程度降低存储和读取成本
  • 查询方式:指定GSI分区键为目标state_city值,直接查询该分区下的所有排序键,就能拿到对应地区的全部人员姓名,不需要跨分区扫描,性能和成本都可控。

第三类查询(获取所有唯一城市/州组合)

先明确你提到的GSI全表扫方案的问题

直接扫描GSI_Location_Name只提取分区键的方案成本随人员数据量线性增长,不适合线上常规调用:
DynamoDB扫描操作的RCU消耗是按实际扫描过的数据量计算的,哪怕你通过投影表达式只取分区键字段,只要扫描过的用户条目都会计入读取容量。如果你的人员表有上百万条记录,哪怕城市组合只有几千个,也要扫完上百万条索引条目,不仅RCU消耗高,扫描耗时也会到秒级,只适合一次性离线统计,不能作为线上接口的实现方案。

标准无额外成本方案:稀疏GSI自动去重

不需要加Lambda、不需要维护二级表、不需要引入外部缓存,只用加第二个稀疏GSI(记为GSI_Unique_Location)就能实现,零额外运维成本:

  1. 给主表增加一个可选字段loc_marker,写入规则非常简单:

    写入新人员记录时,只有当该记录所属的state_city组合是第一次出现时,才给loc_marker赋值为固定字符串"EXISTS";同地区后续写入的所有人员记录,该字段留空不写入。
    并发写入同地区新用户时如果出现重复标记的极端情况,客户端扫描后做一次内存去重即可,概率极低几乎不产生额外开销。

  2. GSI_Unique_Location键配置:
    • 分区键:state_city(和第一个GSI的拼接值完全一致)
    • 排序键:loc_marker
    • 投影配置:KEYS_ONLY
  3. 原理:DynamoDB的GSI只会对分区键、排序键字段同时存在的记录建立索引,同个state_city下只有第一条记录带loc_marker字段,因此这个GSI里每个城市/州组合只会对应1条索引记录,天然完成去重。
  4. 查询方式:全量扫描这个GSI即可,扫描的总条目数等于你全表唯一的城市/州组合总数——哪怕总人员数据有上千万,只要全国/全球的城市/州组合只有几万个,扫描只需要读几万条记录,RCU消耗极低,速度也能稳定在百毫秒级。
  5. 删除适配:如果某个地区的最后一条人员记录被删除,同步删除对应GSI里的marker条目即可,逻辑简单没有一致性问题。

你提到的两个备选方案的明显缺陷

  • DynamoDB流+Lambda维护单条二级表记录:DynamoDB单条记录有400KB的硬大小上限,城市/州组合数量多了会直接写不下;同时高并发下多个Lambda同时更新同一条记录会触发写冲突,需要加乐观锁重试,逻辑复杂容易丢数据。
  • 引入Redis/Memcached:除了额外的资源成本,还要处理缓存和数据库的一致性问题,新增/删除地区时双写逻辑会增加大量故障点,完全没有必要。

内容的提问来源于stack exchange,提问作者mmachenry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 02:09:26