在不依赖数据库的情况下,有没有办法确保字段(比如说用户的 emailAddress
)是独一无二的。
一些常见的失败尝试:
先检查是否 emailAddress
存在(通过查询数据库),如果不存在,则创建用户。很明显,在检查窗口中,其他线程可以创建一个具有相同电子邮件的用户。因此,这种解决办法是不好的。
对负责创建用户的方法应用语言级锁。这个解决方案失败了,因为出于性能原因,我们需要服务的冗余,并且锁在单个jvm上。
使用事件存储(如akka actor的邮箱),事件是adduser消息,但由于actor行为是异步的,因此无法通知请求者(发送者)使用唯一电子邮件创建用户成功。此外,两个请求(使用相同的电子邮件)如何知道它们包含唯一的电子邮件?这可能会变得复杂。
数据库是每个线程和每个服务示例都要写入的单一数据源,实现 unique constraint
在这里。但这对于关系数据库来说是正确的。那nosql数据库呢?有些允许唯一的约束,但这不是它们的固有行为,或者可能是。
但是,如果不使用数据库来实现字段的唯一性,有什么选择呢?
2条答案
按热度按时间5ktev3wc1#
我认为你的问题更一般——“如何确保数据库写入操作成功,以及如何处理没有成功的情况?”。唯一性只是一种失败模式—您可能试图插入的值太大、数据类型错误或与外键约束不匹配。
关系数据库通过与acid兼容并在事务失败时为客户机抛出错误来解决这个问题。
您希望在没有关系数据库的情况下获得acid的一些好处。这是一个相当大的主题。解决这个问题的明显方法是在应用程序层中引入“事务”的概念。例如,在您的示例中,您可能会发送“create account(emailaddress,name,…)”消息,并让应用程序侦听“accountcreated”或“accountcreationfailed”响应。该消息的接收者负责写入数据库;你有几个选择。一种是锁定该线程(因此任何时候只有一个进程可以写入数据库);这不是超级可扩展的。我使用的另一种机制是引入状态标志—您使用“draft”标志将帐户数据写入数据库,然后检查您的约束(包括唯一性),如果满足约束(即没有其他具有相同电子邮件地址的记录),则将“draft”标志设置为“validated”,否则设置为“failed”。
vlurs2pr2#
要检查唯一性,需要存储程序的“状态”。为了安全起见,您需要能够以事务方式应用对状态的更改。
您可以使用数据库事务。一些nosql数据库也支持事务,例如redis和mongodb。您必须分别检查每个供应商以查看它们如何支持事务。在此设置中,每个客户机都将连接到数据库,它将为您处理所有详细信息。另外,根据您的用例,您应该注意隔离级别配置。
如果不考虑持久性,那么可以使用支持事务的内存中数据库。
您选择哪个状态存储,它应该支持事务。有几种方法可以实现事务并实现一致性。许多像postgresql这样的关系数据库通过实现mvcc算法来实现这一点。在分布式环境中,您必须寻找分布式事务,如2pc、paxos等。
通常每个人都依赖可用的数据存储解决方案,除非项目有奇怪或特定的需求。
最后,通信模式与这里的根本问题无关。例如,在您提到的actor案例中,在一天结束时,每个actor都必须查询状态以查找电子邮件是否存在。如果状态存储支持序列化,那么就没有问题,也不会发生冲突(将错误传递给客户端是另一个问题)。假设您使用的是postgresql。当发出insert/update查询时,它被 Package 在一个事务周围,底层的mvcc算法将处理所有事情。在高级分布式环境中,可以使用支持分布式事务的数据存储,如cockroachdb。
如果你想深入研究这些关键词:acid,隔离级别,原子性,可序列化性,cap定理,2pc,mvcc,分布式事务,分布式锁。。。