在我的微服务中,我使用带有mongo (蔚蓝宇宙db)的spring引导。目前,我有一个订单集合存储订单。这些文档的字段userId在创建文档时为空。
然后,向用户请求向他分配一个订单,然后填充这个字段。问题是,我想确保用户最多有一个订单分配给他。如果同一个用户在同一时刻收到两个请求,那么其中只有一个应该将订单分配给用户,两者都应该返回指定的订单。我已经部署了几个微型服务的副本。
对于一些性能限制,我不能用唯一的索引来保证这个约束。
我在考虑将分布式锁机制(如redis )放在用户将其锁定的位置,然后再将顺序分配给该用户。
是否有更好的方法来满足这一要求?
发布于 2023-04-13 16:04:44
要确保用户由于某些性能限制而分配的顺序最多只有一个,我不能用唯一的索引来保证这个约束。
我直截了当地不相信,没有基准结果。但是,假设不存在支持所需读写事务速率的技术。
我将为100亿个用户记录进行设计,并且每秒稳定地订购1000份订单.
..。同时接收两个请求,对于同一个用户,其中只有一个应该将订单分配给用户,两者都应该返回指定的订单。我已经部署了几个微型服务的副本。
使用波斯特格斯或其他主要的关系数据库管理系统。分区您的用户表,并向其添加一个NULLable currentOrder属性。在这里,微服务副本并不有趣,但是承载分区关系的节点数量是。
..。两者都应该返回指定的订单。
在API级别上,这很好。
数据库事务将是currentOrder为NULL的用户的更新,如果尝试两个重叠事务,它将失败,这正是我们所希望的。输家可以稍后重新查询以检索获奖订单ID,并将其作为API响应返回。
这里没有什么稀奇的事情,因为用户ID的散列使两个客户端都可以与有兴趣的用户所在的节点进行交互。我们不需要2PC或辅助日志表或应用程序可见的分布式锁定,因为SQL更新就足够了。
tl;dr:依赖RDBMS特性(节点间分区、高可用性、锁定、事务)在所需的性能级别交付所需的API功能。
编辑
用RDBMS方法我能赢得什么?
酸交易是一个很大的问题。他们让基础设施为细节操心,而不是让应用程序开发人员。经过良好的测试,他们更有可能得到正确的细节。
下面是我对您选择的NoSQL持久化层的了解:
https://en.wikipedia.org/wiki/Cosmos_DB#Partitioning
随着分区容器的引入,Cosmos DB在2016年增加了自动分区功能。在幕后,分区容器跨越多个物理分区,其中包含由客户端提供的分区键分发的项。
这听起来应该足以实现你想要的,让客户在一个横向的高可用性设置,同意将他们的更新发送到一个单一的仲裁节点。Cosmos是否以Postgres和其他酸性解决方案的方式,公开了足够强大的API来满足您的需求?我不知道,我从来没有用过那个产品。
如果您选择的NoSQL方法需要其他技术的帮助,那么这可能是列出利弊的好时机,权衡放松ACID保证是否适合您的业务需求。
红宝石是一个不错的缓存,我已经习惯了良好的效果。我已经看到了操作问题,当它被视为一个实体的真理之源,而这个实体只出现在Redis中。
https://redis.io/docs/manual/transactions
那回滚呢?Redis不支持回滚事务..。
也许这是一个易于使用的范例,应用程序开发人员很少出错。我不能说,因为我没有使用Redis的那个方面。我肯定在各种主流酸性技术如何在充满活力的环境中经受考验中找到了一些价值。降低风险和降低测试成本可能会影响ROI的规模。
我们担心在更新用户记录时会发生竞争。对于currentOrder来说,它是一个不透明的Mongo是非常好的--没有必要从用户到订单之间建立FK关系。将用户记录存储在后台,这缺乏方便的事务性支持,听起来似乎您选择在应用程序层承担一些额外的负担。这是否值得,这是一种商业权衡。有些队擅长测试比赛。历史表明,这不是一件容易做到的事情。
发布于 2023-04-13 11:29:08
您要执行的条件是,用户只有一个订单。
这似乎是用户的(currentOrder)属性。因此,您应该能够对用户执行一个findAndModify(),其中包含一个没有设置currentOrder的条件。
您已经说过,这是一种Microservice架构--这是否意味着用户对象由独立的(用户)服务控制?如果是这样,您仍然可以在用户服务上实现一个“分配当前订单”端点,该端点为您执行findAndModify()。
我有点担心,您正在创建没有UserId的订单--因为您可能最终会遇到孤儿--如果后来发现另一个订单已经分配给了给定的用户--您是否可以使用初始版本中的UserId设置创建订单。
如果您可以使用指定的用户创建所有订单,那么最终可能会在orders集合中为同一个用户分配多个订单,但是用户集合中只有一个"currentOrder“,因此您可以:
假设我们不允许使用用户集合来解决问题(以前的解决方案)。
您所关注的竞赛条件只存在有限的时间(让我们称之为时间T),特别是运行两个MongoDB操作所需的时间:
时间T可能比最初的时间长,例如,您可能需要允许MongoDB集群中的复制延迟。不管怎么说,关键的观察是,我们只需要保护时间T的比赛条件-在那之后,记录将存在于MongoDB和不需要保护。
因此,您所需要的只是一个解决方案,在尝试为用户U分配现有的任务之后,防止额外的请求尝试在T秒内向用户U分配任何进一步的订单。
。
我认为甚至可以使用MemCache add命令来解决这个问题:
https://github.com/memcached/memcached/wiki/Commands#add
即:
发布于 2023-04-13 12:07:45
因此,您的用户不是创建订单的人,而是您的员工(“您”=将使用该软件的组织)。
第一个问题:这些用户如何能够同时要求向他们分配两个订单?在两个不同的终端并排登录,同时按回车键?或者,这是一种理论上可能发生在蓝月亮上一次的种族状况吗?
第二个问题:当用户有两个订单给他们时,会产生什么后果,什么时候会被注意到?是否有些不公平,因为他们的一名同事当时不能按该命令工作?它是否在整个订单工作流程中造成严重破坏?
第三个问题:当订单微服务可能被复制时,为什么要存储与订单的用户/订单连接?正如@DavidT所指出的,将用户处理的当前订单存储在UserService中可能更明智。您还可能有一个没有复制的"AssignedOrdersService“,它存储这些关系以避免可能的竞争条件。
https://softwareengineering.stackexchange.com/questions/445004
复制相似问题