PHP 怎么线性一致性

wen PHP项目 3

本文目录导读:

PHP 怎么线性一致性

  1. 使用 Redis 分布式锁(最常用)
  2. 使用 PostgreSQL 事务(最强保证)
  3. 使用原子操作(Redis Lua 脚本)
  4. 使用分布式系统(etcd/Consul)
  5. 性能优化建议

在 PHP 中实现线性一致性(Linearizability)通常不是语言层面的特性,而是需要通过并发控制外部存储来保证,以下是几种常见方案:

使用 Redis 分布式锁(最常用)

<?php
class LinearizableCounter {
    private $redis;
    private $lockKey;
    public function __construct($redis) {
        $this->redis = $redis;
        $this->lockKey = 'lock:counter';
    }
    // 原子递增操作(线性一致)
    public function increment($userId) {
        // 获取分布式锁
        $lock = $this->acquireLock();
        try {
            // 读取当前值
            $current = $this->redis->get("counter:{$userId}") ?: 0;
            // 执行操作
            $newValue = $current + 1;
            $this->redis->set("counter:{$userId}", $newValue);
            return $newValue;
        } finally {
            // 释放锁
            $this->releaseLock($lock);
        }
    }
    private function acquireLock($timeout = 10) {
        $uniqueId = uniqid('lock:', true);
        $start = time();
        // 尝试获取锁
        while (time() - $start < $timeout) {
            if ($this->redis->set($this->lockKey, $uniqueId, ['NX', 'EX' => 30])) {
                return $uniqueId;
            }
            usleep(100000); // 100ms
        }
        throw new Exception("Failed to acquire lock");
    }
    private function releaseLock($lockId) {
        // 使用 Lua 脚本确保原子性
        $script = "
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            end
            return 0
        ";
        $this->redis->eval($script, [$this->lockKey, $lockId], 1);
    }
}

使用 PostgreSQL 事务(最强保证)

<?php
class LinearizableAccount {
    private $pdo;
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    // 转账操作(线性一致)
    public function transfer($fromAccount, $toAccount, $amount) {
        $this->pdo->beginTransaction();
        try {
            // 锁定源账户
            $stmt = $this->pdo->prepare(
                "SELECT balance FROM accounts 
                 WHERE id = ? FOR UPDATE"
            );
            $stmt->execute([$fromAccount]);
            $fromBalance = $stmt->fetchColumn();
            // 检查余额
            if ($fromBalance < $amount) {
                throw new Exception("Insufficient funds");
            }
            // 更新源账户
            $this->pdo->prepare(
                "UPDATE accounts SET balance = balance - ? WHERE id = ?"
            )->execute([$amount, $fromAccount]);
            // 更新目标账户
            $this->pdo->prepare(
                "UPDATE accounts SET balance = balance + ? WHERE id = ?"
            )->execute([$amount, $toAccount]);
            // 提交事务
            $this->pdo->commit();
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }
}

使用原子操作(Redis Lua 脚本)

<?php
class AtomicOperations {
    private $redis;
    // 使用 Lua 脚本实现原子操作(线性一致)
    public function checkAndSet($key, $expectedValue, $newValue) {
        $script = <<<LUA
            local current = redis.call('GET', KEYS[1])
            if current == ARGV[1] then
                redis.call('SET', KEYS[1], ARGV[2])
                return 1
            end
            return 0
        LUA;
        return $this->redis->eval(
            $script, 
            [$key, $expectedValue, $newValue], 
            1
        );
    }
    // 乐观锁实现
    public function optimisticUpdate($key, $updateFunc) {
        $retryCount = 0;
        do {
            // 读取当前版本
            $current = $this->redis->get($key);
            $newValue = $updateFunc($current);
            // 使用 WATCH/MULTI/EXEC 实现乐观锁
            $this->redis->watch($key);
            $this->redis->multi();
            $this->redis->set($key, $newValue);
            $result = $this->redis->exec();
            $retryCount++;
        } while (!$result && $retryCount < 10);
        return $result;
    }
}

使用分布式系统(etcd/Consul)

<?php
// 使用 etcd 实现分布式锁和线性一致性
class DistributedConsensus {
    private $client;
    public function __construct($client) {
        $this->client = $client;
    }
    // 选举主节点(线性一致)
    public function electLeader($serviceName, $nodeId) {
        $key = "leader/{$serviceName}";
        $ttl = 30; // 租约时长
        // 尝试获取分布式锁
        try {
            $this->client->grant($ttl);
            $this->client->put($key, $nodeId, ['lease' => $ttl]);
            return true;
        } catch (Exception $e) {
            return false;
        }
    }
    // 读取最新配置(线性一致)
    public function getLatestConfig($configKey) {
        // 使用 etcd 的线性一致读
        return $this->client->get($configKey, ['quorum' => true]);
    }
}

性能优化建议

<?php
class OptimizedLinearizability {
    // 批处理操作减少锁竞争
    public function batchIncrement(array $userIds) {
        $pipeline = $this->redis->pipeline();
        foreach ($userIds as $userId) {
            $pipeline->incr("counter:{$userId}");
        }
        return $pipeline->exec();
    }
    // 使用 Redis 计数器(本身是原子的)
    public function incrementCounter($userId) {
        // Redis 的 INCR 操作本身就是线性一致的
        return $this->redis->incr("counter:{$userId}");
    }
    // 缓存 + 数据库一致性
    public function writeThrough($key, $value) {
        // 先写数据库
        $this->pdo->prepare(
            "INSERT INTO cache_store (k, v) VALUES (?, ?) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v"
        )->execute([$key, $value]);
        // 再更新缓存
        $this->redis->set($key, $value);
        // 确保缓存与数据库一致
        return true;
    }
}
  1. 简单计数器:使用 Redis INCR 等原子操作
  2. 资源受限操作:使用分布式锁(Redlock 算法)
  3. 金融级一致:使用数据库事务 + 行级锁
  4. 高并发场景:使用 Lua 脚本组合多个操作
  5. 跨数据中心:使用 etcd/Consul 等共识算法

注意事项

  • 线性一致性会牺牲可用性(CAP 定理)
  • 需要保证锁的自动过期,防止死锁
  • 考虑操作的重试机制
  • 监控锁等待时间,避免性能瓶颈

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