NoSQL中的事务处理?

83

我正在研究用NoSQL来替代数据库进行扩展。如果我需要基于事务的操作,而且这些操作对数据完整性要求很高,应该怎么做?


4
FYI... NoSQL数据库仍然是数据库,只是不具备关系型数据库的特点。至于事务处理,一个事务仅仅是查询和更新操作的逻辑分组。非关系型数据库仍然提供这两个功能。哪些内容与哪些内容相关敏感? - joejoeson
1
好的,我想要进行货币交易,或者至少考虑一下。但是在这方面我仍然希望保持一些诚信。 - Timmy
3
您有多少个数据的 terabyte,以至于您不能使用内置事务支持的标准主流 RDBMS? - gbn
1
@gbn 数据量的大小与使用NoSQL数据库没有任何关系。也许他想要摆脱关系型数据库中的EAV模型。 - Green
11个回答

49

一般来说,与关系型数据库相比,NoSQL的解决方案具有较轻的事务语义,但仍然在某个级别上提供原子操作的功能。

通常情况下,那些进行主-主复制的解决方案在一致性方面提供的较少,可用性则更高。因此,应该选择正确的工具来解决相应的问题。

许多解决方案在单个文档(或行等)级别上提供事务支持。例如,在MongoDB中,单个文档可以实现原子性,但文档可以相当丰富,所以这通常可以很好地工作——更多信息请参见此处


6
有些 NoSQL 数据库(比如 MarkLogic)实际上提供真正的 ACID 事务。 - Eric Bloch
6
RavenDB 同样提供真正的 ACID 事务。 - Matt Johnson-Pint
6
FoundationDB 还在多节点集群中提供多键 ACID 事务。 - eonil
6
Neo4j是一种NoSQL存储,具备ACID属性。 - Nadjib Mami
4
RavenDB不提供真正的ACID事务,它使用一种名为“快照隔离”的弱隔离形式。它通过外部协调器提供全局事务,但不建议使用。 - Akira Yamamoto
显示剩余2条评论

18
这是我找到的最接近的答案,适用于任何NoSQL数据库。 它来自Heroku.com的Adam Wiggins在2007年的博客文章中:
“使用数据库事务包装从一个银行账户向另一个账户转移资金的旧例子是完全错误的。正确的解决方案是存储分类帐事件列表(帐户之间的转账)并将当前余额显示为分类帐的总和。如果您正在使用函数式语言进行编程(或以这种方式思考),则显而易见。”
来源:http://adam.heroku.com/past/2007/12/17/a_world_without_sql/(他的网站对可扩展性的想法非常有帮助。)
我理解以上段落的意思如下:
  1. 创建会员账户的数据库。
  2. 创建消息队列,命名为“ledger”。
  3. 添加后台工作者以完成队列中的每个请求。
关于队列/后台工作者的更多信息:http://adam.heroku.com/past/2009/4/14/building_a_queuebacked_feed_reader_part_1/ 客户(也称为会员或客户)按照以下步骤取款:
  1. 提交取款请求。
  2. 请求被发送到服务器。
  3. 服务器将其放入队列中。消息是:“取出5000美元。”
  4. 客户端显示:“请等待请求正在被执行…”
  5. 客户端机器每2秒轮询服务器,询问:“请求是否已被执行?”
  6. 在服务器上,后台工作者以先进先出的方式履行其他成员先前的请求。最终,他们处理您客户端的取款请求。
  7. 一旦请求已被执行,客户端将获得一个包含其新余额的消息。

如果您熟悉Node.js或Ruby/Rack,可以使用Heroku.com快速创建小型模拟。

总体思路似乎非常简单,并且比使用嵌入式在数据库中的事务要好得多,这使得它难以扩展。

免责声明:我尚未以任何方式实施此功能。我只是出于好奇心阅读这些内容,即使我没有实际需要。是的,@gbn正确,具有事务的关系型数据库可能已足够满足Timmy和我的需求。尽管如此,使用开源工具和名为“A Tornado of Razorblades”的how-to网站来推动NoSQL数据库的发展到哪个程度将是有趣的。


39
对于“hello world”交易示例的批评似乎有些奇怪。如果在创建其中一个“账本事件”的过程中出现了问题,会发生什么?那么该账户的余额将是错误的。对我来说,这听起来不像是可行的事务替代方案。 - user330315
24
这个链接网页对于几乎所有金融系统中 ACID 的必要性表现出惊人的无知。首先,文章提倡“性能”,但忽略了从历史记录中读取每个交易以处理新交易所带来的性能成本。其次,更重要的是,如果同一个账户上存在并发请求,并且业务交易包含多个实体的更新,该解决方案如何工作?如果服务器在处理过程中崩溃会发生什么? - Andrew not the Saint
2
这是关于两阶段提交的内容。在谷歌上搜索一下,你会发现可以在没有事务的情况下实现一致性。 - Papipo
2
安德鲁,如果你的卡交易在中途失败了会发生什么?你见过有反向交易的银行对账单吗? - Alistair

17

NoSQL 涵盖了一系列不同的工具和服务,包括键值存储、文档存储、图形数据库和宽列存储。它们通常试图通过分布式数据处理来提高数据存储的可扩展性。

事务需要数据库执行用户操作的 ACID 特性。ACID 限制了如何提高可扩展性:大多数 NoSQL 工具放松了操作的一致性标准以获得容错性和可用性,这使得实现 ACID 事务非常困难。

分布式数据存储的一个常被引用的理论原因是 CAP 定理:一致性、可用性和分区容错性不能同时实现。根据放弃哪些特性,可以将 SQL、NoSQL 和 NewSQL 工具进行分类;可以在这里找到一个好的图表。

