问题描述
我意识到,MongoDB本质上不会并且可能永远不会支持此类事务.但是,我发现我确实需要以某种有限的方式使用它们,因此我想出了以下解决方案,我想知道:这是最好的方法吗?需要改进吗?(在我将其应用到我的应用之前!)
I realise that MongoDB, by it's very nature, doesn't and probably never will support these kinds of transactions. However, I have found that I do need to use them in a somewhat limited fashion, so I've come up with the following solution, and I'm wondering: is this the best way of doing it, and can it be improved upon? (before I go and implement it in my app!)
很明显,交易是通过应用程序(在我的情况下是Python网络应用程序)控制的.对于此事务中的每个文档(在任何集合中),都会添加以下字段:
Obviously the transaction is controlled via the application (in my case, a Python web app). For each document in this transaction (in any collection), the following fields are added:
'lock_status': bool (true = locked, false = unlocked),
'data_old': dict (of any old values - current values really - that are being changed),
'data_new': dict (of values replacing the old (current) values - should be an identical list to data_old),
'change_complete': bool (true = the update to this specific document has occurred and was successful),
'transaction_id': ObjectId of the parent transaction
此外,还有一个transaction
集合,该集合存储详细描述每个正在进行的交易的文档.它们看起来像:
In addition, there is a transaction
collection which stores documents detailing each transaction in progress. They look like:
{
'_id': ObjectId,
'date_added': datetime,
'status': bool (true = all changes successful, false = in progress),
'collections': array of collection names involved in the transaction
}
这是流程的逻辑.希望它能以某种方式工作,如果它发生中断或以其他方式失败,则可以正确地回滚.
And here's the logic of the process. Hopefully it works in such a way that if it's interupted, or fails in some other way, it can be rolled back properly.
1 :设置transaction
文档
2 :对于受此交易影响的每个文档:
2: For each document that is affected by this transaction:
- 将
lock_status
设置为true
(以锁定"文档以免其被修改) - 将
data_old
和data_new
设置为旧值和新值 - 将
change_complete
设置为false
- 将
transaction_id
设置为我们刚刚制作的transaction
文档的ObjectId
- Set
lock_status
totrue
(to 'lock' the document from being modified) - Set
data_old
anddata_new
to their old and new values - Set
change_complete
tofalse
- Set
transaction_id
to the ObjectId of thetransaction
document we just made
3 :执行更新.对于每个受影响的文件:
3: Perform the update. For each document affected:
- 用
data_new
值替换该文档中所有受影响的字段 - 将
change_complete
设置为true
- Replace any affected fields in that document with the
data_new
values - Set
change_complete
totrue
4 :将transaction
文档的status
设置为true
(因为已成功修改所有数据)
4: Set the transaction
document's status
to true
(as all data has been modified successfully)
5::对于受交易影响的每个文档,请进行一些清理:
5: For each document affected by the transaction, do some clean up:
- 删除
data_old
和data_new
,因为它们不再需要 - 将
lock_status
设置为false
(以解锁文档)
- remove the
data_old
anddata_new
, as they're no longer needed - set
lock_status
tofalse
(to unlock the document)
6 :删除步骤1中设置的transaction
文档(或根据建议,将其标记为完成)
6: Remove the transaction
document set up in step 1 (or as suggested, mark it as complete)
我认为在逻辑上以这样的方式工作:如果它在任何时候失败,则可以回滚所有数据或可以继续进行事务(取决于您要执行的操作).显然所有回滚/恢复/等.通过使用transaction
文档以及具有该transaction_id的其他集合中的文档,由应用程序而不是数据库执行.
I think that logically works in such a way that if it fails at any point, all data can be either rolled back or the transaction can be continued (depending on what you want to do). Obviously all rollback/recovery/etc. is performed by the application and not the database, by using the transaction
documents and the documents in the other collections with that transaction_id.
此逻辑中是否有我遗漏或忽略的明显错误?有没有更有效的解决方案(例如,更少的数据库读写操作)?
推荐答案
作为通用响应,可以将MongoDB上的多文档提交分为两个阶段提交,这在手册中已有详细介绍(请参见: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/).
As a generic response multi-document commits on MongoDB can be performed as two phase commits, which have been somewhat extensively documented in the manual (See: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/).
手册中建议的模式简要如下:
The pattern suggested by the manual is briefly to following:
- 设置单独的
transactions
集合,其中包括目标文档,源文档,值和状态(在交易中) - 使用
initial
作为state
创建新的交易对象 - 开始进行交易,并将
state
更新为pending
- 将交易应用于两个文档(目标,源)
- 将交易状态更新为
committed
- 使用find来确定文档是否反映交易状态,如果可以,请将交易状态更新为
done
- Set up a separate
transactions
collection, that includes target document, source document, value and state (of the transaction) - Create new transaction object with
initial
as thestate
- Start making a transaction and update
state
topending
- Apply transactions to both documents (target, source)
- Update transaction state to
committed
- Use find to determine whether documents reflect the transaction state, if ok, update transaction state to
done
此外:
- 您需要手动处理故障情况(未发生某些情况,如下所述)
- 您基本上需要通过引入名称
state
值canceling
来手动实施回滚
- You need to manually handle failure scenarios (something didn't happen as described below)
- You need to manually implement a rollback, basically by introducing a name
state
valuecanceling
一些具体的实施注意事项:
Some specific notes for your implementation:
- 我不鼓励您将诸如
lock_status
,data_old
,data_new
之类的字段添加到源/目标文档中.这些应该是交易的属性,而不是单据本身. - 为概括目标/源文档的概念,我认为您可以使用
DBref
s: http://www.mongodb.org/display/DOCS/Database+References - 我不喜欢在完成交易文件后删除它们的想法.将状态设置为
done
似乎是一个更好的主意,因为这使您以后可以调试并找出已执行的事务类型.我很确定您也不会用完磁盘空间(为此也有解决方案). - 在您的模型中,如何保证一切都按预期进行了更改?您是否以某种方式检查更改?
- I would discourage you from adding fields like
lock_status
,data_old
,data_new
into source/target documents. These should be properties of the transactions, not the documents themselves. - To generalize the concept of target/source documents, I think you could use
DBref
s: http://www.mongodb.org/display/DOCS/Database+References - I don't like the idea of deleting transaction documents when they are done. Setting state to
done
seems like a better idea since this allows you to later debug and find out what kind of transactions have been performed. I'm pretty sure you won't run out of disk space either (and for this there are solutions as well). - In your model how do you guarantee that everything has been changed as expected? Do you inspect the changes somehow?
这篇关于MongoDB中的多集合,多文档“事务"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!