dynamodb——事务
目的:提供在同一区域内对跨表的数据项执行原子和可序列化操作的能力,同时保证性能的可预测性,并且不影响非事务性的工作负载。
因此,没有采用传统意义上的交互式事务,而是引入了两个新的单请求操作实现一次性事务,即TransactGetItems和TransactWriteItems。
无需其他操作,直接使用api即可支持事务
TransactGetItems
只读事务API,实现从一致的快照中检索多个条目,事务读和任何写操作之间是序列化的
使用
async function testTransactGet() {
const params = {
TransactItems: [
{
Get: {
TableName: userTableName,
Key: {
user_id: '577fb67c-9330-4336-b0b3-9c7a59ef67a3',
},
ProjectionExpression: 'user_id,email,creation_time',
},
},
{
Get: {
TableName: orderTableName,
Key: {
id: '1234',
},
ProjectionExpression: 'id,user_id,creation_time,esim_info',
},
},
{
Get: {
TableName: orderTableName,
Key: {
id: 'de886376-fcfb-4acf-b24c-dd19ba13d768',
},
ProjectionExpression: 'id,user_id,creation_time,esim_info',
},
},
],
}
const resp = await dynamoDb.transactGet(params)
if (resp.$metadata.httpStatusCode !== 200) {
console.log(`error`)
} else {
console.log(JSON.stringify(resp))
}
}
{
"$metadata": {
"httpStatusCode": 200,
"requestId": "5COQSMVUIM6SKDJE127IPLK627VV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"Responses": [
{
"Item": {
"email": "jing.zhang@getnomad.app",
"user_id": "577fb67c-9330-4336-b0b3-9c7a59ef67a3",
"creation_time": "2023-12-14T11:12:36Z"
}
},
{},
{
"Item": {
"esim_info": {
"matching_id": "JQ-20G3W1-1LSANYC",
"iccid": "8932042000006259105",
"require_manual_activate": false,
"qr_data": "LPA:1$rsp.truphone.com$JQ-20G3W1-1LSANYC",
"smdp_url": "rsp.truphone.com",
"support_direct_install": false,
"status": "Released",
"trust_installation_status": false
},
"user_id": "b5f20d65-6f5c-4512-8de5-4eb50dbebfc4",
"id": "de886376-fcfb-4acf-b24c-dd19ba13d768",
"creation_time": "2024-03-23T06:08:17Z"
}
}
]
}
- 只能使用主键索引查询,使用其他字段会抛异常:TransactionCanceledException, The provided key element does not match the schema
- 同一事务内不能存在相同的条目,属于语法错误,会抛异常:ValidationException: Transaction request cannot include multiple operations on one item
- 多个项目的结果按序返回
TransactWriteItems
事务写API,允许在一个或多个表中原子地创建、删除或更新多个条目,支持:Put、Update、Delete 和 ConditionCheck 操作
- Put — 启动 PutItem 操作以创建一个新项目,或者将旧项目替换为新项目
- Update — 启动 UpdateItem 操作以编辑现有项目的属性,或者将新项目添加到表中
- Delete — 启动 DeleteItem 操作,以删除表中由其主键标识的单个项目
- ConditionCheck — 检查项目是否存在,或者检查项目特定属性的条件
使用
async function testUpdateCondition(clientRequestToken, userTagId) {
const params = {
TransactItems: [
{
ConditionCheck: {
TableName: `test.sys.users`,
Key: {
user_id: '4ceae1dd-d331-4535-8835-6451f5d5269f',
},
ConditionExpression: `attribute_exists(user_id)`,
},
},
{
Put: {
TableName: `dev.sys.user_tag`,
Item: {
id: userTagId,
email: 'jing.zhang+testt1@getnomad.app',
organization_id: 'dea6024b-09ea-4835-9335-4d689be48aae',
account_tag: 'BT',
operator_user_id: '577fb67c-9330-4336-b0b3-9c7a59ef67a3',
creation_time: moment().utc().format(),
},
},
},
{
Update: {
TableName: orderTableName,
Key: {
id: '161dd505-1ed7-4384-a666-fa672f942cd5',
},
UpdateExpression: 'SET assigned = :expect_value,user_id = :expect_user_id',
ExpressionAttributeValues: { ':expect_value': true, ':expect_user_id': '4ceae1dd-d331-4535-8835-6451f5d5269f', ':current_assigned': false },
ConditionExpression: 'assigned = :current_assigned',
},
},
],
ClientRequestToken: clientRequestToken,
}
const resp = await dynamoDb.transactWrite(params)
console.log(JSON.stringify(resp))
}
- ConditionCheck 用于条件检测,必须结合ConditionExpression
幂等-ClientRequestToken
- 使用方主动传入参数ClientRequestToken
- 幂等有效期为十分钟
- 十分钟内使用相同token发起相同参数的请求,会返回成功,实际未做更改
async function testUpdateCondition(clientRequestToken, userTagId) {
const params = {
TransactItems: [
{
Update: {
TableName: userTableName,
Key: {
user_id: '577fb67c-9330-4336-b0b3-9c7a59ef67a3',
},
UpdateExpression: 'SET subscribe_to_feed = :expect_value',
ExpressionAttributeValues: { ':expect_value': true },
},
},
],
ClientRequestToken: clientRequestToken,
ReturnConsumedCapacity: 'INDEXES',
}
const resp = await dynamoDb.transactWrite(params)
console.log(JSON.stringify(resp))
}
{
"$metadata": {
"httpStatusCode": 200,
"requestId": "KNPADV761709N5G9F9D0P6RMQVVV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"ConsumedCapacity": [
{
"CapacityUnits": 6,
"TableName": "test.sys.users",
"WriteCapacityUnits": 6
}
]
}
{
"$metadata": {
"httpStatusCode": 200,
"requestId": "7J4BO6MQPCIAGTLMU1ICFJ5LRFVV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"ConsumedCapacity": [
{
"CapacityUnits": 2,
"ReadCapacityUnits": 2,
"TableName": "test.sys.users"
}
]
}
相同token相同request 十分钟内再次请求结果成功,但只消耗了2RCU,无WCU的消耗
- 十分钟内使用相同token发起不同参数的请求会报错:IdempotentParameterMismatchException: Specified idempotent token was used with different request parameters within the idempotency window
dynamoose
await dynamoose.transaction([
// User.transaction.get({ user_id: '577fb67c-9330-4336-b0b3-9c7a59ef67a3' }),
User.transaction.condition({ user_id: '4ceae1dd-d331-4535-8835-6451f5d5269f' }, new dynamoose.Condition().where('email').exists()),
UserTag.transaction.create({
id: '02f15ff7-38df-4250-a963-3aee1fec9fb3',
email: 'jing.zhang+testt1@getnomad.app',
organization_id: 'dea6024b-09ea-4835-9335-4d689be48aae',
account_tag: 'BT',
operator_user_id: '577fb67c-9330-4336-b0b3-9c7a59ef67a3',
creation_time: moment().utc().format(),
}),
Order.transaction.update({ id: '161dd505-1ed7-4384-a666-fa672f942cd5' }, { assigned: true, user_id: '4ceae1dd-d331-4535-8835-6451f5d5269f' }),
])
‼️ dynamoose.transaction()入参支持get,condition,put,update,delete;但不支持同时执行get和write,需要使用方自主将两种操作分开调用
决定是write和get的方式:
- 设置setting.type,但是type定义只有内部声明,没有export,用起来会编译错误,但不影响实际执行
TransactionType
- 由dynamoose自主决定,有一条是write即为write,最终由事务校验报错:ValidationException: Invalid Request: TransactWriteRequest should contain Delete or Put or Update request
并发事务的处理
事务冲突
对相同数据的事务并发操作,存在事务冲突,即TransactionConflictException:
- 事务写和普通写同时执行且包含相同的数据,即 正在进行的TransactWriteItems请求 与 put/update/delete请求包含有相同的数据
- 事务写和事务写同时执行且包含相同的数据,即TransactWriteItems 请求中的某个项目是另一个正在进行的 TransactWriteItems 请求的一部分
- 事务读和写操作(事务写/普通写)同时执行且包含相同的数据,即TransactGetItems 请求中的某个项目是正在进行的 TransactWriteItems/BatchWriteItem/PutItem/UpdateItem/DeleteItem 请求的一部分
隔离级别
可序列化
当前操作在前一个操作完成之前不会开始执行
- 在任何事务操作与任何标准写入操作(PutItem、UpdateItem 或 DeleteItem)之间
- 在任何事务操作与任何标准读取操作 (GetItem) 之间
- 在 TransactWriteItems 操作与 TransactGetItems 操作之间
读已提交
- 事务操作与批量普通读(BatchGetItem、Query 或 Scan)之间均为读取已提交,一定不能读取到事务未提交的数据,预期能读取到提交事务的最新结果
- BatchGetItem和TransactWriteItems是读取已提交,但BatchGetItem中的单个读取和TransactWriteItems整体是序列化的
性能
事务操作对非事务操作的影响只是占据了一定的读写容量,而对于按需的容量模式可以忽略
- 事务操作和非事务操作是互不干扰的
原文地址:https://blog.csdn.net/Zj_boring/article/details/143776702
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!