搜尋
首頁資料庫mysql教程MongoDB操作手册CRUD事务两步提交

执行两步提交 概述 这部分提供了多记录更新或者多记录事务,使用两步提交来完成多记录写入的模板。另外,可以扩展此方法来提供rollback-like功能。 背景 MongoDB对于单条记录的操作是原子性的;但是涉及多条记录的操作却不是原子性的。由于记录可能是相当复杂

执行两步提交

概述

这部分提供了多记录更新或者多记录事务,使用两步提交来完成多记录写入的模板。另外,可以扩展此方法来提供rollback-like功能。

背景

MongoDB对于单条记录的操作是原子性的;但是涉及多条记录的操作却不是原子性的。由于记录可能是相当复杂,并且有内嵌记录,单记录原子性操作提供了实际中常用的必要支持。
除了单记录的原子性操作,还有许多情况需要多记录操作事务,当执行一个包含一些列操作的事务时,就有以下要求:
原子性:如果一个操作失败,事务中之前的操作需要回滚到之前的状态
一致性:如果一个重大失误,比如网络故障,硬件故障,中断了事务,数据库必须能够恢复到之前的状态
对于需要多记录操作的事务,可以在应用中实现两步提交的方法,来提供多记录更新支持。使用这种方法保证了一致性,并且万一出现错误,事务的执行状态是可恢复的。然而在这个过程中,记录处于未定的数据和状态。
注意:因为MongoDB只有单记录操作是原子性的,两步提交只能提供语义上的“类事务”功能。对于应用来说,使其能够回到在两步提交中的某个状态的中间数据或者回滚数据。

模板

考虑以下情景:

要将资金从账户A转移到账户B,在关系型数据库中,可以在一个事务中从A中减去资金,同时在B中加上。在MongoDB中,可以模拟两步提交来获得相同结果。

这个例子使用两个集合
1.accounts,用于存储账户信息
2.transactions,用于存储资金转移事务的信息

初始化账户信息

db.accounts.insert(
[
{ _id: "A", balance: 1000, pendingTransactions: [] },
{ _id: "B", balance: 1000, pendingTransactions: [] }
]
);

初始化转账记录

对于每次资金转移操作,将转账信息添加到transactions集合中,插入的记录包含以下信息:
source和destination字段,引用自ccounts集合中的_id字段
value字段,声明转移数值
state字段,表明当前转移状态,值可以是initial,pending, applied, done, canceling, 或者 canceled.
lastModified字段,反应最后修改日期

从A转账100到B,初始化transactions记录:
db.transactions.insert({ _id: 1, source: "A", destination: "B", value: 100, state: "initial", lastModified: new Date() });

使用两步提交进行转账

