PHP cURL定时执行冲突问题:如何避免多实例重叠运行
解决PHP脚本单实例高频执行问题(避免重叠)
你需要的是让scriptA.php在每分钟内尽可能多次运行,但绝对不能并发执行——核心是进程互斥锁,再配合Cron触发的scriptB.php来实现分钟内的循环执行。下面是几种可靠的实现方案:
方案一:PID文件锁(最常用、轻量)
让scriptA.php自己管理运行状态,不管通过什么方式触发,都能保证同一时间只有一个实例在跑。
修改scriptA.php,添加锁逻辑
在脚本最开头加入以下代码:
<?php // 定义PID文件路径,选一个脚本有读写权限的目录(比如/tmp) $pidFile = '/tmp/scriptA.pid'; // 检查是否已有运行的实例 if (file_exists($pidFile)) { $runningPid = trim(file_get_contents($pidFile)); // 验证PID对应的进程是否真的存在(仅Linux/macOS可用,Windows需调整) if (function_exists('posix_kill') && posix_kill($runningPid, 0)) { // 进程仍在运行,直接退出 error_log("scriptA is already running with PID: $runningPid"); exit(1); } else { // PID文件残留但进程已死,清理旧文件 unlink($pidFile); } } // 写入当前进程PID到文件 file_put_contents($pidFile, getmypid()); // 注册脚本退出时的清理函数(不管正常结束还是异常崩溃) register_shutdown_function(function() use ($pidFile) { if (file_exists($pidFile) && trim(file_get_contents($pidFile)) == getmypid()) { unlink($pidFile); } }); // 下面是你原来的查询API、写入数据库的逻辑 // ...
修改scriptB.php,实现分钟内循环执行
让scriptB.php在每分钟内重复调用scriptA.php,每次等待上一次执行完成后再启动下一次:
<?php // 记录当前分钟的起始时间 $minuteStart = time(); // 循环执行,直到当前时间超过起始时间60秒 while (time() < $minuteStart + 60) { // 执行scriptA,等待它完成后再继续 $output = shell_exec('php /绝对路径/scriptA.php'); // 可选:如果scriptA执行极快,添加短暂延迟避免资源耗尽(比如0.1秒) usleep(100000); // 可选:记录执行日志 error_log("scriptA executed at " . date('Y-m-d H:i:s') . " | Output: $output"); }
配置Cron
设置Cron每分钟运行一次scriptB.php:
* * * * * php /绝对路径/scriptB.php >> /var/log/scriptB.log 2>&1
方案二:MySQL数据库锁(适合已有数据库的场景)
如果你的脚本已经连接MySQL,可以用数据库的GET_LOCK机制实现互斥,不需要额外管理文件:
修改scriptA.php的锁逻辑
<?php // 先建立MySQL连接(替换成你的数据库信息) $db = new mysqli('localhost', 'username', 'password', 'database'); if ($db->connect_error) { die("数据库连接失败: " . $db->connect_error); } // 定义唯一的锁名称 $lockKey = 'scriptA_exclusive_lock'; // 尝试获取锁,超时时间设为0(立即返回,不等待) $result = $db->query("SELECT GET_LOCK('$lockKey', 0) AS lock_acquired"); $row = $result->fetch_assoc(); if (!$row['lock_acquired']) { // 无法获取锁,说明已有实例在运行 error_log("scriptA is already running - couldn't acquire lock"); exit(1); } // 注册退出函数,释放锁 register_shutdown_function(function() use ($db, $lockKey) { $db->query("SELECT RELEASE_LOCK('$lockKey')"); $db->close(); }); // 你的业务逻辑 // ...
关键注意事项
- 权限问题:如果用PID文件,确保脚本对文件所在目录有读写权限(
/tmp通常默认开放)。 - 异常处理:
register_shutdown_function能处理大多数退出场景,但如果服务器强制kill进程(比如kill -9),PID文件可能残留——不过我们已经在脚本开头做了进程存在性检查,会自动清理残留文件。 - 资源监控:如果
scriptA执行非常快,每分钟多次运行可能会占用较多系统资源,建议监控CPU/内存负载,必要时调整循环延迟。
内容的提问来源于stack exchange,提问作者John Chase
相关产品推荐
相关产品推荐

