在不使用FIFO策略的情况下限制MongoDB集合中文档的数量

4
我正在开发一个应用程序来处理售票,预计会有非常高的需求。我想尝试使用MongoDB并同时使用多个并发客户端节点为node.js网站提供服务(并优雅地处理客户端故障)。
我已经阅读了 "Limit the number of documents in a collection in mongodb"(这与本次讨论没有关系),以及 "Is there a way to limit the number of records in certain collection"(但它只涉及覆盖最旧文档的固定集合)。
是否可能将集合中的文档数量限制为某个最大值,并拒绝超出该限制的文档?一个简单的例子是将售票添加到数据库,如果所有门票已经售罄,则失败。
我考虑过使用NumberRemaining文档,我可以原子性地递减它直到达到0,但如果在递减该数字并保存购票信息之间发生节点崩溃,我就会遇到问题。

存储为“票号”的门票数量是否可能超过单个MongoDB文档的最大大小(16MB)?您一次只能原子地设置一个文档(因此可能只需将成功的门票交易存储在单个文档中)。 MongoDB通常不适合像您描述的多文档事务系统,但10gen有一种可行的模式:http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/。 - WiredPrairie
这其实是个好建议,一个单一的文档可以支持大约200万张门票销售。唯一的问题是这个记录会变得非常“热”,可能会成为轻微的性能瓶颈,但这很可能是个好计划。我可以将这些文档分开存储(并在尝试将它们添加到成功购票列表之前将它们添加)。 - ForbesLindesay
如果您将其作为答案添加,我将标记为已接受 - ForbesLindesay
1
如果你想在这里保证售票的原子性,那么瓶颈将存在于表格/集合级别或文档级别,可能在文档级别更容易,因为该文档将位于内存中。 - Sammaye
3个回答

2

将门票存储在单个MongoDB文档中。由于您每次只能原子地设置一个文档,因此不应该出现可以通过使用传统的事务性数据库系统解决的文档依赖问题。

由于文档可以长达16MB,因此仅在主文档中存储ticket_id,您应该能够存储大量门票,而无需进行任何额外的复杂文档管理。虽然它可能会引入热点,但文档可能不会很大。如果它变得很大,您可以使用多个文档(将其拆分为多个文档,当一个文档“填满”时激活另一个文档)。

如果这样做不起作用,10gen有一个模式可能适合。


2阶段提交并不是原子性的,因此仍有可能出现售出的门票数量超过可用数量的情况。 - Sammaye
两阶段提交选项是伪原子性的,因为始终可以通过定期清理作业返回到一致状态。 - ForbesLindesay
@ForbesLindesay 确保作业本身的一致性,除了应用程序级别外,没有其他层面隔离等措施来确保数据库端的一致性。要有效地避免超卖门票,您需要在销售结束后回来并双重检查谁有票,谁没有。 - Sammaye
一旦两阶段提交已终止,它就完成了,只是有时会启动一个事务,然后必须回滚。 - ForbesLindesay

1

到目前为止,我唯一的解决方案是(我希望有人能改进这个):

当文档到达时将其插入到未封顶的集合中。保留ObjectID的隐式_id值,可以排序,从而按添加时间对文档进行排序。

运行所有按_id排序并限制最大文档数的查询。

要确定插入是否“成功”,请运行额外的查询,检查新插入的文档是否在最大文档数内。


0

我的解决方案是:我在另一个集合中使用额外的计数变量。该集合具有验证规则,避免计数变量变为负数。计数变量应始终为非负整数。

"count": { "$gte": 0 }

算法很简单。将计数减1。如果成功插入文档。如果失败,则表示没有剩余空间。

删除操作相反。

此外,您可以使用事务来防止故障(计数递减,但在插入操作之前服务失败)。


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