在AWS DynamoDB中插入关联项前如何校验父项存在性
如何在DynamoDB事务中校验客户与产品的存在性
业务场景与问题
业务中一个订单可包含多个产品,且归属单个客户,订单、产品、客户均存储在同一张DynamoDB表中。创建订单的请求格式如下:
{ "customer_id": "c8fb0e64-43b0-45b1-a9e7-fe2ce61c2c1f", ... "products": [ { "id": "7362ea92-2063-48ad-9bfe-c91a5d9af4a6", ... }, { "id": "83e38b2f-2839-43c5-a834-0834d841e566", ... } ] }
当前使用TransactWriteItems创建订单的代码可正常执行,但未校验客户及关联产品的存在性:
- 需校验请求中指定的客户是否存在
- 需校验请求中所有关联产品是否存在
表结构说明:
- 客户可通过
part_key和/或customer_id字段识别,_type属性值为customer - 产品可通过
part_key和/或product_id字段识别,_type属性值为product
需要在事务插入订单数据前完成上述存在性校验,要求兼顾性能与成本。
最优解决方案
利用DynamoDB TransactWriteItems支持的ConditionCheck操作,在事务中原子执行存在性校验与数据插入。ConditionCheck会先验证指定条件,只有所有校验通过,后续的Put操作才会执行;任意校验失败,整个事务回滚,无数据写入。
具体实现步骤
添加客户存在性校验:
构造ConditionCheck项,指定客户的主键(示例中假设客户的part_key为customer#<customer_id>,sort_key与part_key一致),通过条件表达式确保该条目存在且为客户类型。添加产品存在性校验:
对每个关联产品,构造对应的ConditionCheck项,指定产品的主键(示例中假设产品的part_key为product#<product_id>,sort_key与part_key一致),验证条目存在且为产品类型。整合事务操作:
将所有ConditionCheck与原有的订单、订单-产品关联数据的Put操作放入同一个事务请求中,保证原子性。
修改后的Go代码
func (d DynamoDB) CreateOrder(ctx context.Context, model database.Order) error { items := make([]types.TransactWriteItem, 0, len(model.Products)*2 + 2) // 预留校验与写入的位置 // 1. 校验客户存在性 customerKey := database.Key{ PartKey: "customer#" + model.CustomerID, SortKey: "customer#" + model.CustomerID, } customerItem, err := attributevalue.MarshalMap(customerKey) if err != nil { return err } items = append(items, types.TransactWriteItem{ ConditionCheck: &types.ConditionCheck{ TableName: aws.String(d.Table), Key: customerItem, ConditionExpression: aws.String("attribute_exists(_type) AND _type = :customer_type"), ExpressionAttributeValues: map[string]types.AttributeValue{ ":customer_type": &types.AttributeValueMemberS{Value: "customer"}, }, }, }) // 2. 校验每个产品的存在性 for _, product := range model.Products { productKey := database.Key{ PartKey: "product#" + product.ID, SortKey: "product#" + product.ID, } productItem, err := attributevalue.MarshalMap(productKey) if err != nil { return err } items = append(items, types.TransactWriteItem{ ConditionCheck: &types.ConditionCheck{ TableName: aws.String(d.Table), Key: productItem, ConditionExpression: aws.String("attribute_exists(_type) AND _type = :product_type"), ExpressionAttributeValues: map[string]types.AttributeValue{ ":product_type": &types.AttributeValueMemberS{Value: "product"}, }, }, }) } // 3. 添加订单-产品关联数据 for _, product := range model.Products { product.Key = database.Key{ PartKey: "order#" + product.OrderID, SortKey: "product#" + product.ID, } product.Type = "order_product" item, err := attributevalue.MarshalMap(product) if err != nil { return err } items = append(items, types.TransactWriteItem{ Put: &types.Put{ TableName: aws.String(d.Table), Item: item, }, }) } // 4. 添加订单主数据 model.Key = database.Key{ PartKey: "order#" + model.ID, SortKey: "order#" + model.ID, } model.Type = "order" item, err := attributevalue.MarshalMap(model) if err != nil { return err } items = append(items, types.TransactWriteItem{ Put: &types.Put{ TableName: aws.String(d.Table), Item: item, }, }) _, err = d.Client.TransactWriteItems(ctx, &dynamodb.TransactWriteItemsInput{ TransactItems: items, }) if err != nil { return err } return nil }
注意事项
- 若你的表中客户或产品的主键结构与示例不同(比如sort_key为其他固定值),请调整
customerKey和productKey的构造逻辑,确保能准确定位到对应条目。 - 条件表达式中加入
_type校验,是为了避免主键冲突导致的误判(比如避免把其他类型的条目当成客户/产品)。 - DynamoDB事务最多支持25个操作,若订单关联产品数量较多,需注意此限制,超过时可拆分事务处理。
内容的提问来源于stack exchange,提问作者BentCoder
相关产品推荐
相关产品推荐

