【问题标题】:Make database operation and sending email a transaction使数据库操作和发送电子邮件成为一个事务
【发布时间】:2012-07-23 10:30:55
【问题描述】:

我有一个预订酒店的网站。预订成功后,将向客户发送一封电子邮件。我是这样做的:

- Update table "booking"
- Check the result
   + If true, send email
   + Else return error

但是如果电子邮件没有发送怎么办?已插入数据库但未发送电子邮件。这使得预订操作不正确。 如何进行数据库操作和发送邮件交易?有什么简单的方法吗? A 和 B 必须成功,否则都不成功。

【问题讨论】:

  • 为什么不发邮件?
  • 您可以使用mysql_insert_id 并删除您刚刚添加的记录,如果它失败了?还是在电子邮件发送后插入?

标签: php mysql email


【解决方案1】:

严格意义上的事务不能扩展数据库内部以及数据库外部的东西。所以我们必须将“事务”重新定义为,要么所有操作都失败,要么全部成功。

这是一个想法:

  • 启动数据库事务
    • 更新表“预订”
    • 将撤消记录写入表中,状态为“已更新,未发送”。这需要有一些 ID,可用于撤消预订
  • 提交数据库事务
  • 如果失败,退出(无需回滚)

  • 发送电子邮件

  • 如果一切正常
    • 删除撤消记录
    • 返回“成功”
  • 其他
    • 使用撤消记录撤消预订
    • 删除撤消记录
    • 返回“错误”

有两点需要理解:

  • 在操作系统崩溃、断电等情况下,您必须手动检查撤消表并将其与邮件服务器日志进行比较:崩溃可能发生在邮件刚刚失败,或者邮件刚刚发送,但不是记录
  • 最重要:我还没有看到一种快速机制,可以可靠地判断电子邮件是否已发送。 我的意思是真的发送,而不是在某个地方排队

【讨论】:

  • 优秀的答案!不应在成功发送电子邮件时尝试同步数据库事务,这将在您有一点负载时立即导致锁定和事务超时。像这里提出的“两阶段”方法是要走的路。
  • @Eugen Rieck 某些系统可以配置为在操作系统崩溃、断电后自动重启应用程序。您是否建议禁用自动重启?或者,我们可以挂钩一个将在系统重新启动之前运行的脚本吗?不确定脚本是否可以可靠地解析邮件服务器日志...
【解决方案2】:

如果你使用 InnoDB,你可以使用数据库事务:

伪代码:

Start DB transaction
Update table "booking"
Check the result
If true, send email
If email sends, commit your transaction
Else rollback your transaction, and return error

这里有更多关于交易的信息:http://dev.mysql.com/doc/refman/5.0/en/commit.html

【讨论】:

  • 您的意思是使用存储过程并在其中执行所有操作吗?问题是我没有数据库的权限。我只使用 PHP 和 CodeIgniter。我在 PHP 文件中逐行编写代码。
  • @Emerald214,不,我不是说存储过程,我是说数据库事务,查看我给你的链接。
  • 完美,另外您必须使用相同的 MySQL 连接对象来回滚事​​务,并确保会话 wait_timout 设置为大于发送电子邮件所需时间的值。
  • 我不知道可以在TRANSACTION中发送电子邮件。我会尝试并稍后回复。 :)
  • @Emerald214 你用一个查询开始你的事务,然后用另一个查询更新你的表,在 PHP 中发送你的电子邮件,然后如果一切顺利,用另一个查询提交你的事务。
【解决方案3】:

但是如果电子邮件没有发送怎么办?已插入数据库但未发送电子邮件。这使得预订操作不正确。如何进行数据库操作和发送邮件交易?有什么简单的方法吗? A 和 B 必须成功或都不成功。

好吧,我想说:开始一个事务,将数据输入数据库并测试是否成功。如果成功,请发送电子邮件。如果数据输入和电子邮件发送都成功,则提交事务。如果其中一个不成功,则回滚事务。

这就是说;没有可靠的方法来确定电子邮件是否已成功发送到电子邮件地址。您可以检查接受服务器的 SMTP 状态代码,但这并不能保证您的电子邮件以后不会被退回。

【讨论】:

  • 您可以设置 PHP 来接收退回邮件,但同样这也不是一步交易,也不是很简单。
  • @Petah 没错,你可以这样做,但它比这复杂得多:你的电子邮件可能会被垃圾邮件过滤器删除,这意味着你不会被退回,而用户反正没看邮件。
【解决方案4】:

您可以通过在主表上添加额外的列 status_flag DEFAULT 0 来实现这一点

步骤:

1. Update table "booking" SET col1 = val1, status_flag = 1 WHERE tr_id = "abc"

2. Send Email

3. Update table "booking" SET status_flag = 2 WHERE tr_id = "abc"

因此,如果您在预订表中找到 status_flag 值为 1 的任何记录,那么这些记录可能是发送电子邮件失败的记录。

因此,您可以从同一脚本重新发送电子邮件,也可以添加另一个 cron-job 脚本来识别失败的条目并采取相应的措施。

【讨论】:

  • 但是如果Update table_lock 查询失败怎么办?
  • 你说得对,但在您可以重新发送电子邮件的情况下,这可以作为替代的附加故障转移方法。
  • 在这种情况下,最好在主表上多放一列。
猜你喜欢
  • 2016-11-12
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 2016-11-17
  • 1970-01-01
  • 2016-10-16
  • 2018-07-14
相关资源
最近更新 更多