目前,我看到当我在DynamoDB中使用版本控制时,它会更改版本号,但新条目将替换旧条目;即:
旧
{ object:one, name:"hey", version:1}
新
{ object:one, name:"ho", version:2}
我想要的是在db中有两个条目;即:
旧
{ object:one, name:"hey", version:1 }
新
{ object:one, name:"hey", version:1}
{ object:one, name:"ho", version:2}
有什么方法可以实现这个目标吗?
答案 0 :(得分:9)
我不认为DynamoDB服务目前支持行版本控制。 如果您需要版本控制功能,则需要在您身边进行。
在DynamoDB中,一行由其主键唯一标识。主键可以是HashKey-only或HashKey + RangeKey。如果要将同一行与不同版本区分开来,则需要在主键的某处包含版本号。
例如,您可以将版本号附加到散列键的末尾,以用于行的所有旧版本。具有最新版本的行将使用原始哈希密钥。
Hash Attr Version
hey a2 2
hey_v1 a1 1
将行更新到版本3后,表格应如下所示:
Hash Attr Version
hey a3 3
hey_v1 a1 1
hey_v2 a2 2
在客户端进行版本控制总是不完美。例如,对于上述方法,如果进行扫描,您将获得hey_V1和hey_v2。如果这对你有用,请告诉我。如果您有更好的方法在客户端进行版本控制,请在此处发布。
答案 1 :(得分:4)
您还可以通过维护两个单独的表来实现此目的。一个用于最新项目,另一个用于其版本。我写了一篇博客文章,详细解释https://www.efekarakus.com/2018/05/25/client-side-row-versioning-in-dynamo-db.html
资源表,其中 hash 是主键。
+----------+---------+-------------------+
| hash | version | attr1..attrN |
+----------+---------+-------------------+
| 1c5815b2 | 2 | some values |
+----------+---------+-------------------+
resource-history 表,其中 hash 是分区键, version 是排序键。
+----------+---------+-------------------+
| hash | version | attr1..attrN |
+----------+---------+-------------------+
| 1c5815b2 | 2 | some values |
+----------+---------+-------------------+
| 1c5815b2 | 1 | some old values |
+----------+---------+-------------------+
重要的是,任何更改记录的操作都应该增加其版本号。
创建或更新资源时,首先写入 resource-history 表,然后写入 resource 表。
我发现这稍微更清晰,因为在单个表上处理不可变数据时,您不会遇到潜在的数据丢失情况。
答案 2 :(得分:4)
我一直在试验和计算在读/写单位和成本方面最有效的方法,考虑了在记录版本时进行更新的竞争条件,并避免了数据重复。我缩小了一些可能的解决方案。您必须考虑自己的最佳变化。
基本概念围绕着将版本0
作为最新版本。另外,我们将使用一个revisions
键,该键将列出该项目之前存在的修订版本,也将用于确定该项目的当前版本(version = revisions + 1
)。能够计算版本的存在是一项要求,在我看来,revisions
可以满足该需求以及可以提供给用户的值。
因此,将使用version: 0
和revisions: 0
创建第一行。尽管从技术上讲这是第一个版本(v1),但在归档之前,我们不会应用版本号。当该行更改时,version
停留在0
(仍表示最新),并且revisions
递增到1
。将使用所有先前的值创建一个新行,但现在该行表示version: 1
。
总结:
创建商品时:
revisions: 0
和version 0
创建项目在商品更新或覆盖时:
revisions
version: 0
更改为新版本,可以很容易地将其计算为version: revisions + 1
。在只有主键的表上,这就是转换为转换的样子:
主键: id
id color
9501 violet
9502 cyan
9503 magenta
主键: id + version
id version revisions color
9501 0 6 violet
9501 1 0 red
9501 2 1 orange
9501 3 2 yellow
9501 4 3 green
9501 5 4 blue
9501 6 5 indigo
这里要转换已使用排序键的表:
主键: id + date
id date color
9501 2018-01 violet
9501 2018-02 cyan
9501 2018-03 black
主键: id + date_ver
id date_ver revisions color
9501 2018-01__v0 6 violet
9501 2018-01__v1 0 red
9501 2018-01__v2 1 orange
9501 2018-01__v3 2 yellow
9501 2018-01__v4 3 green
9501 2018-01__v5 4 blue
9501 2018-01__v6 5 indigo
替代方法2:
id date_ver revisions color
9501 2018-01 6 violet
9501 2018-01__v1 0 red
9501 2018-01__v2 1 orange
9501 2018-01__v3 2 yellow
9501 2018-01__v4 3 green
9501 2018-01__v5 4 blue
9501 2018-01__v6 5 indigo
实际上,我们可以选择将以前的版本放在同一张表中,也可以将它们分开放在自己的表中。两种选择都有各自的优点和缺点。
number
或附加到现有的排序键中作为string
优势:
缺点:
revision
键version
建立一个排序键。主表将始终具有version: 0
。不是必须在主表上使用此键。优势:
get
请求保持不变。缺点:
无论您决定如何对数据进行分区,现在我们都必须决定如何创建修订行。以下是几种不同的方法:
摘要:获取该行的当前版本。在当前行上执行一次更新,并通过一次事务插入先前的版本。
为避免竞争情况,我们需要使用TransactWriteItems
在同一操作中写入更新和插入。另外,我们需要确保在请求到达数据库服务器时,我们正在更新的版本是正确的版本。我们可以通过两种检查之一,甚至两种检查来实现此目的:
Update
中的TransactItems
命令中,ConditionExpression
必须检查要更新的行中的revision
与我们对象中的revision
是否匹配之前执行过Get
。Put
的{{1}}命令中,TransactItems
检查以确保该行尚不存在。费用
注释:
摘要:获取并存储当前行。覆盖或更新行时,请检查当前修订版本并递增ConditionExpression
。插入先前存储的带有版本号的行。
通过
执行revisions
update
在覆盖先前存在的行时,我们可以将{
UpdateExpression: 'SET revisions = :newRevisionCount',
ExpressionAttributeValues: {
':newRevisionCount': previousRow.revisions + 1,
':expectedRevisionCount': previousRow.revisions,
},
ConditionExpression: 'revisions = :expectedRevisionCount',
}
与ConditionExpression
一起使用。
在回复中,我们正在注意put
。如果返回此内容,则表示修订已被另一个过程更改,我们需要从头开始重复该过程或完全中止。如果没有例外,那么我们可以在适当时更新版本属性上的值(数字或字符串)后插入上一个存储的行。
费用
摘要:在v0行上执行“盲目”更新,同时增加ConditionalCheckFailedException
并请求旧属性。使用返回值创建具有版本号的新行。
通过
执行revisions
update-item
{
UpdateExpression: 'ADD revisions :revisionIncrement',
ExpressionAttributeValues: {
':revisionIncrement': 1,
},
ReturnValues: 'ALL_OLD',
}
操作将自动创建ADD
(如果不存在),并将其视为revisions
。 ReturnValues的一个不错的好处是:
除了小型网络外,没有其他与请求返回值相关的开销,也没有接收较大响应的处理开销。没有消耗读取容量单位。
在更新响应中,0
值将是旧记录中的数据。该记录的版本为Attributes
的值。适当地更新您的version属性上的值(数字或字符串)。
现在您可以将该记录插入目标表中。
费用
注释:
Attributes.revisions + 1
长度限制为65535。摘要:在递增Attributes
的同时执行“盲”更新并在主数据库上插入。使用Lambda触发器监视revisions
的更改以异步插入修订。
通过{p>执行revision
update
如果{
UpdateExpression: 'ADD revisions :revisionIncrement',
ExpressionAttributeValues: {
':revisionIncrement': 1,
},
}
操作不存在,则会自动创建ADD
并将其视为revisions
。
用于基于先前的0
请求以put
递增revisions
值覆盖记录。
配置DynamoDB流视图类型以返回新图像和旧图像。针对数据库表设置Lambda触发器。这是NodeJS的示例代码,它将比较旧图像和新图像,并调用一个函数来批量编写修订。
get
这只是示例,但是生产代码可能会包含更多检查。
费用
注释:
对于我的用例,我已经在使用DynamoDB流,并且我不希望用户经常请求版本化行。我也可以让用户稍等片刻,因为它们是异步的。这使得使用第二张桌子和自动lambda流程对我来说是更理想的解决方案。
对于异步选项,存在一些故障点。不过,您可以立即按需重试,也可以为DynamoDB Stream解决方案安排稍后的时间。
如果任何人还有其他解决方案或批评,请发表评论。谢谢!
答案 3 :(得分:2)
Amazon提出了有关如何在DynamoDB中进行版本控制的建议:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html#bp-sort-keys-version-control
使用排序键作为版本,您可以确保最新的键始终排在最前面(例如“ v0_”),然后其余键依次排序。他们还建议将v0_latest克隆为“ v00x_”,以便它可以作为最后一个关键字,以便进行查找以按顺序获取版本历史记录。
有关详细信息,请参见该链接。