我可以在CouchDB中执行事务和锁吗?

ajsxfq5m  于 2022-12-09  发布在  CouchDB
关注(0)|答案(7)|浏览(177)

我需要执行事务(开始、提交或回滚)、锁定(选择以进行更新)。如何在文档模型数据库中执行这些操作?
编辑:
案情是这样的:

  • 我想经营一个拍卖网站。
  • 而且我觉得怎么直接进货也是一样。
  • 在直接采购中,我必须减少项目记录中的数量字段,但只有当数量大于零时,这就是我需要锁和事务的原因。
  • 我不知道如何在没有锁和/或事务的情况下解决这个问题。

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

pu3pd22g

pu3pd22g1#

不可以。CouchDB使用的是“乐观并发”模型。简单来说,这只是意味着你在发送更新的沿着发送一个文档版本,如果当前文档版本与你发送的版本不匹配,CouchDB会拒绝更改。
它看起来很简单,真的。你可以为CouchDB重新构建许多基于事务的普通场景。不过,在学习CouchDB时,你确实需要抛弃你的RDBMS领域知识。从更高的层次来解决问题是有帮助的,而不是试图将Couch塑造成一个基于SQL的世界。

跟踪库存

您概述的问题主要是库存问题。如果您有一个描述物料的文档,并且该文档包含一个“可用数量”字段,则可以按如下方式处理并发问题:
1.检索文档,记下CouchDB沿着的_rev属性
1.如果数量字段大于零,则递减数量字段
1.使用_rev属性将更新的文档发回
1.如果_rev与当前存储的数字匹配,则完成!
1.如果存在冲突(当_rev不匹配时),则检索最新的文档版本
在这种情况下,有两种可能的失败场景需要考虑:如果最新文档版本的数量为0,则可以像在RDBMS中一样处理它,并警告用户他们实际上不能购买他们想购买的东西;如果最新文档版本的数量大于0,则只需对更新后的数据重复该操作,并从头开始。这迫使您比RDBMS做更多的工作,如果有频繁的、冲突的更新,可能会变得有点烦人。
现在,我刚才给出的答案假设您将在CouchDB中以与在RDBMS中大致相同的方式执行操作。我可能会以稍微不同的方式处理这个问题:
我会从包含所有描述符数据的“主产品”文档开始(名称、图片、描述、价格等)。然后,我将为每个特定示例添加一个“inventoryticket”文档,其中包含product_keyclaimed_by字段。如果您正在销售一个Hammer模型,并且有20个这样的模型要销售,则可能会有包含hammer-1hammer-2等等,以表示每个可用的锤子。
然后,我会创建一个视图,提供可用锤子的列表,并使用reduce函数显示“总数”。这些都是现成的,但应该能让您了解工作视图的样子。

唐飞

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

这给了我一个可用“门票”的列表,按产品密钥。当有人想买锤子时,我可以抓住一组这样的门票,然后迭代发送更新(使用X1 M8 N1 X和X1 M9 N1 X),直到我成功地认领一张(之前认领的门票将导致更新错误)。

减少

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

wixjitnu

wixjitnu2#

扩展Kurt先生的答案。在很多情况下,你不需要按顺序兑换股票票。你可以从剩余的票中随机选择,而不是选择第一张票。如果有大量的票和大量的并发请求,你将大大减少对这些票的争用,而不是每个人都试图获得第一张票。

rnmwe5a2

rnmwe5a23#

剩余交易的一种设计模式是在系统中创建一种“紧张”。对于银行帐户交易的常见示例用例,您必须确保更新两个相关帐户的合计:

  • 创建一个交易文档“从账户11223向账户88733转账10美元”。这在系统中造成了紧张。
  • 解决所有交易文档的张力扫描问题,并
  • 如果源帐户尚未更新,请更新源帐户(-10 USD)
  • 如果来源账户已更新,但交易单据未显示,则更新交易单据(例如,在单据中设置标志“sourcedone”)
  • 如果目标帐户尚未更新,请更新目标帐户(+10美元)
  • 如果目标账户已更新,但交易单据未显示,则更新交易单据
  • 如果两个帐户都已更新,则可以删除事务处理单据或保留该单据以供审核。

