Symfony Lock 锁机制详解
基本概念
Symfony Lock组件提供了一种协调多个进程或服务器之间资源访问的机制,用于避免并发操作导致的数据冲突。

安装
composer require symfony/lock
配置和存储类型
1 支持的存储后端
# config/packages/framework.yaml
framework:
lock:
# 使用文件系统存储
file_system_lock:
resource: '%kernel.cache_dir%/locks'
type: 'flock'
# 使用Redis
redis_lock:
resource: 'redis://localhost'
type: 'redis'
# 使用Memcached
memcached_lock:
resource: 'memcached://localhost'
type: 'memcached'
# 使用数据库
pdo_lock:
resource: 'mysql:host=localhost;dbname=mydb'
type: 'pdo'
username: 'root'
password: ''
基本用法
1 简单锁示例
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\FlockStore;
// 创建锁存储
$store = new FlockStore('/path/to/locks');
$factory = new LockFactory($store);
// 创建锁
$lock = $factory->createLock('resource-name');
// 尝试获取锁
if ($lock->acquire()) {
// 执行需要同步的代码
try {
// 处理敏感操作
processPayment();
} finally {
// 释放锁
$lock->release();
}
}
2 使用阻塞方式
use Symfony\Component\Lock\Store\RedisStore;
use Symfony\Component\Lock\LockFactory;
$redis = new Redis();
$redis->connect('127.0.0.1');
$store = new RedisStore($redis);
$factory = new LockFactory($store);
// 创建锁,设置超时时间
$lock = $factory->createLock('payment-lock', 30); // 30秒超时
// 阻塞等待锁(默认无限等待)
$lock->acquire(true); // 阻塞等待
// 或者指定最大等待时间
$lock->acquire(true, 10); // 最多等待10秒
// 也可以检查是否获取到锁
if ($lock->acquire(true, 5)) {
try {
// 处理任务
updatePoints();
} finally {
$lock->release();
}
}
高级特性
1 自动过期
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;
$store = new RedisStore($redis);
$factory = new LockFactory($store);
// 锁会自动过期,防止死锁
$lock = $factory->createLock('cronjob-lock', 60); // 60秒后自动释放
if ($lock->acquire()) {
try {
// 长时间运行的任务
processHeavyTask();
// 刷新锁的过期时间(延长锁定时间)
$lock->refresh();
continueProcessing();
} finally {
$lock->release();
}
}
2 信号量锁
use Symfony\Component\Lock\Store\SemaphoreStore;
// 使用信号量(仅支持Linux)
$store = new SemaphoreStore();
$factory = new LockFactory($store);
$lock = $factory->createLock('semaphore-lock');
实际应用场景
1 防止重复提交
class OrderController
{
private LockFactory $lockFactory;
public function createOrder(Request $request)
{
$userId = $this->getUser()->getId();
$lock = $this->lockFactory->createLock("order-creation-{$userId}", 10);
if (!$lock->acquire()) {
throw new \Exception('请勿重复提交');
}
try {
// 处理订单创建
$order = $this->createOrder();
// 更新库存
$this->updateStock($order);
return $this->json($order);
} finally {
$lock->release();
}
}
}
2 定时任务控制
class CronJobService
{
public function runCashJob()
{
$lock = $this->lockFactory->createLock('cron-cash-job', 300);
if (!$lock->acquire()) {
$this->logger->info('将金任务正在运行中,跳过');
return;
}
try {
// 执行将金计算
$this->processCashUpdate();
} finally {
$lock->release();
}
}
}
3 分布式锁示例
use Symfony\Component\Lock\Store\RedisStore;
use Symfony\Component\Lock\LockFactory;
// Redis存储是最常用的分布式锁实现
class DistributedLockService
{
private LockFactory $lockFactory;
public function __construct()
{
$redis = new \Redis();
$redis->connect('redis-master', 6379);
$store = new RedisStore($redis);
$this->lockFactory = new LockFactory($store);
}
public function processWithLock(string $resourceId): bool
{
$lock = $this->lockFactory->createLock(
"resource:{$resourceId}",
30, // TTL
true // 自动刷新
);
if (!$lock->acquire()) {
return false;
}
try {
// 执行分布式任务
$this->executeCriticalTask($resourceId);
return true;
} finally {
$lock->release();
}
}
}
最佳实践
1 始终使用 try-finally
$lock = $factory->createLock('resource');
$lock->acquire();
try {
// 业务逻辑
} finally {
$lock->release();
}
2 合理的超时设置
// 根据业务时间设置合适的TTL
$lock = $factory->createLock('resource', 10); // 10秒
// 避免设置过长或过短
// 太短:任务可能没完成就释放
// 太长:影响其他进程
3 监控锁状态
if ($lock->acquire()) {
// 检查锁是否仍被当前进程持有
if ($lock->isAcquired()) {
// 安全执行代码
}
}
4 连接池配置(Redis)
# config/packages/framework.yaml
framework:
lock:
redis:
resource: 'redis://localhost'
options:
# 连接池配置
persistent: true
timeout: 5
retry_interval: 100
常见问题及解决方案
1 死锁预防
// 设置合理的TTL防止死锁
$lock = $factory->createLock('resource', 30);
// 使用refresh()方法维持锁
$lock->acquire();
$lock->refresh(); // 刷新过期时间
2 锁竞争优化
// 使用更细粒度的锁
// 不好的做法
$lock = $factory->createLock('all-users', 30);
// 好的做法
$lock = $factory->createLock("user-{$userId}", 30);
// 使用读写锁来处理不同场景
3 锁释放失败处理
try {
$lock->acquire();
// 业务代码
} catch (\Exception $e) {
// 确保释放锁
if ($lock->isAcquired()) {
$lock->release();
}
throw $e;
}
Symfony Lock组件提供了一套完整的锁机制实现,支持多种存储后端,适用于单机和分布式环境,合理使用锁机制可以有效避免并发问题,但需要注意死锁预防、性能影响和适当的事务设计,在选择锁存储时,Redis是最推荐的分布式锁实现,而文件锁适合单机应用。