【问题标题】:Work around INSERT race condition mysql, avoiding locks解决 INSERT 竞争条件 mysql,避免锁定
【发布时间】:2013-03-01 10:25:39
【问题描述】:

假设我有几个脚本发送(合法!)电子邮件。每个脚本处理更大列表的一部分,并且它们同时运行。在发送之前,必须检查每个地址以避免两次发送到同一地址。

为此,我创建了一个仅包含电子邮件地址的简单表(mysql 5.1,innodb)。如果它不在表中,则添加它,然后发送邮件。现在我需要避免多个脚本同时测试同一个地址并错误地断定它没有被发送到的竞争条件。我想我可以为此使用锁,但出于性能原因,我宁愿不这样做。

所以我想知道以下替代方案是否正确:

  • 在地址列上添加唯一索引
  • 只需插入地址,无需选择检查
  • trap返回的mysql错误码:如果是1062,则地址已经存在。

在此设置中,是否仍有可能出现竞争条件?我的意思是:是否仍然有可能两个几乎同时插入地址的脚本都得出邮件尚未发送的结论?或者我应该为此使用锁吗?

谢谢, 斯蒂金

【问题讨论】:

  • 出于兴趣,为什么不只使用 1 个脚本?
  • 这些电子邮件是从哪里来的?如果脚本在实际发送电子邮件之前崩溃,是否会出现问题?是什么让您认为锁无法正常工作? (除此之外,唯一索引是避免欺骗的有效方法。)
  • @F4r-20:启用到 postfix 服务器的多个并发连接。
  • @ÁlvaroG.Vicario:这是一个很大的列表,为了加快发送速度,它被分成了多个较小的列表。脚本不应崩溃。锁可以工作,但似乎有点矫枉过正。

标签: mysql innodb


【解决方案1】:

首先,我觉得数据库不是最好的地方。虽然您的更大列表正在发送电子邮件(由于您尝试瘫痪,我猜测规模非常大)您必须使用临时表,因为您不想限制向收件人发送不同的电子邮件以前的邮件。

这里显然选择缓存是维护地址列表或充当共享内存资源的服务器。

但是您可以在数据库中执行此操作,据我了解,如果一个电子邮件地址多次存在并不重要,因为您所做的只是检查一个过去没有发送到该地址。如果没有锁定策略,您将无法真正控制多个脚本同时发送到同一地址的竞争条件。但是,您可以通过使用索引来提高效率。我不会索引实际地址,而是使用地址的 CRC32 哈希创建一个新列(可以是一个 32 位无符号整数,只占用 4 个字节的内存)。由于生日悖论,使用 CRC32 方法您还必须检查查询中的电子邮件地址。

例如:

SELECT COUNT(*) FROM email_addresses
WHERE email_address_crc = CRC32(?address)
AND email_address = ?address

拥有一些高效的东西应该有助于对抗竞争条件,但是正如我之前所说,保证它的唯一方法是在发送每封电子邮件时锁定数据库,这样您就可以维护一个准确的列表 - 不幸的是,这并没有t 规模,这意味着让并行任务发送电子邮件可能无济于事。

针对以下 cmets 进行编辑:

正如 cmets 中所指出的,我实际上忘记解决 svdr 的锁定解决方案替代方案。确实,如果地址存在,包含电子邮件地址的唯一索引(或包含活动 ID 和地址的复合索引)确实会引发 MySQL 异常,从而导致并行脚本发送到同一地址的有效解决方案同时。但是,如果在脚本“尝试”发送电子邮件之前输入地址,则很难处理任何异常,例如由于 SMTP 错误/网络问题而无法发送电子邮件,这可能会导致收件人收不到电子邮件。还提供了一个非常简单的 INSERT 和 SELECT 它应该可以捕获 MySQL 异常,但是如果有任何更复杂的东西,例如在事务中包装命令或使用 SELECT FOR UPDATE 等,这可能会导致死锁情况。

另外几个考虑因素是,出于性能原因,电子邮件地址字段需要完全索引,如果使用 INNODB,则此限制为 767 字节 - 假设电子邮件地址的最大有效长度为 254(如果长度为 +1 字节)使用 VARCHAR)如果你没有一些巨大的主键,你应该没问题。

还应解决索引性能问题,并且应评估 CHAR 与 VCHAR。 CHAR 字段上的索引查找通常比等效的 VCHAR 查找快 15% - 25% - 固定宽度的表大小也可以根据使用的表引擎提供帮助。

总而言之,是的,您的非锁定解决方案可以工作,但应根据您的确切要求仔细测试和评估(我无法评论具体细节,因为我认为您的现实生活场景比您的 SO 问题更复杂)。正如答案的第一行所述,我仍然认为数据库不是解决此问题的最佳场所,并且缓存或共享内存空间会更有效且更易于实现。

【讨论】:

  • 谢谢!我们需要持久性,因为活动可能会重新发送给添加的收件人,我们需要再次检查。使用哈希作为索引的好主意。关于我的锁定替代方案:你能解释一下为什么这不起作用吗?
  • @svdr 我将编辑我的答案 - 我被另一种方法迷住了,我完全忘记了解决这一点!
  • 感谢您的广泛回答。我会做一些测试以找到最佳解决方案。一旦我达到 15 名声望,我就会支持你的回答 ;)
猜你喜欢
  • 1970-01-01
  • 2012-01-10
  • 2015-01-30
  • 2010-09-25
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-24
相关资源
最近更新 更多