PHP项目Symfony Lock锁机制

wen PHP项目 1

Symfony Lock 锁机制详解

基本概念

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

PHP项目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是最推荐的分布式锁实现,而文件锁适合单机应用。

抱歉,评论功能暂时关闭!