
浏览器中同时运行多个php脚本时,因会话级资源竞争(如php内置会话锁)导致mysql查询被阻塞,而非数据库本身锁表;将长时脚本移至cli环境执行可彻底规避该问题。
你遇到的现象——“Script 1 运行时,Script 2 查询 TableA 完全无响应”——表面像数据库锁表,实则根源在 PHP 的会话机制(session locking),而非 MySQL 的行锁或表锁。
? 真正的问题:PHP 会话阻塞,不是 MySQL 锁
当 Script 1 和 Script 2 都通过 Web 服务器(如 Apache)以 HTTP 方式执行,并且都调用了 session_start()(显式或隐式,例如通过框架、CMS 或配置 session.auto_start=1),PHP 默认会对同一会话 ID 的请求进行串行化处理:第二个请求会一直阻塞,直到第一个请求的会话写入完成并释放会话文件锁(Linux 下通常为 /var/lib/php/sessions/ 中的文件锁)。此时即使 Script 1 只是 SELECT + 循环中的 I/O 或网络爬虫操作,其会话仍处于打开状态,导致 Script 2 在 session_start() 阶段就卡住——后续的 MySQL 查询根本没机会执行。
✅ 验证方法:在 Script 2 开头添加 error_log("Script 2 started"); 并检查日志时间戳。若日志输出明显滞后于请求发起时间,大概率是 session 阻塞;若日志立刻输出但 SQL 卡住,则才需排查 MySQL 锁。
? 正确解决方案(不止“改用 CLI”)
虽然将 Script 1 改为命令行执行(php /path/to/script1.php)能立即解决问题(CLI 模式默认不启用 session),但生产环境中更健壮的做法应结合场景选择:
✅ 方案一:Web 环境下解除会话阻塞(推荐用于需 Web 触发的场景)
// Script 1 开头:读取会话后立即关闭,避免阻塞其他请求
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// 立即释放会话锁(关键!)
session_write_close();
// 此后可安全执行耗时操作,不影响其他页面会话
$stmt = $conn->prepare("SELECT * FROM TableA");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// ... 爬虫逻辑
if ($condition) {
$conn->prepare("INSERT INTO TableB (...) VALUES (...)")->execute($params);
$conn->prepare("UPDATE TableA SET processed = 1 WHERE id = ?")->execute([$row['id']]);
}
}✅ 方案二:使用 CLI 模式(适合后台任务)
# 通过 Web 接口触发(轻量),实际工作交由 CLI 执行
// trigger_process.php
if ($_POST['action'] === 'start') {
exec('nohup php /var/www/scripts/process_tablea.php > /dev/null 2>&1 & echo $!');
echo "Task started in background";
}CLI 脚本天然无会话、无超时限制、无内存回收干扰,是处理批量任务的标准实践。
立即学习“PHP免费学习笔记(深入)”;
⚠️ 补充建议:优化数据库交互(防误判)
- 当前代码中 SELECT * FROM TableA 全表遍历 + 每次 UPDATE 单行,在高并发下可能引发 InnoDB 行锁累积(尤其 UPDATE 无索引条件时)。务必确保 UPDATE 语句中的 WHERE 条件字段已建立索引。
- 避免在循环内重复 prepare(),应提前准备并复用:
$insertStmt = $conn->prepare("INSERT INTO TableB (col1, col2) VALUES (?, ?)"); $updateStmt = $conn->prepare("UPDATE TableA SET status = 'done' WHERE id = ?"); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if ($condition) { $insertStmt->execute([$val1, $val2]); $updateStmt->execute([$row['id']]); } }
✅ 总结
| 现象 | 真实原因 | 解决关键 |
|---|---|---|
| Script 2 查询卡死 | PHP 会话文件锁阻塞,非 MySQL 锁 | session_write_close() 或改用 CLI |
| “MySQL 不响应”错觉 | Script 2 根本未到达 MySQL 层 | 日志验证执行起点 |
| 浏览器执行不稳定 | 受限于 PHP-FPM/Apache 超时、内存限制 | 后台任务必须剥离出 Web 生命周期 |
记住:“MySQL 无响应”往往是应用层瓶颈的假象。先排除会话、连接池、超时配置,再深入数据库锁分析。











