我正在我正在开发的网站中实现 PayPal 付款标准。这个问题与PayPal无关,我只是想通过我的真实问题来提出这个问题。
PayPal 可以通过两种方式通知您的服务器有关付款的信息:
- PayPal IPN - 每次付款后,PayPal 都会向网址(由您选择)发送包含交易详细信息的(服务器到服务器)通知。
- PayPal PDT - 付款后(如果您在 PP 帐户中设置了此项)PayPal 会将用户重定向回您的网站,并在网址中传递交易 ID,以便您可以查询 PayPal 有关该交易的信息,以获取详细信息.
问题是,您无法确定哪一个先发生:
- 您的服务器是否会收到 IPN 通知
- 将用户重定向回您的网站
无论哪一个先发生,我都想确保我不会处理两次交易。 因此,在这两种情况下,我都会根据来自 paypal 的交易 ID(以及实际上的付款状态......但现在不重要)查询我的数据库,以查看我是否已经保存并处理了该交易。如果没有,我会对其进行处理,并将交易 ID 和其他交易详细信息保存到我的数据库中。
问题
如果我开始处理第一个请求(让它成为 PDT..因此用户被重定向回我的网站,但 IPN 尚未通知我的服务器),但在我实际将事务保存到之前,会发生什么数据库中,第二个(IPN)请求到达,它也会尝试处理事务,因为它在数据库中找不到它。
我很想确保当我将事务写入数据库时,没有其他查询可以读取该表,查找给定的事务 ID。
我正在使用 InnoDB,并且不想在写入时锁定整个表。 这可以简单地通过事务来解决吗?我必须“手动”锁定该行吗?我真的很困惑,我希望一些更有经验的mysql开发人员可以帮助我弄清楚这一点并解决问题。
最佳答案
native 数据库锁在 Web 上下文中几乎毫无用处,尤其是在这种情况下。 MySQL 连接通常不会以持久方式完成 - 当脚本关闭时,MySQL 连接也会关闭,所有锁都会被释放,并且任何正在进行的事务都会回滚。
例如
情况 1:您引导用户访问 PayPal 网站以完成购买
当他们离开 paypal 时,通过 http 重定向发送的脚本将终止并关闭。锁/事务被释放/回滚,并且就数据库而言,它们回到“原始”状态。他们的记录不再被锁定。
情况 2:Paypal 进行服务器到服务器响应。这将通过完全独立的 HTTP 连接来完成,与用户与服务器建立的连接完全不同。这意味着您在 yourserver<->user 连接中建立的任何锁定都将与 paypal<->yourserver session 不同,并且 paypal 响应将遇到锁定的表。当然,无法预测 PayPal 响应何时到来。如果网络之神向您微笑并且 PayPal 没有被淹没,您会很快收到响应,并且可能在用户<->您的连接仍然处于打开状态时得到响应。如果速度缓慢且响应延迟,则该响应可能会遇到未锁定的表/行,因为用户<->服务器 session 已完成。
您可以使用持久的 MySQL 连接,但它们打开了另一个痛苦的世界。例如考虑这样的情况:您的脚本有一个错误,该错误在处理过程中被触发。你连接,做一些事务工作,设置一些锁......然后脚本就死了。因为 MySQL 连接是持久的,MySQL 将不会看到客户端脚本已终止,并且它将保持事务/锁定进行中。但连接仍然位于共享池中,等待另一个 session 来接收它。当它总是如此时,新脚本并不知道它已经获得了这种旧的“陈旧”连接。它将陷入一堆它不知道存在的锁和事务中。您很容易陷入这样的死锁情况,因为您的有缺陷的脚本已将垃圾转储到整个系统,而其他脚本无法处理这些垃圾。
基本上,除非您在系统顶部实现自己的锁定机制,例如UPDATE users SET Locked=1 WHERE id=XXX
,您无法在 Web 上下文中使用 native 数据库锁定机制,除非在 1-shot-per-script 上下文中。绝不应该在多个独立请求上尝试锁定。
关于MySQL 事务 : reads while writing,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16342292/