2

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

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

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

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

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

谢谢, 斯蒂恩

4

1 回答 1

0

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

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

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

例如:

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

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

编辑以回应以下评论:

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

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

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

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

于 2013-03-03T15:23:23.853 回答