选错Store驱动锁即失效:RedisStore为生产首选,FlockStore仅限单机同文件系统,PdoStore需REPEATABLE READ隔离级,NullStore仅用于调试;必须检查acquire()返回值并显式release(),锁范围应最小化。

选错 Store 驱动,锁就等于没加
锁组件本身不存状态,LockFactory 必须配一个 StoreInterface 实现——这一步错了,整个锁机制就失效。
-
RedisStore是生产首选:支持原子操作、自动过期、跨进程/跨机器共享;但得确认 Redis 配了timeout和retry_interval,否则acquire(true)可能卡死 -
FlockStore只适用于单机、同一文件系统;Docker 里没挂共享卷、或用了tmpfs,每次重启锁就丢;CLI 进程若以不同用户(如www-datavsroot)运行,锁也互不可见 -
PdoStore要求 MySQL 事务隔离级别 ≥REPEATABLE READ,否则SELECT FOR UPDATE可能漏锁 -
NullStore是个“假锁”,只用于本地调试,上线前必须删掉
acquire() 返回值不检查,锁就形同虚设
调一次 $lock->acquire() 不代表你拿到了锁。忽略返回值,等于裸奔。
- 非阻塞模式(默认):
if (!$lock->acquire()) { throw new RuntimeException('Lock not acquired'); } - 阻塞模式:
$lock->acquire(true),但必须搭配超时控制,比如用LockFactory构造时传['ttl' => 30],避免无限等待 - 别依赖
__destruct()自动释放——PHP 垃圾回收时机不可控;应显式调$lock->release(),或包在try/finally里
锁的范围太大,反而拖垮并发性能
锁不是越早加越好,而是只包真正需互斥的那几行代码。锁内做耗时 I/O,等于人为制造瓶颈。
- 禁止在锁内调远程 API、读大文件、执行
sleep()或复杂计算 - 推荐结构:
acquire()→ 读 DB → 内存处理 → 写 DB →release();把 DB 之外的逻辑全移出去 - 读多写少场景可用
$lock->acquireRead()获取共享锁,提升并发度
CLI 命令加锁后仍并发?大概率是 Store 选错了
Web 请求和 CLI 命令本质都是独立 PHP 进程。FlockStore 在容器、多用户、NFS 等环境下极易失效。
- 验证方式:开两个终端,同时运行同一命令,观察是否并行执行
- 根治方法:改用
RedisStore,确保所有 CLI 进程连的是同一个 Redis 实例和 DB 库 - 进阶技巧:给 CLI 锁加唯一上下文标识,比如
"import:{$batchId}",避免不同批次互相阻塞
FlockStore 的失效表现非常隐蔽——进程看似被锁住了,实则各自跑各自的。这点最容易被忽略。


















