php“锁表”多为会话阻塞、索引缺失或长事务所致;session_write_close()需在事务前统一调用以解并发阻塞;update无索引将全表加锁;应优化索引、分片批量更新、降级隔离级别,并注意锁超时后需重连事务。

PHP 中所谓“锁表”,绝大多数时候根本没锁表——是会话阻塞、索引缺失或事务拖太久,让查询看起来像卡死在表锁上。
session_write_close() 必须加在事务前
浏览器并发请求同一会话 ID 时,PHP 默认串行化处理:第二个请求会卡在 session_start(),连 MySQL 连接都没发出去,更别说锁表了。这不是数据库问题,是 PHP 运行时行为。
- 只要脚本里有
session_start()(包括框架自动调用),且你不需要在后续逻辑中写入 session 数据,就在事务开始前加session_write_close() - 别只在“可能并发”的脚本里加——统一加,省得漏掉;加完之后 session 变量仍可读,只是不能改
- CLI 模式天然无 session 阻塞,耗时任务(如导出、同步)优先走命令行,别从浏览器触发
UPDATE 不走索引 = 实际锁全表
InnoDB 行锁依赖索引。WHERE 条件没命中索引时,引擎会退化为全表扫描,并对所有扫描过的行加记录锁——效果等同于锁表,且极易引发锁等待超时。
- 用
EXPLAIN UPDATE ... WHERE xxx看type字段:要是ALL或index,立刻补索引 - 联合索引注意顺序:
WHERE status = ? AND created_at > ?应建(status, created_at),反过来就失效 - 警惕隐式类型转换:
user_id是BIGINT,但传入字符串'123',索引直接失效 - 别信“小表不用索引”——哪怕只有 1000 行,没索引的 UPDATE 在并发下照样排队等锁
innodb_lock_wait_timeout 不是调大就安全
默认 50 秒,看着宽裕,实际会让用户白等半分钟才失败。更糟的是,它掩盖了真正的问题:事务太长、逻辑太重、锁持有太久。
- 把
innodb_lock_wait_timeout调到 300 秒,不如把事务里 HTTP 请求、日志写入、循环计算全移出去 - 批量更新必须分片:每 200 行一个事务,
COMMIT后立刻释放锁,别攒 10 万行一起交 -
SELECT ... FOR UPDATE后别做复杂计算——先查 ID 列表,再算逻辑,最后UPDATE ... WHERE id IN (...) - 高并发写场景,考虑降级隔离级别:把
REPEATABLE READ改成READ COMMITTED,能砍掉大部分间隙锁冲突
最常被忽略的一点:锁等待超时错误(1206)和死锁(1213)报错后,连接状态已损坏,必须重新 beginTransaction(),不能接着用旧事务对象重试。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











