一个订单可以包含一种或多种产品。订单也属于客户。订单、产品和客户是我们在数据库中的项目模型。新订单创建接受如下请求。
{
"customer_id": "c8fb0e64-43b0-45b1-a9e7-fe2ce61c2c1f",
...
"products": [
{
"id": "7362ea92-2063-48ad-9bfe-c91a5d9af4a6",
...
},
{
"id": "83e38b2f-2839-43c5-a834-0834d841e566",
...
}
]
}
当我创建新订单时,我使用
TransactWriteItems
操作,到目前为止效果很好(见下图)。然而,目前我不检查是否存在:
c8fb0e64-43b0-45b1-a9e7-fe2ce61c2c1f
)。7362ea92-2063-48ad-9bfe-c91a5d9af4a6
和83e38b2f-2839-43c5-a834-0834d841e566
)将项目放入数据库时。
如图所示,客户(底部的一个)和产品项目(顶部的两个)已经在同一张表中。客户可通过
part_key
和/或 customer_id
属性来识别,而产品可通过 part_key
和/或 product_id
属性来识别。此外,表中的每个项目都有一个 _type
属性来表示它们是什么。
问题是,在使用事务将新项目插入数据库之前,我们通常如何管理存在性检查?我知道
attribute_exists
和条件检查,但不确定就性能和成本考虑而言正确的方法。
这是我的代码,如果有帮助的话,仅供参考。
func (d DynamoDB) CreateOrder(ctx context.Context, model database.Order) error {
items := make([]types.TransactWriteItem, 0, len(model.Products)+1)
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,
},
})
}
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
}
attribute_not_exists () 带有条件表达式来检查项目是否存在。添加条件表达式没有额外成本。但是,即使您的条件失败,您也将需要为写入操作(放置/交易)付费,就像成功写入时所产生的费用一样。这种条件检查失败在分布式系统中最有用,以避免竞争条件。
避免写入成本(如果写入成本变得成本密集)的另一种方法是简单地执行 get 请求来检查是否存在,因为读取成本会更低。 Dynamodb WCU-RCU