应选RedisStore用于生产,FlockStore仅限单机同文件系统,PdoStore需MySQL事务隔离级别≥REPEATABLE READ;必须检查acquire()返回值、显式release()、避免锁内耗时操作;CLI并发问题多因FlockStore跨进程失效,推荐改用RedisStore。

symfony/lock 组件怎么选存储驱动
锁组件本身不自带持久化能力,必须显式指定一个 StoreInterface 实现——选错驱动会导致锁完全失效或跨进程不生效。
-
FlockStore最简单,但只适用于单机、且 PHP 进程共享同一文件系统;NFS 或容器挂载卷下可能失效 -
RedisStore推荐用于生产,支持自动过期和原子性,但需确保 Redis 配置了timeout和retry_interval,否则锁等待会卡死 -
PdoStore依赖数据库事务隔离级别,MySQL 必须用REPEATABLE READ或更高,否则SELECT ... FOR UPDATE可能漏锁 - 别用
NullStore测试完就删掉——它不真正加锁,只是“假装锁住了”
如何正确使用 Lock::acquire() 防止并发执行
直接调 acquire() 并不等于任务被保护了,关键在返回值判断和锁生命周期管理。
- 必须检查返回值:
if (!$lock->acquire()) { throw new RuntimeException('Failed to acquire lock'); },忽略它等于没加锁 - 锁对象不能提前
unset或让其超出作用域——PHP 垃圾回收会触发release(),但时机不可控;应显式调$lock->release()或用try/finally - 不要在锁内做耗时 I/O(如远程 API 调用),否则锁持有时间过长,阻塞其他任务;把锁范围缩到最小,只包核心写操作
-
acquire()默认不阻塞,想等锁释放得传true:$lock->acquire(true),但要配好超时,避免无限等待
为什么 Symfony CLI 命令里加锁后还是并发了
CLI 命令默认以新进程启动,FlockStore 在容器或某些部署环境下无法跨进程识别同一文件锁,导致“以为加了锁,其实没生效”。
- 确认锁 key 是否唯一且稳定:用
md5(__FILE__ . 'my-command')这类硬编码 key 很危险,不同部署路径或符号链接会导致 key 不同 - 检查进程用户权限:多个 CLI 进程若以不同系统用户运行(比如 www-data vs root),
FlockStore文件锁会被视为不同锁 - Docker 环境下,若未挂载共享卷或用了
tmpfs,FlockStore的锁文件每次重启都消失,锁状态不延续 - 推荐改用
RedisStore,并确保所有 CLI 进程连的是同一个 Redis 实例和 DB 库
LockFactory 创建锁实例的常见误用
LockFactory 是工厂,不是单例管理器;反复 new 它没问题,但重复用同一 key 创建多个锁实例,可能引发资源竞争或连接泄漏。
- 别在循环里每次都
new LockFactory($store)->createLock($key)——LockFactory本身轻量,但频繁创建锁实例可能绕过内部连接复用逻辑(尤其 Redis) - 同一个
$key应该对应一个锁实例,而不是每次执行都新建;若需动态 key,确保生成逻辑幂等(比如基于业务 ID 哈希) - 如果用了
Symfony\Component\Lock\Store\RedisStore,注意它内部持有一个Predis\Client或Redis实例,别让它被 GC 提前释放 - 测试时容易漏掉:
LockFactory构造时传的$store若是 mock,要确保 mock 的save()和delete()方法行为符合原子性预期
锁的可靠性高度依赖底层存储的一致性模型,而不是组件 API 多“优雅”。Redis 的 EVAL 脚本实现、MySQL 的行锁语义、甚至文件系统的 flock() 行为,在不同环境里差异很大——别只测本地开发机。










