我需要执行事务(开始,提交或回滚),锁(选择更新)。如何在文档模型数据库中做到这一点?
编辑:
情况是这样的:
- 我想经营一个拍卖网站。
- 而且我也想如何直接购买。
- 在直接购买中,我必须减少项目记录中的数量字段,但前提是数量大于零。这就是为什么我需要锁和事务。
- 我不知道如何在没有锁和/或交易的情况下解决这个问题。
我可以用CouchDB解决吗?
Answers:
否。CouchDB使用“乐观并发”模型。用最简单的术语来说,这仅意味着您将文档版本与更新一起发送,如果当前文档版本与您发送的版本不匹配,则CouchDB拒绝更改。
真的,这看似简单。您可以为CouchDB重新构造许多基于常规事务的方案。但是,在学习CouchDB时,您确实需要投入RDBMS领域的知识。从更高层次解决问题很有帮助,而不是尝试将Couch塑造为基于SQL的世界。
跟踪库存
您概述的问题主要是库存问题。如果您有描述项目的文档,并且其中包含“可用数量”字段,则可以处理以下并发问题:
_rev
CouchDB发送属性_rev
属性_rev
匹配当前存储的号码,请完成!_rev
不匹配时),请获取最新的文档版本在这种情况下,有两种可能的故障场景需要考虑。如果最新文档版本的数量为0,则可以像在RDBMS中一样处理它,并警告用户他们实际上无法购买他们想要购买的东西。如果最新文档版本的数量大于0,则只需对更新的数据重复该操作,然后从头开始。这迫使您比RDBMS做更多的工作,并且如果有频繁的,相互冲突的更新,可能会有些烦人。
现在,我刚刚给出的答案假定您将以与RDBMS中几乎相同的方式在CouchDB中进行操作。我对这个问题的处理方式可能有所不同:
我将从包含所有描述符数据(名称,图片,描述,价格等)的“主产品”文档开始。然后,我将为每个特定实例添加一个“库存票据”文档,其中包含product_key
和字段claimed_by
。如果您要出售一把锤子模型,并且要出售20支锤子,则可能会有带有诸如hammer-1
,hammer-2
等等,来表示每个可用锤子。
然后,我将创建一个视图,该视图为我提供可用的锤子列表,并带有一个reduce函数,使我可以看到“总计”。这些完全没有用,但是应该让您了解工作视图的外观。
地图
function(doc)
{
if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) {
emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev });
}
}
这给了我按产品密钥列出的可用“机票”列表。当有人想要购买锤子时,我可以抓住其中的一组,然后通过发送更新(使用id
和_rev
)进行迭代,直到我成功索取一个(以前索取的票证将导致更新错误)。
降低
function (keys, values, combine) {
return values.length;
}
该reduce函数仅返回无人认领的inventory_ticket
物品的总数,因此您可以知道有多少“锤子”可供购买。
注意事项
对于您提出的特定问题,该解决方案大约需要花费3.5分钟的时间。可能有更好的方法可以做到这一点!也就是说,它确实减少了冲突的更新,并减少了使用新更新来响应冲突的需求。在这种模型下,您不会有多个用户试图更改主产品条目中的数据。在最坏的情况下,您将有多个用户试图索取一张票,并且,如果您从视图中抓取了其中几张票,则只需转到下一张票,然后再试一次即可。
参考:https : //wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
完全交易的设计模式是在系统中创建一个“张力”。对于银行帐户交易的流行示例用例,您必须确保更新两个相关帐户的总数:
应当在后端过程中对所有“张力文档”进行张力扫描,以使系统中的张力时间更短。在上面的示例中,当第一个帐户已更新但第二个帐户尚未更新时,预计会出现短暂的不一致。如果分发了Couchdb,则必须以与最终一致性相同的方式来考虑这一点。
另一种可能的实现方式完全避免了交易需求:只需存储张力文档并通过评估每个涉及的张力文档来评估系统状态。在上面的示例中,这意味着帐户的总数仅确定为涉及该帐户的交易文档中的总和。在Couchdb中,您可以很好地将其建模为地图/缩小视图。
作为对OP问题的回应,Couch可能不是此处的最佳选择。使用视图是跟踪库存的一种好方法,但是几乎不可能钳制为0。问题是当您阅读视图结果,确定可以使用“ hammer-1”项目并编写文档以使用它时的竞争条件。问题是,如果视图结果是> 0 Hammer-1,则没有原子方法仅编写文档以使用Hammer。如果100个用户同时查询该视图并看到1个Hammer-1,则他们都可以编写文档以使用Hammer 1,从而得到-99 Hammer-1。实际上,竞争条件将非常小-如果您的数据库正在运行localhost,则竞争条件将非常小。但是,一旦进行扩展并拥有一个非现场数据库服务器或群集,问题就会变得更加明显。
MrKurt的回复的更新(它可能已经过时,或者他可能不知道某些CouchDB功能)
视图是处理CouchDB中的余额/库存之类的好方法。
您无需在视图中发出docid和rev。检索视图结果时,您将免费获得这两个功能。发出它们-特别是像字典这样的冗长格式-只会不必要地扩大您的视野。
跟踪库存余额的简单视图应该更像这样(也离我远去)
function( doc )
{
if( doc.InventoryChange != undefined ) {
for( product_key in doc.InventoryChange ) {
emit( product_key, 1 );
}
}
}
而reduce功能更加简单
_sum
这使用内置的reduce函数,该函数仅将具有匹配键的所有行的值相加。
在此视图中,任何文档都可以具有成员“ InventoryChange”,该成员将product_key映射到其总库存中的更改。即。
{
"_id": "abc123",
"InventoryChange": {
"hammer_1234": 10,
"saw_4321": 25
}
}
将增加10个Hammer_1234和25个saw_4321。
{
"_id": "def456",
"InventoryChange": {
"hammer_1234": -5
}
}
将燃烧库存中的5锤。
使用此模型,您将永远不会更新任何数据,而只会追加数据。这意味着没有更新冲突的机会。所有更新数据的事务性问题都消失了:)
此模型的另一个好处是,数据库中的任何文档都可以从清单中添加和减去项目。这些文档中可以包含各种其他数据。您可能有一个“装运”文档,其中包含有关接收日期和时间,仓库,接收员工等的大量数据,只要该文档定义了InventoryChange,它就会更新库存。就像“销售”文档和“ DamagedItem”文档等一样。在查看每个文档时,他们阅读得非常清楚。该视图处理所有艰苦的工作。
实际上,您可以采用某种方式。查看HTTP文档API,然后向下滚动至标题“使用单个请求修改多个文档”。
基本上,您可以在对URI / {dbname} / _ bulk_docs的单个发布请求中创建/更新/删除一堆文档,它们要么全部成功要么全部失败。该文档确实提醒您,这种行为将来可能会改变。
编辑:如预期的那样,从0.9版开始,批量文档不再以这种方式工作。