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

MySQL: How to hold lock and make other threads wait for an insert that hasn't happened yet

我遇到了一个看似简单的问题,但结果却很难弄清楚。我们希望记录每次收到营销信息(线索)的时间,这样我们在 90 天内不会购买超过一次。许多潜在客户提供者可以多次向我们展示相同的潜在客户,通常是同时进行。我们想要 return 一个 "accept" 给一个主要提供者。

那么让我们谈谈有效的场景:我们 在过去 90 天内看到了 material 并且在 table 上有记录并且有 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 returns 到线程 1:

+-----------+
| 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 现在 return 将此立即发送到 所有三个 线程,而之前,只有一个线程会继续:

+-----------+
| 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 理想地阻塞。

谢谢, -乔纳森

所以我们最终放弃了锁定。相反,我们在单独的事务中提交该行,然后 select 返回电子邮件的所有行减去 last_insert_id()。如果我们找到具有较低主键的行,我们假设另一个线程已经在处理该请求。这很好,因为它是无锁的,更容易调试。