蒙古有多酸?


关系数据库通常保证ACID properties与事务(读和写)处理的可靠性相关。MySQL和PostgreSQL是提供这些属性作为卖点的数据库示例。

NoSQL运动用酸性合规性换取其他属性,如100%可用性,蒙古数据库是该领域的领导者。我不是说Mongo不提供这些保证是不好的,因为它们不是用例中最重要的约束;只是当在生产系统中从MySQL和类似产品过渡时,你必须意识到你牺牲了什么。

实际上,这些属性的一些变体比它们的经典对应物更好地映射到面向对象编程;例如,基于文档的事务比MySQL的任意宽事务更符合领域驱动的设计聚合模式。

原文

以下是关系数据库管理系统如何解释ACID属性的概述:

  • 原子性要求每个事务都完整地执行,或者在没有任何改变的情况下失败。
  • 一致性要求数据库只从一个有效状态传递到下一个有效状态,没有中间点。
  • 隔离要求如果事务并发执行,结果等同于它们的串行执行。一个事务不能看到应用另一个事务的部分结果。
  • 持久性意味着提交事务的结果是永久性的,即使数据库立即崩溃或断电。

原子数

MongoDB只提供文档范围的事务:写入永远不会部分应用于插入或更新的文档。从失败或成功的意义上来说,该操作是原子的,对于整个文档而言。

因此,至少Mongo不像使用一堆文件那样低级,因为等价的是一组文件,每个文件都有自己的锁。

不存在跨越多个文档或集合的原子变化的可能性:要么将应用程序的状态变化建模为附加文档,要么不能在需要这些数据库事务的地方使用Mongo。一个经典的例子是用以下方法模拟银行账户的操作活动文档,而不是单一的账户第一:运动的插入不是成功就是失败。

如果您必须自己实现2阶段提交,那么只需为您应用程序的持久性组件使用一个关系数据库(我不会对其余的数据说什么)。(

一致性

即使在副本集配置中,主Mongo服务器也是所有写入的目标;单一服务器的一致性很容易保证。

辅助节点相对于主节点可能已经过时,因为最终的一致性只能保证,如果在足够长的时间内没有写入,它们相对于主节点将是最新的。但是,默认情况下,辅助服务器不能应答读取,因此,只有在您希望并配置它们这样做时,您才能够以不一致的代价来分发流量。一致性和可用性是不相容的,因为CAP定理——你必须选择。

隔离

几个月前,MongoDB有一个服务器范围的写锁!我猜你可以说这是一个完美的隔离机制。相反,只要没有人在写,读锁就可以同时被多个连接占用。

从2.2版本开始,Mongo开始使用database-specific write locks,许多操作在遇到诸如页面错误等缓慢事件时开始产生锁。Mongo在未来将至少转向集合特定锁。

但是,请记住,Mongo模型类似于关系数据库的事务自动提交:您不能真正谈论隔离,因为每个操作都可以立即访问任何其他连接。

持久性

写操作的持久性是Mongo最大的问题。在2.0版之后,单台服务器的情况是:

  • 数据库文件每60秒提交一次。
  • 操作日志(预写日志)每100毫秒提交一次。

这些参数可通过syncdelayjournalCommitInterval配置选项。提交文件意味着在其上发出操作系统同步命令;与所有数据库一样,日志非常频繁地同步,以便在发生崩溃或强制关闭时,可以从日志中重建数据库。

MySQL所做的就是提交日志每个写操作(实际上是每个提交的事务)。Mongo开发人员表示,他们不会这样做,因为在许多情况下,即使在同步(硬件缓冲)之后,操作系统也不会将文件写入磁盘,而且等待恢复的时间会影响可用性。只有电池供电的磁盘控制器才能保证这些写入不会在故障时丢失,但这不是一种常见的配置。关闭硬件缓冲将"very slow"

因此,如果服务器崩溃,日志最后一次提交后接受的写入将会丢失。罕见但可能的情况。

在多台服务器之间,服务器可能会在将更新传输到任何辅助服务器之前死亡,默认情况下,这些更新是异步同步的。如果发生故障的主服务器是可恢复的,则可以将这些更新合并回来。

但是,您可以指定在考虑写操作完成之前,将写操作复制到至少N个辅助节点,并提供写操作选项(集群持久性)。书写问题甚至可以针对单次插入进行定制。不管怎样,跳过在磁盘上写只是为了等待网络呼叫完成,这有点奇怪。

因此,很久以前,Mongo只支持集群持久性,通过将一切复制到辅助服务器。在假设不相关的故障的情况下,这可以保护您,因为两台服务器永远不会在同一时间范围内发生故障(意味着一台发生故障,另一台在第一台修复之前发生故障)。)但是,如果您的数据中心断电,那么只有Mongo 1.8中引入的日志功能才能拯救您。

这一讨论的要点是蒙戈没有提供durability by default(过期帖子),但让你tune the configuration of a replica set如果你想牺牲足够的性能。