【问题标题】:Execute php script only once on live site在实时站点上只执行一次 php 脚本
【发布时间】:2010-10-21 17:12:09
【问题描述】:

我有一个实时执行更新功能的脚本。我会将其移至 cron 作业,但由于某些限制,我更愿意让它在页面加载时运行并调用。

问题是,当流量很大时,它就不太好用了,因为它使用了一些随机的加权数字,所以如果它被点击了很多次,结果就不是我们想要的。

所以,问题是。有没有办法知道特定脚本被访问了多少次?并且一次只能限制一次?

谢谢!

【问题讨论】:

    标签: php


    【解决方案1】:

    您正在寻找的技术称为锁定。

    最简单的方法是创建一个临时文件,并在操作完成后将其删除。其他进程将查找该临时文件,发现它已经存在并消失。

    但是,您还需要注意锁的所有者进程崩溃以及无法移除锁的可能性。这就是这个简单的任务似乎变得复杂的地方。

    基于文件的锁定解决方案

    PHP 有一个内置的flock() 函数,它承诺提供独立于操作系统的基于文件的锁定功能。 This question 有一些关于如何使用它的实用提示。但是,手册页警告说,在某些情况下,flock() 在尝试同时获得锁定的多个 PHP 脚本实例方面存在问题。 This question 在这个问题上似乎有更高级的答案,但它们都不是微不足道的。

    基于数据库的锁定

    this question 的作者 - 可能被 flock() 周围的复杂性吓跑了 - 寻求其他非基于文件的锁定技术,并提出了 MySQL 的 GET_LOCK()。我从未使用过它,但它看起来很简单——如果你仍然使用 mySQL,它可能值得一试。

    该死,如果你想把它做好的话,这个问题很复杂!有兴趣看看是否有更优雅的东西出现。

    【讨论】:

    • 你可以用 register_shutdown_function() 注册一个函数来清理你的锁。即使脚本因致命错误而崩溃,它也会被调用。
    【解决方案2】:

    你可以这样做(需要 PHP 5):

    if(file_get_contents("lock.txt") == "unlocked"){
      // no lock present, so place one
      file_put_contents("lock.txt", "locked");
    
      // do your processing
      ...
    
      // remove the lock
      file_put_contents("lock.txt", "unlocked", LOCK_EX);
    }
    

    file_put_contents() 默认情况下会覆盖文件(而不是追加),因此文件的内容应该永远是“锁定”或什么都没有。您需要指定 LOCK_EX 标志,以确保在您尝试写入文件时,该文件当前未被脚本的另一个实例打开。

    显然,正如@Pekka 在他的回答中提到的那样,如果在放置锁定和删除锁定之间发生致命错误(或 PHP 崩溃,或服务器崩溃等),这可能会导致问题,因为文件将简单保持锁定状态。

    【讨论】:

    • 布拉德,谢谢。如果我们想要多个产品的锁怎么办?你会为每个产品创建一个文件吗?像 product_id-lock.txt 一样,还是将其移动到包含每个产品记录的数据库表“product_locks”中?
    • 我什至会走得更远,只创建/删除一个空的锁定文件。这消除了竞争条件的可能性
    【解决方案3】:

    使用 sql 查询启动脚本,该查询测试数据库中的时间戳字段是否超过 1 天前。 如果是 - 写入当前时间戳并执行脚本。

    伪sql来展示思路:

    UPDATE runs SET lastrun=NOW() WHERE lastrun<NOW()-1DAY
    

    (不同的sql server会在上面做不同的改动)

    检查更新了多少行以查看此脚本运行是否获得了锁定。 不要使用两个查询 - SELECT 和 UPDATE,因为它不再是原子的。

    【讨论】:

    • 使用更新似乎很聪明。谢谢你。在 SQL SERVER 中,查询可能如下所示(添加 IDrun 以选择更多脚本):UPDATE [runs] SET [lastrun] = GETDATE() WHERE [IDrun]= 1 AND [lastrun] &lt; DATEADD(dd, -1, GETDATE())
    • 在脚本结束时,您可以运行UPDATE [runs] SET [lastrun] = DATEADD(dd, -1, GETDATE()) WHERE [IDrun]= 1 来释放“锁定”,以便脚本可以再次运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 2019-06-20
    相关资源
    最近更新 更多