我们想在公司试点MongoDB。我们选择了一个相对简单的项目-短信网关。它允许我们的软件向蜂窝网络发送短信,并且网关通过不同的通信协议实际与提供商通信。网关还管理消息的计费。申请该服务的每个客户都必须购买一些积分。当发送消息时,系统会自动减少用户的余额并拒绝访问,如果余额不足。此外,因为我们是第三方短信提供商的客户,我们也可能有自己的余额。我们也必须跟踪这些余额。
我开始思考如果简化一些复杂性(如外部计费,排队短信发送),如何使用MongoDB存储所需数据。来自SQL世界的我会为用户创建一个单独的表,为SMS消息创建另一个表,并为存储与用户余额相关的交易创建一个表。假设我在MongoDB中为所有这些内容创建单独的集合。
在这个简化系统中,想象一下以下步骤的短信发送任务:
1. 检查用户是否有足够的余额;如果余额不足,则拒绝访问。 2. 发送和存储带有详细信息和成本的短信到SMS集合中(在实时系统中,该消息将具有状态属性,并且任务将根据其当前状态选择进行传递并设置短信价格)。 3. 减少已发送消息的成本的用户余额。 4. 记录交易到交易集合中。
那么问题出现在哪里?MongoDB仅能对一个文档进行原子更新。在先前的流程中,可能会发生某种错误,导致消息存储在数据库中,但未更新用户余额和/或未记录交易。
我想出了两个解决方案:
创建一个单一的用户集合,将余额作为字段存储,将与用户相关的交易和消息作为子文档存储在用户文档中。由于我们可以原子地更新文档,这实际上解决了交易问题。缺点是:如果用户发送许多短信,文档的大小可能会变大,达到4MB文档限制。也许我可以在这种情况下创建历史文档,但我认为这不是一个好主意。此外,如果我向同一个大型文档推送越来越多的数据,我不知道系统会有多快。
为用户创建一个集合,为交易创建一个集合。可以有两种类型的交易:充值(余额增加)和发送消息(余额减少)。交易可以有一个子文档;例如,在发送消息中,SMS的详细信息可以嵌入交易中。缺点是:我没有存储当前用户余额,因此每次用户尝试发送消息时都必须计算余额以确定消息是否能够通过。随着存储的交易数量增加,我担心这种计算会变得很慢。
我有点困惑该选择哪种方法。还有其他解决方案吗?我在网上找不到关于如何解决这些问题的最佳实践。我想许多试图熟悉NoSQL世界的程序员在开始时都会面临类似的问题。