我可以在CouchDB中进行交易和locking吗?

我需要做交易(开始,提交或回滚),锁(select更新)。 我怎样才能在文档模型数据库中做到这一点?

编辑:

情况是这样的:

  • 我想运行一个拍卖网站。
  • 我想如何直接购买。
  • 在直接采购中,我必须减less物料logging中的数量字段,但只有在数量大于零的情况下。 这就是为什么我需要锁和交易。
  • 我不知道如何解决没有锁和/或交易。

我可以用CouchDB解决这个问题吗?

不.CouchDB使用“乐观并发”模型。 用最简单的话来说,这仅仅意味着你发送一个文档版本以及你的更新,如果当前的文档版本与你发送的不一致,CouchDB会拒绝这个更改。

这看起来很简单,真的。 您可以重构许多基于事务的CouchDB场景。 不过,在学习CouchDB时,您确实需要抛弃您的RDBMS领域知识。 从更高级别处理问题是有帮助的,而不是试图将沙发塑造成基于SQL的世界。

跟踪库存

您列出的问题主要是库存问题。 如果您有描述项目的文档,并且包含“可用数量”字段,则可以处理如下所示的并发问题:

  1. 检索文档,记下CouchDB一起发送的_rev属性
  2. 减less数量字段,如果它大于零
  3. 使用_rev属性发回更新的文档
  4. 如果_rev与当前存储的号码相匹配,就完成了!
  5. 如果发生冲突( _rev不匹配),请检索最新的文档版本

在这种情况下,有两种可能的故障情况需要考虑。 如果最近的文档版本的数量为0,那么就像在RDBMS中一样处理它,并提醒用户他们实际上不能购买他们想要购买的东西。 如果最近的文档版本数量大于0,则只需使用更新的数据重复该操作,然后从头开始。 这迫使你做比RDBMS更多的工作,并且如果频繁的,冲突的更新可能会有点恼人。

现在,我刚刚给出的答案假定您将在CouchDB中执行与在RDBMS中执行相同的操作。 我可能会有点不同的方式处理这个问题:

我将从包含所有描述符数据(名称,图片,描述,价格等)的“主产品”文档开始。 然后,我将为每个特定实例添加一个“库存票据”文档,其中包含product_keyclaimed_by字段。 如果您销售的是锤子模型,并且有20个销售,那么您可以使用hammer-1hammer-2等钥匙的文档来表示每个可用的锤子。

然后,我会创build一个视图,给我一个可用的锤子列表,使我可以看到一个“全部”的减lessfunction。 这些都是完全的袖口,但应该给你一个什么样的工作看法的想法。

地图

 function(doc) { if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) { emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); } } 

这给我一个可用的“门票”,按产品密钥的列表。 当有人想要购买一把锤子时,我可以抓住这些组,然后迭代发送更新(使用id_rev ),直到我成功申请一个(之前声称的票据将导致更新错误)。

减less

 function (keys, values, combine) { return values.length; } 

这种减lessfunction只是返回无人认领inventory_ticket项目的总数,所以你可以知道有多less“锤子”可供购买。

注意事项

这个解决scheme对于你提出的特定问题总共花费了大约3.5分钟的时间。 可能有更好的方法来做到这一点! 也就是说,它大大减less了更新的冲突,并减less了对新冲突的反应。 在这种模式下,您不会有多个用户尝试更改主要产品条目中的数据。 最糟糕的情况是,您将有多个用户尝试申请一张票,如果您从视图中抓取了多个用户,则只需转到下一张票,然后再试一次。

参考: https : //wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F

扩展MrKurt的答案。 对于很多场景,您不需要按顺序兑换股票。 而不是select第一张票,您可以从其余票中随机select。 鉴于大量的门票和大量的并发请求,与那些试图获得第一张门票的人相比,这些门票的竞争将大大减less。

重复交易的devise模式是在系统中造成“紧张”。 对于银行帐户交易的stream行示例用例,您必须确保更新两个相关帐户的总额:

  • 创build一个交易文档“从账户11223转账USD10到账户88733”。 这造成了系统的紧张。
  • 解决所有交易文件和任何张力扫描
    • 如果源帐户未更新,请更新源帐户(-10美元)
    • 如果源账户已更新但交易文档没有显示,则更新交易文档(例如,在文档中设置标志“sourcedone”),
    • 如果目标账户没有更新,则更新目标账户(+10美元)
    • 如果目标帐户已更新,但交易文档没有显示,则更新交易文档
    • 如果两个帐户都已更新,您可以删除交易文档或保留以进行审计。

