我需要执行事务(开始、提交或回滚)、锁定(选择以进行更新)。如何在文档模型数据库中执行这些操作?编辑:案情是这样的:
我可以用CouchDB解决这个问题吗?
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_key和claimed_by字段。如果您正在销售一个Hammer模型,并且有20个这样的模型要销售,则可能会有包含hammer-1、hammer-2等等,以表示每个可用的锤子。然后,我会创建一个视图,提供可用锤子的列表,并使用reduce函数显示“总数”。这些都是现成的,但应该能让您了解工作视图的样子。
_rev
product_key
claimed_by
hammer-1
hammer-2
唐飞
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项的总数,因此您可以知道有多少“锤子”可供购买。
inventory_ticket
注意事项
对于您所提出的特定问题,这个解决方案代表了大约3.5分钟的思考时间。可能有更好的方法来实现这一点!也就是说,它确实大大减少了冲突更新,并减少了对新更新冲突的响应。在这种模式下,您不会有多个用户试图更改主产品条目中的数据。在最坏的情况下,您将有多个用户试图申请一个票证,如果您已经从视图中获取了多个票证,则只需转到下一个票证并重试。参考:https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
wixjitnu2#
扩展Kurt先生的答案。在很多情况下,你不需要按顺序兑换股票票。你可以从剩余的票中随机选择,而不是选择第一张票。如果有大量的票和大量的并发请求,你将大大减少对这些票的争用,而不是每个人都试图获得第一张票。
rnmwe5a23#
剩余交易的一种设计模式是在系统中创建一种“紧张”。对于银行帐户交易的常见示例用例,您必须确保更新两个相关帐户的合计:
应该在后台进程中对所有“紧张文档”进行紧张扫描,以保持系统中紧张的时间较短。在上面的例子中,当第一个帐户已经更新,但第二个帐户还没有更新时,将有一个短时间的预期不一致。如果Couchdb是分布式的,必须考虑到这一点,同样的方法你将处理最终的一致性。另一种可能的实现完全避免了对事务的需要:只需存储张力文档,并通过评估每个相关的张力文档来评估系统的状态。在上面的示例中,这意味着帐户的总额仅确定为涉及该帐户的事务文档中的总和值。在Couchdb中,您可以将其很好地建模为map/reduce视图。
s3fp2yjn4#
不,CouchDB通常不适合事务性应用程序,因为它不支持集群/复制环境中的原子操作。CouchDB牺牲了事务处理能力来支持可伸缩性。为了实现原子操作,您需要一个中央协调系统,这限制了您的可伸缩性。如果你能保证你只有一个CouchDB示例,或者修改特定文档的每个人都连接到同一个CouchDB示例,那么你可以使用冲突检测系统,通过上面描述的方法创建一种原子性,但是如果你后来扩展到一个集群,或者使用像Cloudant这样的托管服务,它就会崩溃,你必须重做系统的这一部分。所以,我的建议是使用CouchDB以外的东西来计算你的账户余额,这样会容易得多。
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”文档等。查看每个文档,他们都读得很清楚。视图处理了所有的困难工作。
scyqe7ek6#
实际上,在某种程度上你可以。看一看HTTP Document API,向下滚动到标题“用一个请求修改多个文档”。基本上,您可以在对 URI /{dbname}/_bulk_docs 的单个post请求中创建/更新/删除一组文档,它们要么全部成功,要么全部失败。不过,该文档警告说,这种行为将来可能会改变。编辑:正如预测的那样,从0.9版本开始,批量文档不再以这种方式工作。
hxzsmxv27#
只需使用SQLite一类的轻量级解决方案进行事务处理,并在事务成功完成后复制它,并在SQLite中标记它已复制SQLite表格
txn_id , txn_attribute1, txn_attribute2,......,txn_status dhwdhwu$sg1 x y added/replicated
您也可以删除成功复制的事务。
7条答案
按热度按时间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_key
和claimed_by
字段。如果您正在销售一个Hammer模型,并且有20个这样的模型要销售,则可能会有包含hammer-1
、hammer-2
等等,以表示每个可用的锤子。然后,我会创建一个视图,提供可用锤子的列表,并使用reduce函数显示“总数”。这些都是现成的,但应该能让您了解工作视图的样子。
唐飞
这给了我一个可用“门票”的列表,按产品密钥。当有人想买锤子时,我可以抓住一组这样的门票,然后迭代发送更新(使用X1 M8 N1 X和X1 M9 N1 X),直到我成功地认领一张(之前认领的门票将导致更新错误)。
减少
这个reduce函数只返回未认领的
inventory_ticket
项的总数,因此您可以知道有多少“锤子”可供购买。注意事项
对于您所提出的特定问题,这个解决方案代表了大约3.5分钟的思考时间。可能有更好的方法来实现这一点!也就是说,它确实大大减少了冲突更新,并减少了对新更新冲突的响应。在这种模式下,您不会有多个用户试图更改主产品条目中的数据。在最坏的情况下,您将有多个用户试图申请一个票证,如果您已经从视图中获取了多个票证,则只需转到下一个票证并重试。
参考:https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
wixjitnu2#
扩展Kurt先生的答案。在很多情况下,你不需要按顺序兑换股票票。你可以从剩余的票中随机选择,而不是选择第一张票。如果有大量的票和大量的并发请求,你将大大减少对这些票的争用,而不是每个人都试图获得第一张票。
rnmwe5a23#
剩余交易的一种设计模式是在系统中创建一种“紧张”。对于银行帐户交易的常见示例用例,您必须确保更新两个相关帐户的合计:
应该在后台进程中对所有“紧张文档”进行紧张扫描,以保持系统中紧张的时间较短。在上面的例子中,当第一个帐户已经更新,但第二个帐户还没有更新时,将有一个短时间的预期不一致。如果Couchdb是分布式的,必须考虑到这一点,同样的方法你将处理最终的一致性。
另一种可能的实现完全避免了对事务的需要:只需存储张力文档,并通过评估每个相关的张力文档来评估系统的状态。在上面的示例中,这意味着帐户的总额仅确定为涉及该帐户的事务文档中的总和值。在Couchdb中,您可以将其很好地建模为map/reduce视图。
s3fp2yjn4#
不,CouchDB通常不适合事务性应用程序,因为它不支持集群/复制环境中的原子操作。
CouchDB牺牲了事务处理能力来支持可伸缩性。为了实现原子操作,您需要一个中央协调系统,这限制了您的可伸缩性。
如果你能保证你只有一个CouchDB示例,或者修改特定文档的每个人都连接到同一个CouchDB示例,那么你可以使用冲突检测系统,通过上面描述的方法创建一种原子性,但是如果你后来扩展到一个集群,或者使用像Cloudant这样的托管服务,它就会崩溃,你必须重做系统的这一部分。
所以,我的建议是使用CouchDB以外的东西来计算你的账户余额,这样会容易得多。
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。当你检索视图结果时,你可以免费得到这两个参数。发出它们--特别是像字典这样的冗长格式--只会让你的视图变得不必要的大。
跟踪库存余额的一个简单视图应该看起来更像这样(也是我的想法)
而reduce函数则更加简单
这使用了一个built in reduce function,它只对所有具有匹配键的行的值求和。
在该视图中,任何文档都可以具有成员“InventoryChange”,该成员将product_keyMap到它们的总库存中的变化。
将添加10个hammer_1234和25个saw_4321。
会烧掉库存中的5把锤子。
在这个模型中,您永远不会更新任何数据,只会追加数据。这意味着不会发生更新冲突。所有更新数据的事务性问题都将消失:)
这个模型的另一个优点是DB中的任何文档都可以在库存中添加和减少项目。这些文档中可以包含各种各样的其他数据。您可能有一个“Shipment”文档,其中包含一堆关于接收日期和时间、仓库、接收员工等的数据,只要该文档定义了InventoryChange,它就会更新库存。就像“销售”文档一样,和一个“DamagedItem”文档等。查看每个文档,他们都读得很清楚。视图处理了所有的困难工作。
scyqe7ek6#
实际上,在某种程度上你可以。看一看HTTP Document API,向下滚动到标题“用一个请求修改多个文档”。
基本上,您可以在对 URI /{dbname}/_bulk_docs 的单个post请求中创建/更新/删除一组文档,它们要么全部成功,要么全部失败。不过,该文档警告说,这种行为将来可能会改变。
编辑:正如预测的那样,从0.9版本开始,批量文档不再以这种方式工作。
hxzsmxv27#
只需使用SQLite一类的轻量级解决方案进行事务处理,并在事务成功完成后复制它,并在SQLite中标记它已复制
SQLite表格
您也可以删除成功复制的事务。