0

我正在制作一个网站,我认为用一个宁静的架构(至少不是与这个问题相关的部分)来实现它没有意义,但它会导致共享数据库的多个服务器之间的竞争条件出现一些问题。

我的网站有关于另一个产品的用户的信息,所以它有一个用户表(虽然不是我网站的用户)。用户有很多文件。

用户和文件由自动化服务填充,而不是在站点上手动填充。该服务将文件发布到服务器,服务器解析它们并从文件中获取用户名。如果用户名是新的,它会在表中创建一个新的用户行。然后它将有关文件的信息返回给发出请求的服务。

我看到的问题是当多个请求同时进入相关对象的竞争条件时,它会导致诸如违反数据库中的唯一索引之类的事情。

例如,用户名上有一个唯一键。如果自动服务对来自同一用户的文件的 2 个请求同时进入,则此代码可能会出现问题。

var myuser = db.users.FirstOrDefault(u => u.username == username);
if(myuser == null)
{
   myuser = new user(username);
   db.AddObject(user);
}
db.SaveChanges();

请求 1 将看到没有用户名为 foo 的用户,因此 if 条件返回 true。请求 2 看到同样的事情,不知道请求 1 已经开始创建用户,当请求 2 尝试保存时,它违反了唯一密钥。

这个问题有共同的模式或解决方案吗?我知道如果服务器是 RESTful 的,这不会是一个问题,但我不认为服务改变它发出请求的方式真的可行,所以如果可能的话,我希望保持不变。现在,它只是将文件发布到服务器,不知道该文件的用户是否已经存在,或者该文件是否已发布到服务器(它可能会多次发布)。如果这些对象尚不存在,则创建这些对象,如果存在,则更新项目列表。但就服务而言,它只是想知道有关文件的某些信息,而不关心它是否已经存在于我的数据库中。

我认为尝试通过请求创建用户,然后尝试通过请求创建文件,然后在另一个请求中请求有关文件的信息,这太慢了。此外,该服务通过 Parallel.ForEach 一次运行多个请求,如果在单个线程中运行它会太慢。

4

1 回答 1

3

首先是关注点分离。如果您有一个自动服务来填充数据,那么服务(或另一个中间件)应该负责创建数据库记录。这不应该在运行时响应对您网站的请求而发生。

其次,如果你必须这样做,那就是锁的用途。对您网站的每个请求都在它自己的线程中运行。因此,如果多个线程需要访问相同的易失性资源(您的数据库),那么您需要建立乐观锁定,以便第一个线程获胜,并且任何其他线程将只能尝试与该表或行交互(取决于关于锁的类型)一旦第一个完成了它的工作。

第三,这几乎正是RESTful 架构试图解决的问题。您可以使用 ETag 对资源进行版本控制,因此任何对过时资源的 POST 尝试都将返回 HTTP 错误(409 冲突),指示客户端重新获取原始资源。

于 2013-05-21T20:22:58.957 回答