应该在后台进程中对所有“紧张文档”进行紧张扫描,以保持系统中紧张的时间较短。在上面的例子中,当第一个帐户已经更新,但第二个帐户还没有更新时,将有一个短时间的预期不一致。如果Couchdb是分布式的,必须考虑到这一点,同样的方法你将处理最终的一致性。
另一种可能的实现完全避免了对事务的需要:只需存储张力文档,并通过评估每个相关的张力文档来评估系统的状态。在上面的示例中,这意味着帐户的总额仅确定为涉及该帐户的事务文档中的总和值。在Couchdb中,您可以将其很好地建模为map/reduce视图。

s3fp2yjn

s3fp2yjn4#

不,CouchDB通常不适合事务性应用程序,因为它不支持集群/复制环境中的原子操作。
CouchDB牺牲了事务处理能力来支持可伸缩性。为了实现原子操作,您需要一个中央协调系统,这限制了您的可伸缩性。
如果你能保证你只有一个CouchDB示例,或者修改特定文档的每个人都连接到同一个CouchDB示例,那么你可以使用冲突检测系统,通过上面描述的方法创建一种原子性,但是如果你后来扩展到一个集群,或者使用像Cloudant这样的托管服务,它就会崩溃,你必须重做系统的这一部分。
所以,我的建议是使用CouchDB以外的东西来计算你的账户余额,这样会容易得多。

e4eetjau

e4eetjau5#

作为对OP问题的回应,Couch可能不是最好的选择。使用视图是跟踪库存的好方法,但是钳制到0或多或少是不可能的。问题是当你读到视图的结果时的竞争条件,决定你可以使用“锤子-1”项目,然后写一个doc来使用它。问题是,如果视图的结果是有〉0个hammer-1,那么没有原子的方法来只写doc来使用hammer。如果100个用户同时查询视图并看到1个hammer-1,那么他们都可以编写一个doc来使用hammer 1,结果是-99个hammer-1。实际上,竞争条件相当小--如果您的DB运行localhost,那么竞争条件非常小。但是一旦您进行了扩展,并且有一个非现场的DB服务器或集群,那么这个问题将变得更加明显。无论如何,在一个与金钱相关的关键系统中出现这种竞争条件是不可接受的。
对MrKurt回复的更新(可能只是注明了日期,或者他可能不知道CouchDB的一些特性)
视图是在CouchDB中处理余额/库存之类事情的好方法。
你不需要在一个视图中发出docid和rev。当你检索视图结果时,你可以免费得到这两个参数。发出它们--特别是像字典这样的冗长格式--只会让你的视图变得不必要的大。
跟踪库存余额的一个简单视图应该看起来更像这样(也是我的想法)

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

而reduce函数则更加简单

_sum

这使用了一个built in reduce function,它只对所有具有匹配键的行的值求和。
在该视图中,任何文档都可以具有成员“InventoryChange”,该成员将product_keyMap到它们的总库存中的变化。

{
    "_id": "abc123",
    "InventoryChange": {
         "hammer_1234": 10,
         "saw_4321": 25
     }
}

将添加10个hammer_1234和25个saw_4321。

{
    "_id": "def456",
    "InventoryChange": {
        "hammer_1234": -5
    }
}

会烧掉库存中的5把锤子。
在这个模型中,您永远不会更新任何数据,只会追加数据。这意味着不会发生更新冲突。所有更新数据的事务性问题都将消失:)
这个模型的另一个优点是DB中的任何文档都可以在库存中添加和减少项目。这些文档中可以包含各种各样的其他数据。您可能有一个“Shipment”文档,其中包含一堆关于接收日期和时间、仓库、接收员工等的数据,只要该文档定义了InventoryChange,它就会更新库存。就像“销售”文档一样,和一个“DamagedItem”文档等。查看每个文档,他们都读得很清楚。视图处理了所有的困难工作。

scyqe7ek

scyqe7ek6#

实际上,在某种程度上你可以。看一看HTTP Document API,向下滚动到标题“用一个请求修改多个文档”。
基本上,您可以在对 URI /{dbname}/_bulk_docs 的单个post请求中创建/更新/删除一组文档,它们要么全部成功,要么全部失败。不过,该文档警告说,这种行为将来可能会改变。
编辑:正如预测的那样,从0.9版本开始,批量文档不再以这种方式工作。

hxzsmxv2

hxzsmxv27#

只需使用SQLite一类的轻量级解决方案进行事务处理,并在事务成功完成后复制它,并在SQLite中标记它已复制
SQLite表格

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

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

相关问题