PHP项目分片扩容如何平滑增加分片节点

wen PHP项目 30

本文目录导读:

PHP项目分片扩容如何平滑增加分片节点

  1. 一致性哈希方案(推荐)
  2. 双写方案(避免停机)
  3. 数据迁移策略
  4. 完整扩容流程
  5. 最佳实践建议
  6. 注意事项

在PHP项目中进行数据库分片扩容时,平滑增加分片节点是一个重要挑战,以下是几种主流方案:

一致性哈希方案(推荐)

实现原理

class ConsistentHash {
    private $nodes = [];
    private $positions = [];
    private $virtualNodeCount = 64; // 每台物理服务器的虚拟节点数
    public function addNode($node) {
        for ($i = 0; $i < $this->virtualNodeCount; $i++) {
            $position = crc32($node . '_' . $i);
            $this->positions[$position] = $node;
        }
        ksort($this->positions);
        $this->nodes[$node] = true;
    }
    public function getNode($key) {
        if (empty($this->positions)) return null;
        $hash = crc32($key);
        $positions = $this->positions;
        foreach ($positions as $position => $node) {
            if ($hash <= $position) {
                return $node;
            }
        }
        // 如果超出范围,返回第一个节点
        return reset($this->positions);
    }
}

优势

  • 最小数据迁移:只迁移需要重新分配的数据
  • 平滑扩容:无需停机,逐步增加节点

双写方案(避免停机)

实现步骤

class ShardManager {
    private $oldShards = [];
    private $newShards = [];
    private $migrationMode = 'write_both'; // read_old/write_both/read_new
    public function writeData($key, $data) {
        switch ($this->migrationMode) {
            case 'write_both':
                // 同时写入新旧分片
                $this->writeToShard($this->oldShards, $key, $data);
                $this->writeToShard($this->newShards, $key, $data);
                break;
            case 'read_new':
                // 只写新分片
                $this->writeToShard($this->newShards, $key, $data);
                break;
            default:
                $this->writeToShard($this->oldShards, $key, $data);
        }
    }
    public function readData($key) {
        switch ($this->migrationMode) {
            case 'read_old':
                return $this->readFromShard($this->oldShards, $key);
            case 'write_both':
                // 先读新分片,没有则读旧分片
                $data = $this->readFromShard($this->newShards, $key);
                if ($data === null) {
                    $data = $this->readFromShard($this->oldShards, $key);
                }
                return $data;
            case 'read_new':
                return $this->readFromShard($this->newShards, $key);
        }
    }
}

数据迁移策略

渐进式迁移

class DataMigrator {
    private $sourceShard;
    private $targetShard;
    private $batchSize = 1000;
    public function migrateData($dateRange) {
        $startTime = microtime(true);
        $migrated = 0;
        // 按主键范围分批迁移
        while ($items = $this->getNextBatch($batchSize)) {
            $this->migrateBatch($items);
            $migrated += count($items);
            // 控制迁移速度,避免影响线上服务
            if (microtime(true) - $startTime > 5) {
                sleep(1); // 短暂暂停
                $startTime = microtime(true);
            }
        }
    }
    private function migrateBatch($items) {
        $this->beginTransaction($this->sourceShard, $this->targetShard);
        foreach ($items as $item) {
            // 先写入目标分片
            $this->writeToTarget($item);
            // 标记源数据为已迁移
            $this->markMigrated($item['id']);
        }
        $this->commit();
    }
}

完整扩容流程

准备期

// 1. 添加新分片服务器
$shardManager->addNewShard('shard3', $config);
// 2. 启动双写
$shardManager->setMigrationMode('write_both');
// 3. 修改路由规则
$router->addNewShard('shard3', '2024-01-01');

数据迁移

// 1. 分批迁移数据
$migrator = new DataMigrator();
$migrator->startMigration();
// 2. 监控迁移状态
while (!$migrator->isComplete()) {
    $progress = $migrator->getProgress();
    echo "Migration progress: {$progress}%\n";
    sleep(60); // 每分钟检查一次
}

切换期

// 1. 验证数据一致性
$validator = new DataValidator();
$validator->compareData($oldShard, $newShard);
// 2. 切换到只读新分片
$shardManager->setMigrationMode('read_new');
// 3. 等待确认无误后
$shardManager->setMigrationMode('normal'); // 正常模式
$shardManager->removeOldShardConfig();

最佳实践建议

分片键设计

// 使用复合分片键
$shardKey = $userId . '_' . ($date->format('Ymd'));
$shardId = crc32($shardKey) % $totalShards;

监控与告警

class ShardMonitor {
    public function monitor() {
        $metrics = [
            'read_latency' => $this->measureReadLatency(),
            'write_latency' => $this->measureWriteLatency(),
            'migration_progress' => $this->getMigrationProgress(),
            'error_rate' => $this->calculateErrorRate()
        ];
        if ($metrics['error_rate'] > 0.01) { // 1%的错误率
            $this->alert('High error rate detected');
            $this->rollback();
        }
    }
}

回滚机制

class RollbackHandler {
    public function rollback() {
        // 1. 停止所有写操作
        $this->pauseWrites();
        // 2. 切换回旧分片
        $this->setActiveShard('old');
        // 3. 清理新分片上的数据
        $this->cleanNewShard();
        // 4. 恢复写操作
        $this->resumeWrites();
    }
}

注意事项

  1. 数据一致性:使用事务或2PC保证数据一致性
  2. 性能影响:双写期间会有额外开销(约20-30%)
  3. 监控报警:实时监控延迟、错误率等指标
  4. 渐进扩容:一次增加太多节点可能影响性能
  5. 测试验证:先在测试环境模拟完整扩容流程

通过以上方案,可以实现PHP项目分片数据库的平滑扩容,最小化对线上服务的影响。

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