DynamoDB能否及如何实现指定多字段组合的表级唯一性约束
DynamoDB 实现四字段组合全局唯一的落地方案
DynamoDB 原生不支持在表定义层面直接给任意非主键字段配置自动唯一约束,但可以通过原生的表设计能力在数据层实现强唯一校验,完全替代应用层“先查再写”的逻辑,从根本上规避并发写导致的重复数据问题。
为什么不推荐纯应用层校验方案
你最初想到的服务层先查询、再判断是否重复的逻辑,存在天然的并发漏洞:两个同时发起的新增请求,可能都查到目标四字段组合不存在,最后双双写入成功,产生重复记录。这种问题在高并发场景下触发概率很高,事后排查成本也非常高。
可直接在数据层落地的实现路径
路径1:调整主表主键设计(性能最优、无额外一致性损耗)
这是成本最低、可靠性最高的方案:
- 提前定义固定拼接规则,把四个字段按顺序拼接成唯一标识,比如用固定分隔符拼接:
${region}#${market}#${brand}#${productType},把拼接结果作为主表的分区键 - DynamoDB 原生就会保证分区键值的全局唯一,只要写入相同分区键的记录,就会直接被识别为重复写入,不需要额外写校验逻辑
- 如果业务上必须保留
code字段作为核心主键,可以用复合主键设计:把拼接的唯一组合值设为分区键,code设为排序键,既保证四字段组合全局唯一,也能兼容原有的code字段的存储需求。
路径2:基于原子事务+独立约束表实现(完全兼容原有主键设计)
如果你不想调整现有主表的主键结构(也就是保留现在code作为hash key的设计),可以通过DynamoDB原生的事务能力在数据层实现强唯一校验:
- 新建一张结构极简的唯一约束表,表的分区键就是之前拼接的
uniqKey(四字段拼接的字符串),不需要存储其他业务字段 - 每次写入
Terms主表记录的时候,调用DynamoDB的TransactWriteItems接口,一次提交两个原子操作:- 向约束表写入一条记录,主键值为当前业务记录的
uniqKey,同时配置写入条件:attribute_not_exists(uniqKey)—— 如果这个uniqKey已经在约束表存在,整个事务会直接失败,阻断本次写入 - 向主表写入本次的
Terms业务记录
- 向约束表写入一条记录,主键值为当前业务记录的
- 这套方案是完全原子的,不存在并发漏洞,因为事务的所有操作要么全部成功提交,要么全部回滚,完全不需要在应用层做额外的查询校验逻辑
落地注意事项
- 拼接字段时一定要选一个绝对不会出现在业务字段值里的分隔符,比如
#或者~,避免不同字段组合拼出完全相同的字符串(比如region="a"、market="b#c"和region="a#b"、market="c"会拼出同一个uniqKey,导致误判重复) - 如果四个字段拼接后的总长度超过DynamoDB分区键的最大限制(单属性最大2048字节),可以对拼接后的字符串做SHA-256哈希,把哈希值存为
uniqKey,只要业务上能接受可忽略的碰撞概率就可以用 - 如果需要根据四字段组合快速检索
Terms记录,可以基于主表的uniqKey属性创建全局二级索引,把uniqKey设为GSI的分区键提升查询效率,但不要用GSI做唯一性校验,因为GSI的最终一致性特性会导致短暂的写入窗口漏洞
内容的提问来源于stack exchange,提问作者Andrey Yaskulsky
相关产品推荐
相关产品推荐

