最新公告
  • 欢迎您光临源码库,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入
  • PHP后端分布式锁实现方案

    PHP后端分布式锁实现方案插图

    PHP后端分布式锁实现方案:从Redis到ZooKeeper的实战指南

    在分布式系统开发中,我经常遇到需要协调多个进程同时访问共享资源的情况。如果没有合适的锁机制,就会导致数据不一致、重复处理等严重问题。今天我就结合自己的实战经验,分享几种在PHP中实现分布式锁的方案。

    为什么需要分布式锁

    记得有一次,我们的订单系统在促销活动时出现了重复扣款的问题。排查后发现,多个PHP进程同时处理同一个订单,导致业务逻辑被执行了多次。这就是典型的并发问题,分布式锁正是解决这类问题的利器。

    基于Redis的分布式锁实现

    Redis是目前最常用的分布式锁实现方案,我推荐使用RedLock算法。下面是我在实际项目中使用的代码:

    
    class RedisDistributedLock
    {
        private $redis;
        private $lockKey;
        private $lockValue;
        private $expireTime;
        
        public function __construct($redis, $lockKey, $expireTime = 30)
        {
            $this->redis = $redis;
            $this->lockKey = $lockKey;
            $this->expireTime = $expireTime;
            $this->lockValue = uniqid();
        }
        
        public function acquire()
        {
            $result = $this->redis->set(
                $this->lockKey,
                $this->lockValue,
                ['NX', 'EX' => $this->expireTime]
            );
            
            return $result !== false;
        }
        
        public function release()
        {
            $script = "
                if redis.call('get', KEYS[1]) == ARGV[1] then
                    return redis.call('del', KEYS[1])
                else
                    return 0
                end
            ";
            
            $this->redis->eval($script, [$this->lockKey, $this->lockValue], 1);
        }
    }
    

    这里有个踩坑经验:一定要使用Lua脚本来保证释放锁的原子性,否则可能会出现误删其他进程锁的情况。

    基于数据库的分布式锁

    在没有Redis的环境下,我们可以利用数据库来实现分布式锁。我通常使用MySQL的唯一索引特性:

    
    class DatabaseDistributedLock
    {
        private $pdo;
        private $tableName = 'distributed_locks';
        
        public function __construct($pdo)
        {
            $this->pdo = $pdo;
        }
        
        public function acquire($lockName, $timeout = 30)
        {
            try {
                $stmt = $this->pdo->prepare(
                    "INSERT INTO {$this->tableName} (lock_name, acquired_at, expires_at) VALUES (?, NOW(), DATE_ADD(NOW(), INTERVAL ? SECOND))"
                );
                $stmt->execute([$lockName, $timeout]);
                return true;
            } catch (PDOException $e) {
                // 锁已存在,尝试获取过期锁
                return $this->acquireExpiredLock($lockName, $timeout);
            }
        }
        
        private function acquireExpiredLock($lockName, $timeout)
        {
            $stmt = $this->pdo->prepare(
                "UPDATE {$this->tableName} SET acquired_at = NOW(), expires_at = DATE_ADD(NOW(), INTERVAL ? SECOND) WHERE lock_name = ? AND expires_at < NOW()"
            );
            $stmt->execute([$timeout, $lockName]);
            
            return $stmt->rowCount() > 0;
        }
    }
    

    基于ZooKeeper的分布式锁

    对于更复杂的分布式场景,ZooKeeper提供了更可靠的锁机制。虽然配置相对复杂,但它的强一致性保证是其他方案无法比拟的。

    
    use ZooKeeper;
    
    class ZookeeperDistributedLock
    {
        private $zk;
        private $lockPath = '/locks/';
        private $currentNode;
        
        public function __construct($hosts)
        {
            $this->zk = new ZooKeeper($hosts);
        }
        
        public function acquire($lockName)
        {
            $path = $this->lockPath . $lockName;
            $this->currentNode = $this->zk->create(
                $path . '/lock-',
                '',
                [ZooKeeper::EPHEMERAL | ZooKeeper::SEQUENCE],
                ZooKeeper::PERM_ALL
            );
            
            return $this->waitForLock($path);
        }
        
        private function waitForLock($path)
        {
            $children = $this->zk->getChildren($path);
            sort($children);
            
            $currentNodeName = basename($this->currentNode);
            return $children[0] === $currentNodeName;
        }
    }
    

    实战中的注意事项

    根据我的经验,选择哪种方案需要考虑以下几个因素:

    • 性能要求:Redis性能最好,适合高并发场景
    • 一致性要求:ZooKeeper提供最强的一致性保证
    • 运维成本:数据库方案部署最简单
    • 锁超时设置:一定要设置合理的超时时间,避免死锁

    总结

    在实际项目中,我通常根据具体场景选择合适的方案。对于大多数Web应用,Redis分布式锁已经足够;对于金融等对一致性要求极高的场景,ZooKeeper是更好的选择。希望这些经验能帮助你在分布式系统开发中少走弯路!

    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!

    源码库 » PHP后端分布式锁实现方案