对于所有的“张力文件”,应该在后端程序中进行张力扫描,以保持系统中的张力时间短。 在上面的例子中,当第一个账户被更新但是第二个账户没有被更新时,会有一个短时间的不一致。 如果你的Couchdb是分布式的,那么你必须以同样的方式考虑这个问题。

另一种可能的实现是完全避免了交易的需要:只需存储紧张文件,并通过评估每个涉及到的紧张文件来评估系统的状态。 在上面的例子中,这意味着一个账户的总额只能被确定为涉及该账户的交易文件中的总和值。 在Couchdb中,您可以很好地将其作为地图/缩小视图进行build模。

作为对OP问题的回应,Couch可能不是这里最好的select。 使用视图是跟踪库存的好方法,但是钳制到0或多或less是不可能的。 问题是当你阅读一个视图的结果时的竞争条件,决定你可以使用“锤子-1”项目,然后写一个文档来使用它。 问题是,如果视图的结果是> 0锤1,那么没有primefaces的方式来只写文档来使用锤子。 如果100个用户同时查询视图并看到1个锤子1,他们都可以写一个文档来使用锤子1,结果是-99锤子-1。 在实践中,竞争条件将相当小 – 如果您的数据库运行本地主机,那么竞争条件将非常小。 但是,一旦你扩展,并有一个非现场的数据库服务器或集群,问题将变得更加明显。 无论如何,在一个与货币相关的关键系统中出现这样的竞争条件是不可接受的。

更新MrKurt的回复(可能只是过时了,或者他可能不知道一些CouchDBfunction)

视图是处理CouchDB中的余额/库存等的好方法。

您不需要在视图中发布docid和rev。 当您检索查看结果时,您可以免费获得这两个。 发射它们 – 特别是像字典这样的冗长格式 – 将会使你的视图变得不必要的大。

跟踪库存余额的一个简单的看法应该看起来更像这样(也是我的头顶)

 function( doc ) { if( doc.InventoryChange != undefined ) { for( product_key in doc.InventoryChange ) { emit( product_key, 1 ); } } } 

减lessfunction更简单

 _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锤。

有了这个模型,你永远不会更新任何数据,只能追加。 这意味着没有更新冲突的机会。 所有更新数据的交易问题都离开了:)

这个模型的另一个好处是,数据库中的任何文档都可以添加和减去库存中的项目。 这些文件可以有其他各种数据。 您可能会收到一份“Shipment”文件,其中包含有关收到的date和时间,仓库,接收员工等的一系列数据,只要该文档定义了InventoryChange,它就会更新库存。 像“销售”文档和“DamagedItem”文档等。看着每个文件,他们读得非常清楚。 视图处理所有艰苦的工作。

不,CouchDB通常不适用于事务性应用程序,因为它不支持集群/复制环境中的primefaces操作。

CouchDB牺牲了事务处理能力,有利于可伸缩性。 为了进行primefaces操作,您需要一个中央协调系统,这会限制您的可扩展性。

如果您可以保证您只有一个CouchDB实例,或者每个修改特定文档的人都连接到同一个CouchDB实例,那么您可以使用冲突检测系统使用上述方法创build一种primefaces性,但如果稍后扩展到群集或者使用像Cloudant这样的托pipe服务,它将会崩溃,您将不得不重做系统的这一部分。

所以,我的build议是使用除了CouchDB以外的帐户余额,这将是更容易的方式。

其实,你可以在某种程度上。 查看HTTP Document API并向下滚动到标题“使用单个请求修改多个文档”。

基本上,您可以在URI / {dbname} / _ bulk_docs的单个发布请求中创build/更新/删除一堆文档,它们将全部成功或全部失败。 该文件确实警告说,这种行为在未来可能会改变。

编辑:正如所预测的,从0.9版本的批量文档不再这样工作。

只需使用SQlitetypes的轻量级解决scheme进行事务处理,并在事务完成时成功复制它,并将其标记为在SQLite中复制

SQLite表

 txn_id , txn_attribute1, txn_attribute2,......,txn_status dhwdhwu$sg1 xy added/replicated 

您也可以删除成功复制的事务。