当发生写入数据库的并发 API 调用(或服务器速度较慢时)时,防止出现竞争条件
Posted
技术标签:
【中文标题】当发生写入数据库的并发 API 调用(或服务器速度较慢时)时,防止出现竞争条件【英文标题】:Prevent race condition when concurrent API calls that write to database occurs (Or when the server is slow) 【发布时间】:2014-10-07 10:45:46 【问题描述】:让我们想象一个场景,您将拥有一个用于创建用户的端点。这将在一个 RESTful 应用程序中,因此让我们假设一个富客户端调用此 API 端点。
exports.createUser = function(req,res)
if(req.body)
//Check if email has already been used
db.User.find(where:email:req.body.email).success(function(user)
if(user === null || user === undefined)
//Create user
res.send(201);
else
res.json(409,error: 'User already exists');
);
else
res.send(400);
;
如果我以非常快的速度多次调用此端点,即使您查询用户表以确保没有重复,也可以在数据库中创建具有相同电子邮件的多条记录。
我确定这是一个常见问题,但是如何防止这个问题呢?我严格限制对特定端点的请求数量,但这似乎不是一个很好的解决方案。
有什么想法吗? 非常感谢!
【问题讨论】:
您可以对 db 表设置约束以保持电子邮件的唯一性。这不会阻止错误的批准,但它会保持数据库清洁,并且您可以在随后的 ajax 调用中对创建进行 dbl-check。 许多解决方案,主要取决于您使用的数据库和技术;就像一个约束,一个 upsert 后跟一个更新,一个插入 select 的结果的单个请求,当已经插入时不返回任何行,......你能更具体地说明你期望的答案类型吗? 【参考方案1】:最简单的选择是LOCK TABLE "users" IN EXCLUSIVE MODE
在开始执行查找然后插入的事务。这确保了一次只能将一个事务写入表。
为了更好的并发,您可以:
在email
上定义UNIQUE
约束,然后跳过find
步骤。尝试insert
,如果失败,捕获错误并报告重复;或
使用insert-if-not-exists techniques known to be concurrency-safe之一
如果使用唯一约束,需要考虑的一件事是,您的应用可能会将用户标记为已禁用而不会删除它们,并且可能不希望强制禁用用户的电子邮件地址是唯一的。如果是这样,您可能需要一个部分唯一索引(请参阅文档)。
【讨论】:
以上是关于当发生写入数据库的并发 API 调用(或服务器速度较慢时)时,防止出现竞争条件的主要内容,如果未能解决你的问题,请参考以下文章