如何防止 Web 应用程序中的竞争条件

How to prevent race conditions in a web application

我有一个网络应用程序,用户可以通过单击按钮进行注册 "Join"。网站上可以有这么多用户,这就是为什么要保持我的数据库查询速度快;我选择不在数据库中添加 foriegnkey 约束(虽然它是关系数据库)。

现在,当具有相同 userId 的用户在两个不同的浏览器中打开应用程序并同时点击 "Join" 按钮时会发生什么情况;同一用户的两行被添加到数据库中,这是错误的。

我必须阻止的想法是:

  1. 在存储过程和事务中执行 check/insertion 逻辑,事务隔离级别为 SQL 为 SERIALIZABLE;但是使用这种方法 table 将被锁定,即使两个不同的用户同时点击 "JOIN" 按钮也是如此。
  2. 在 c# 中使用 lock 关键字并从其中执行 check/insertion 逻辑,但我相信如果同一用户来自两个浏览器,他们将获得自己的锁并且仍然能够在数据库中拥有两个条目。同样对于不同的用户,它可能会产生问题,因为其他人的代码将等待第一个释放资源。
  3. 使用 EntityFramework 开箱即用支持的 Optimistic 并发,但我不确定它是否能解决我的问题。

你能帮我解决这个问题吗?

您可以通过在用户名中创建唯一索引轻松解决您的问题。所以,只有第一个会被保存。下一个将被报告为错误,因为它会破坏唯一索引。

其实应该是主键

根据您的评论,您的 table 很大。因此,在整个 table 中查找一行而不在每个插入操作中使用索引肯定比在每个 insert/delete/update 操作中更新索引更糟糕。你应该考虑这个。

无论如何,如果值已经存在则不插入问题的唯一方法就是检查它。

乐观并发与此无关。开放式并发与读取数据、修改数据和保存更改有关,而无需锁定 table。可以在以下步骤中解释乐观并发的作用:

  1. 从数据库中读取原始行,没有任何锁或事务
  2. 应用修改原始行
  3. 当应用程序尝试保存更改时,它会检查数据库中的行是否与在步骤 1 中读取时的行完全相同。如果是,则保存更改。如果不是,则抛出并发异常。

所以乐观并发帮不了你

我坚持使用唯一索引,这是最安全、最简单、可能更有效的解决方案。

我会使用实体及其乐观并发。

它会把它包裹在一个事务中,为你处理这些问题。请记住将身份和主键都放在 table 上。如果用户名必须是唯一的,则在 table.

上添加唯一注释