MySQL:如何保持锁定并让其他线程等待尚未发生的插入

时间:2019-12-12 18:25:21

标签: mysql sql multithreading locking

我面临着一个看似简单的问题,但事实却很难解决。我们希望保留每次获得营销(线索)的记录,因此在90天内不要购买超过一次。许多潜在客户提供商经常会同时多次向我们提供相同的潜在客户。我们希望将“接受”返回给恰好一个潜在客户提供商。

因此,让我们谈谈可行的方案:我们已经查看了过去90天内的材料,并在桌子上有记录,并且有3个提供商同时呈现线索:

select count(id) from recent_leads where 
  last_seen_at >= '2019-10-11 00:00:00' 
  and email = 'yes@example.com' for update;

线程1首先到达,并获取锁。 MySQL返回Thread1:

+-----------+
| count(id) |
+-----------+
|         1 |
+-----------+
1 row in set (0.00 sec)

Thread1发出新插入:

insert into recent_leads (email, last_seen_at)
  values ('yes@example.com', '2019-12-12 18:23:35');

线程2和线程3将阻止尝试执行同一条语句,直到线程1提交或对其事务进行回滚。然后Thread2和Thread3争夺锁,并且发生相同的过程。

因此可以按预期工作,我们对此感到满意。没有记录的时候车轮掉了。

线程1,线程2和线程3均发出与上述相同的SQL。 MySQL现在立即将其返回到所有三个线程,而以前只有一个线程可以继续执行:

+-----------+
| count(id) |
+-----------+
|         0 |
+-----------+
1 row in set (0.00 sec)

所有三个线程现在尝试插入。其中两个会出现错误:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

有没有一种方法可以使MySQL始终保持第一个场景的行为?我们希望Thread2和Thread3理想地阻塞。

谢谢你, -乔纳森

1 个答案:

答案 0 :(得分:0)

因此,我们最终放弃了锁定。相反,我们在单独的事务中提交该行,然后选择减去last_insert_id()的电子邮件的所有行。如果发现行的主键较低,则假定另一个线程已经在处理请求。很好,因为它是无锁的,调试起来有点容易。