替代 ACID 的一组较弱要求是 BASE("基本可用、软状态、最终一致性")。然而,最终一致性的工具("最终所有对项目的访问将返回最后更新的值")在银行等事务应用中几乎不可接受。在这里,一个好的想法是使用内存、列式和分布式 SQL/ACID 数据库,例如VoltDB;我建议看看这些 "NewSQL" 解决方案。


大多数这些工具放弃了一致性,因此也就放弃了ACID。看起来你把CAP中的一致性和ACID中的一致性混淆了。CAP中的C表示数据的所有副本都是相等的,而ACID中的C是一个模糊和不明确的术语...一般来说,可用性并不与ACID相矛盾。Google Spinner的例子证明了这一点。 - Alexey
ACID一致性要求事务作为客户端操作系列只能起始于和结束于有效的数据库状态。它仅与CAP中的C类似,因此没错,它们并不相同也不矛盾。在AP系统中实现ACID事务通常很困难,这通常被认为是可扩展性的代价。我重新表述我的答案。回顾现在,我发现CAP定理和CAP分类过于模糊,没有提供真正帮助来对这些工具进行分类。我认为CAP只是分布式系统设计妥协的有趣理论例子。 - csaba

14

在这个帖子中,我想评论有关金钱转账建议的问题。交易是您真正想要与货币转移一起使用的东西。

给出的关于如何排队传输的示例非常好和整洁。

但是在现实生活中,转移资金可能包括向其他帐户支付费用或付款。人们可以因使用来自另一个帐户的某些卡而获得奖金,或者他们可能需要从他们的帐户中向同一系统中的另一个帐户缴纳费用。这些费用或付款可能因财务交易而异,您可能需要保持簿记系统以显示每个交易的借方和贷方。

这意味着您想要同时更新多行,因为一个帐户的贷方可能是一个或多个帐户的借方。首先,您锁定行,以便在更新之前不会更改任何内容,然后确保所写入的数据与交易一致。

这就是为什么您真正想要使用事务。如果对一行写入的任何内容出了问题,您可以回滚整批更新,而不会使财务交易数据无法匹配。


1
处理事务的副作用还有其他更好的方法。事务是最初的事件,只要以原子方式记录,任何其他错误或问题都可以追溯到该事件。 - Chris Nicola

6
使用一次交易和两个操作(例如,一个人支付5000美元,第二个人收到5000美元)的问题是你有两个优先级相同的帐户。你无法使用一个帐户来确认第二个(或反过来)。在这种情况下,你只能保证一个帐户将是正确的(即被确认),第二个(进行确认)可能发生失败。让我们看看为什么它可能会失败(使用消息方法,发送方由接收方确认):
  1. 向接收方帐户写入+ 5000美元
  2. 如果成功-向发送方帐户写入- 5000美元
  3. 如果失败-重试或取消或显示消息
除了#1之外都不能保证安全。但是,这可以实现为安全性不需要事务和NoSQL。您始终可以使用第三个实体,该实体将从发送方和接收方确认,并保证执行了您的操作:
  1. 生成唯一的交易ID并创建交易实体
  2. 向接收方帐户写入+$5,000(引用交易ID)
  3. 如果成功-设置交易状态为send
  4. 向已发送帐户帐户写入-$5,000(引用交易ID)
  5. 如果成功-设置交易状态为receive
此交易记录将确保发送/接收消息是正确的。现在,您可以通过交易ID检查每条消息,并且如果它具有接收或完成状态,则将其纳入用户余额中。

1
如果步骤3和5失败了怎么办?这会增加很多复杂性,这就是为什么数据库事务非常有用的原因。 - ajeetdl
通常这样的系统不仅依赖于 SQL 的能力来验证交易。在实际场景中,信用和借记大多发生在时间和银行之间 - 超出了 SQL 或 NoSQL 的能力范围...这种情况只能由良好设计的架构来处理 - 该架构可以平稳地处理系统内或跨系统的交易。 - Kalpesh Popat
我认为这种方法很好。然而,我们还必须考虑将交易部分进行分布式执行(比如一部分在微服务1中运行,另一部分在微服务2中运行,后者在云端的不同域中运行)。如果没有一些背景任务来通过适当地设置存在于多个服务器上的相关记录的状态来处理这些交易,则在NoSQL中进行分布式交易很难(但不可避免)。 - Prasad

2
根据您的数据库,但是...我认为一般来说,您可以使用'乐观事务'来实现这一点,但我想您应该确保了解数据库实现的原子性保证(例如,哪种写入和读取操作是原子性的)。
如果有所帮助,似乎有一些关于HBase事务的讨论

1

在SQL DB中,您总是可以使用NoSQL方法。 NoSQL似乎通常使用“键/值数据存储”:您始终可以在首选的RDBMS中实现此功能,从而保留诸如事务、ACID属性、DBA友好支持等优点,同时实现NoSQL性能和灵活性等好处,例如通过类似以下的表:

CREATE TABLE MY_KEY_VALUE_DATA
(
    id_content INTEGER PRIMARY KEY,
    b_content  BLOB
);

好处是您可以在此添加额外的字段,将您的内容链接到其他适当的关系表中,同时仍然将庞大的内容保留在主BLOB(或TEXT如果合适)字段中。

个人而言,我更喜欢使用TEXT表示,这样您就不会因为处理数据而被绑定到某种语言上,例如使用序列化的Java意味着您可以从Perl中访问内容以进行报告。 TEXT也更容易调试和开发人员通常更容易使用。


1

1

看一下Scalaris,它是一个具有强一致性和实现事务的NoSQL数据库。


1
这就是为什么我正在创建一个NoSQL文档存储解决方案,以便能够在企业应用程序中使用“真正”的事务和非结构化数据方法的强大功能。请查看http://djondb.com,并随时添加您认为有用的任何功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接