gpt4 book ai didi

mysql - 解决 INSERT race condition mysql,避免锁

转载 作者:行者123 更新时间:2023-11-29 03:39:59 25 4
gpt4 key购买 nike

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

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

所以我想知道下面的选择是否正确:

  • 在地址列上添加唯一索引
  • 只填地址,不用选择检查
  • 捕获返回的mysql错误码:如果是1062,地址已经存在。

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

谢谢,静

最佳答案

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

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

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

例如:

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

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

编辑以回应以下评论:

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

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

索引性能也应该得到解决,并且应该评估 CHAR 与 VCHAR。 CHAR 字段上的索引查找通常比等效的 VCHAR 查找快 15% - 25% – 固定宽度的表大小也有帮助,具体取决于所使用的表引擎。

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

关于mysql - 解决 INSERT race condition mysql,避免锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15155420/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com