1.从transactions集合中,找到state为initial的记录。此时transactions集合中只有一条记录,即刚插入的那条。在包含其他记录的集合中,除非你声明了其他查询条件,否则这个查询将返回任何state为initial的记录。
var t = db.transactions.findOne( { state: "initial" } );
在MongoDB的shell中输入t,查看t的内容,类似于:
{ "_id" : 1, "source" : "A", "destination" : "B", "value" : 100, "state" : "initial", "lastModified":??}
2.更新事务状态为pending
设置state为pending,lastModified为当前时间
db.transactions.update(
{ _id: t._id, state: "initial" },
{
$set: { state: "pending" },
$currentDate: { lastModified: true }
}
)
在更新操作中,state:'initial'确保没有其他进程已经更新了这条记录。如果nMatched和nModified是0,回到第一步,获取一个新的事务,重新开始这个过程。
3.在两个账户中应用该事务
使用update方法将事务t应用到两个账户中。在更新条件中,包含条件pendingTransactions:{$ne:t._id},以避免重复应用该事务。
db.accounts.update(
{ _id: t.source, pendingTransactions: { $ne: t._id } },
{ $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
)
db.accounts.update(
{ _id: t.destination, pendingTransactions: { $ne: t._id } },
{ $inc: { balance: t.value }, $push: { pendingTransactions: t._id } }
)
从A账号减去t.value,给B账户加上t.value,同时给每个账户的pendingTransactions数组添加事务id
4.更新事务状态为applied
db.transactions.update(
{ _id: t._id, state: "pending" },
{
$set: { state: "applied" },
$currentDate: { lastModified: true }
}
)
5.更新账户pendingTransactions数组
db.accounts.update(
{ _id: t.source, pendingTransactions: t._id },
{ $pull: { pendingTransactions: t._id } }
)
db.accounts.update(
{ _id: t.destination, pendingTransactions: t._id },
{ $pull: { pendingTransactions: t._id } }
)
从两个账户中移除已应用的事务。
6.更新事务状态为done
db.transactions.update(
{ _id: t._id, state: "applied" },
{
$set: { state: "done" },
$currentDate: { lastModified: true }
}
)

从失败场景中恢复数据

事务最重要的不是以上这个例子提供的原型,而是当事务没有完全执行成功的时候,从各种失败场景中恢复数据的可能性。
恢复操作
两步提交模型允许应用程序重新执行事务操作序列,以保证数据一致性。在程序启动时,或者定时执行恢复操作,来抓取任何未完成的事务。
恢复到数据一致的状态的时间取决于应用程序多久需要恢复每个事务。
以下恢复操作使用lastModified日期作为pending状态的事务是否需要回滚的标识符。如果pending或者applied状态的事务在最近的30分钟内没有被更新,说明这些事务需要被恢复。可以使用不同的条件来决定是否需要恢复。

Pending状态的事务

恢复事务状态在pending之后,applied之前
例:
获取三十分钟内未成功的事务记录
var dateThreshold = new Date();
dateThreshold.setMinutes(dateThreshold.getMinutes() - 30);
var t = db.transactions.findOne( { state: "pending", lastModified: { $lt: dateThreshold } } );
然后回到“3.在两个账户中应用该事务”这一步

Applied状态的事务

例:
获取三十分钟内未成功的事务记录
var dateThreshold = new Date();
dateThreshold.setMinutes(dateThreshold.getMinutes() - 30);
var t = db.transactions.findOne( { state: "applied", lastModified: { $lt: dateThreshold } } );
然后回到“5.更新账户pendingTransactions数组”这一步

回滚操作

有些情况下,可能需要回滚,或者撤销操作,比如,应用程序需要取消事务,或者其中一个账户不存在或者被冻结。

Applied状态的事务

在“4.更新事务状态为applied”这一步之后,不应该再回滚事务,而是应该完成当前事务,然后创建一个新的事务来把数据修改回来。

Pending状态的事务

在“2.更新事务状态为pending”这一步之后,“4.更新事务状态为applied”这一步之前,可以通过以下步骤回滚事务:
1.更新事务状态为取消中
db.transactions.update(
{ _id: t._id, state: "pending" },
{
$set: { state: "canceling" },
$currentDate: { lastModified: true }
}
)
2.在两个账户中取消操作
如果事务已经应用,需要回退这个事务以取消在两个账户上的操作。在更新的条件中,包含pendingTransactions:t._id,以便在pending transaction已经被应用的时候更新账户。
更新目标账户,减去事务中给其增加的值,cong pendingTransactions数组中移除事务_id
db.accounts.update(
{ _id: t.destination, pendingTransactions: t._id },
{
$inc: { balance: -t.value },
$pull: { pendingTransactions: t._id }
}
)
如果pending transaction还没有被应用到这个账户中,将不会有记录匹配查询条件。
3.更新事务状态为已取消
db.transactions.update(
{ _id: t._id, state: "canceling" },
{
$set: { state: "cancelled" },
$currentDate: { lastModified: true }
}
)
更新事务状态为cancelled来标志事务已取消。

多应用情景

由于事务的存在,多个应用可以同时创建和执行操作,而不会产生数据不一致或者冲突。在之前的例子中,更新或者回滚记录,包含state字段的更新条件防止不同应用重复提交事务
例如,app1和app2同时获取了一个在initial状态的事务。app1在app2开始前提交了整个事务。当app2试图更新事务状态为pending的时候,包含state:'initial'的更新条件将不会匹配任何记录,
同时nMatched和nModified将为0.这就表明app2需要回到第一步,重启一个不同的事务过程。
当多个应用运行的时候,关键在有只有一个应用可以及时处理指定的事务。这样的话,即使在有符合更新条件的记录,也可以在事务记录中创建一个标记来标志应用正在处理这个事务。使用findAndModify()方法来修改事务,并且回退。
t = db.transactions.findAndModify(
{
query: { state: "initial", application: { $exists: false } },
update:
{
$set: { state: "pending", application: "App1" },
$currentDate: { lastModified: true }
},
new: true
}
)
修正后的事务操作确保只有标识符匹配的应用可以提交该事务。
如果app1在事务执行中失败,可以使用之前讲的恢复操作,但是应用程序需要在应用事务之前确保“拥有”该事务。
例如,查找并恢复pending状态的job
var dateThreshold = new Date();
dateThreshold.setMinutes(dateThreshold.getMinutes() - 30);
db.transactions.find(
{
application: "App1",
state: "pending",
lastModified: { $lt: dateThreshold }
}
)

在生产环境中使用两步提交

以上的例子是有意写的很简单。例如,它假设一个账户的回滚操作总是可能的,并且账户可以保存负值。
生产环境可能会更加负值,通常来说,账户需要当前账户值,信用,欠款等多种信息。
对于所有事务,要确保使用的是write concern权限等级。
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
MySQL的許可與其他數據庫系統相比如何?MySQL的許可與其他數據庫系統相比如何?Apr 25, 2025 am 12:26 AM

MySQL使用的是GPL許可證。 1)GPL許可證允許自由使用、修改和分發MySQL,但修改後的分發需遵循GPL。 2)商業許可證可避免公開修改,適合需要保密的商業應用。

