Symfony锁组件需明确选存储、判返回、控范围、管生命周期:首选RedisStore确保跨进程有效,必须检查acquire()返回值,显式释放锁,且锁范围应最小化,仅包裹真正需互斥的代码。
Symfony 锁组件不是“加了就自动生效”的黑盒,它需要明确选存储、判返回、控范围、管生命周期。用错方式,锁等于没加。
选对存储驱动,锁才跨进程有效
锁组件本身不存数据,必须指定一个
StoreInterface
实现——这一步错了,整个锁机制就失效。
RedisStore
:生产环境首选。支持原子操作、自动过期、多进程/多机器共享。需确认 Redis 配置了
和
,否则等待可能卡死
FlockStore
:仅限单机、同文件系统。容器中若未挂载持久化卷,或用了
,每次重启锁就消失;不同用户(如
和
)运行的 CLI 进程也无法共享同一把锁
PdoStore
:依赖数据库事务隔离级别。MySQL 必须设为
或更高,否则
可能漏锁
NullStore
:仅用于开发调试,不真正加锁,上线前必须删掉
acquire() 返回值必须检查
调一次
不代表任务已受保护。忽略返回值,等于裸奔。
非阻塞模式(默认):
阻塞模式:
,但必须搭配超时控制,避免无限等待
不要依赖
自动释放——PHP 垃圾回收时机不可控;应显式调
,或包在
中
锁的范围要最小化
锁不是越早加越好,而是只包真正需要互斥的那几行代码。
禁止在锁内做远程 API 调用、大文件读写、
等耗时 I/O 操作
推荐结构:
,把 DB 查询和写入之外的逻辑全移出去
对读多写少场景,可考虑
获取共享锁,提升并发度
CLI 命令加锁仍并发?大概率是存储选错了
Web 请求和 CLI 命令本质都是独立 PHP 进程。FlockStore 在容器、NFS、多用户等环境下极易失效。
验证方式:起两个终端,同时运行同一命令,观察是否并行执行
根治方法:改用
,确保所有 CLI 进程连接的是同一个 Redis 实例和 DB 库
进阶技巧:给 CLI 锁加唯一上下文标识(如
),避免不同批次互相阻塞
timeoutretry_intervaltmpfswww-datarootREPEATABLE READSELECT ... FOR UPDATE$lock->acquire()if (!$lock->acquire()) { throw new RuntimeException('Lock not acquired'); }$lock->acquire(true)__destruct()$lock->release()try/finallysleep()acquire → 读DB → 修改内存数据 → 写DB → releaseacquireRead()RedisStore"import:{$batchId}"