您什麼時候選擇InnoDB而不是Myisam,反之亦然?您什麼時候選擇InnoDB而不是Myisam,反之亦然?Apr 25, 2025 am 12:22 AM

選擇InnoDB而不是MyISAM的情況包括:1)需要事務支持,2)高並發環境,3)需要高數據一致性;反之,選擇MyISAM的情況包括:1)主要是讀操作,2)不需要事務支持。 InnoDB適合需要高數據一致性和事務處理的應用,如電商平台,而MyISAM適合讀密集型且無需事務的應用,如博客系統。

在MySQL中解釋外鍵的目的。在MySQL中解釋外鍵的目的。Apr 25, 2025 am 12:17 AM

在MySQL中,外鍵的作用是建立表與表之間的關係,確保數據的一致性和完整性。外鍵通過引用完整性檢查和級聯操作維護數據的有效性,使用時需注意性能優化和避免常見錯誤。

MySQL中有哪些不同類型的索引?MySQL中有哪些不同類型的索引?Apr 25, 2025 am 12:12 AM

MySQL中有四種主要的索引類型:B-Tree索引、哈希索引、全文索引和空間索引。 1.B-Tree索引適用於範圍查詢、排序和分組,適合在employees表的name列上創建。 2.哈希索引適用於等值查詢,適合在MEMORY存儲引擎的hash_table表的id列上創建。 3.全文索引用於文本搜索,適合在articles表的content列上創建。 4.空間索引用於地理空間查詢,適合在locations表的geom列上創建。

您如何在MySQL中創建索引?您如何在MySQL中創建索引?Apr 25, 2025 am 12:06 AM

toCreateAnIndexinMysql,usethecReateIndexStatement.1)forasingLecolumn,使用“ createIndexIdx_lastNameEnemployees(lastName); 2)foracompositeIndex,使用“ createIndexIndexIndexIndexIndexDx_nameOmplayees(lastName,firstName,firstName);” 3)forauniqe instex,creationexexexexex,

MySQL與Sqlite有何不同?MySQL與Sqlite有何不同?Apr 24, 2025 am 12:12 AM

MySQL和SQLite的主要區別在於設計理念和使用場景:1.MySQL適用於大型應用和企業級解決方案,支持高性能和高並發;2.SQLite適合移動應用和桌面軟件,輕量級且易於嵌入。

MySQL中的索引是什麼?它們如何提高性能?MySQL中的索引是什麼?它們如何提高性能?Apr 24, 2025 am 12:09 AM

MySQL中的索引是數據庫表中一列或多列的有序結構,用於加速數據檢索。 1)索引通過減少掃描數據量提升查詢速度。 2)B-Tree索引利用平衡樹結構,適合範圍查詢和排序。 3)創建索引使用CREATEINDEX語句,如CREATEINDEXidx_customer_idONorders(customer_id)。 4)複合索引可優化多列查詢,如CREATEINDEXidx_customer_orderONorders(customer_id,order_date)。 5)使用EXPLAIN分析查詢計劃,避

說明如何使用MySQL中的交易來確保數據一致性。說明如何使用MySQL中的交易來確保數據一致性。Apr 24, 2025 am 12:09 AM

在MySQL中使用事務可以確保數據一致性。 1)通過STARTTRANSACTION開始事務,執行SQL操作後用COMMIT提交或ROLLBACK回滾。 2)使用SAVEPOINT可以設置保存點,允許部分回滾。 3)性能優化建議包括縮短事務時間、避免大規模查詢和合理使用隔離級別。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能