PHP项目分布式ID如何生成唯一编号

wen PHP项目 26

本文目录导读:

PHP项目分布式ID如何生成唯一编号

  1. UUID(最简单)
  2. 雪花算法(推荐)
  3. Redis自增(适合高并发)
  4. 数据库号段模式(最稳妥)
  5. 方案选择建议

在PHP分布式系统中生成唯一ID,常见方案如下,我会从实现复杂度性能两个维度帮你分析:

UUID(最简单)

// 生成36位字符串:550e8400-e29b-41d4-a716-446655440000
$uuid = bin2hex(random_bytes(16)); // 32位无连字符
// 或使用ramsey/uuid包
use Ramsey\Uuid\Uuid;
$uuid = Uuid::uuid4()->toString();

特点:

  • ✅ 无中心化,完全本地生成
  • ❌ 长度36位,不适合做数据库主键(影响索引性能)
  • ❌ 无序,B+树插入效率低

雪花算法(推荐)

Twitter开源的经典方案,PHP实现:

class Snowflake {
    const EPOCH = 1609459200000; // 2021-01-01 00:00:00 毫秒
    private $workerId;
    private $lastTimestamp = -1;
    private $sequence = 0;
    public function __construct($workerId = 0) {
        $this->workerId = $workerId;
    }
    public function nextId() {
        $timestamp = $this->getTimestamp();
        if ($timestamp < $this->lastTimestamp) {
            throw new \Exception('时钟回拨');
        }
        if ($timestamp == $this->lastTimestamp) {
            $this->sequence = ($this->sequence + 1) & 0xFFF;
            if ($this->sequence == 0) {
                $timestamp = $this->waitNextMs($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }
        $this->lastTimestamp = $timestamp;
        return (($timestamp - self::EPOCH) << 22)
            | ($this->workerId << 12)
            | $this->sequence;
    }
    private function getTimestamp() {
        return intval(microtime(true) * 1000);
    }
    private function waitNextMs($lastTimestamp) {
        $timestamp = $this->getTimestamp();
        while ($timestamp <= $lastTimestamp) {
            usleep(100);
            $timestamp = $this->getTimestamp();
        }
        return $timestamp;
    }
}
// 使用
$snowflake = new Snowflake($workerId);
$id = $snowflake->nextId(); // 纯数字,短且有序

特点:

  • ✅ 64位整数,有序递增
  • ✅ 高性能(PHP环境下每秒数万)
  • ✅ 支持分布式(通过workerId区分节点)

Redis自增(适合高并发)

// 每天一个key,自增
$date = date('Ymd');
$seq = $redis->incr("order_id:$date");
$orderId = $date . str_pad($seq, 6, '0', STR_PAD_LEFT);
// 使用INCRBY批量获取
$ids = [];
$idRange = $redis->incrby('global_id', 10);
for ($i = $idRange - 9; $i <= $idRange; $i++) {
    $ids[] = $id;
}

特点:

  • ✅ 绝对唯一,可控制长度
  • ❌ 依赖Redis,需要考虑高可用
  • ✅ 适合秒杀等高并发场景

数据库号段模式(最稳妥)

// 数据库表结构
CREATE TABLE id_generator (
    id INT PRIMARY KEY AUTO_INCREMENT,
    `key` VARCHAR(50) UNIQUE,
    max_id BIGINT NOT NULL DEFAULT 0,
    step INT NOT NULL DEFAULT 1000
);
// PHP获取号段
function getSegment($key) {
    $db = getDb();
    $db->beginTransaction();
    $stmt = $db->query("SELECT max_id, step FROM id_generator WHERE `key`='$key' FOR UPDATE");
    $row = $stmt->fetch();
    $newMax = $row['max_id'] + $row['step'];
    $db->query("UPDATE id_generator SET max_id=$newMax WHERE `key`='$key'");
    $db->commit();
    return range($row['max_id'] + 1, $newMax);
}
// 使用时
$ids = getSegment('order');
$id = array_shift($ids); // 内存中分配

特点:

  • ✅ 不依赖第三方组件
  • ✅ 批量获取,减少数据库访问
  • ❌ 需要管理号段池

方案选择建议

场景 推荐方案 原因
订单号(需展示) Redis自增 可以控制长度,如"20231201-000001"
内部系统ID 雪花算法 性能好,自增索引
文件/资源标识 UUID 无需中心化
大型分布式系统 号段模式 可控性强,无单点

实际项目常见做法:

  • 订单类:时间前缀 + Redis自增20231201001
  • 用户ID:雪花算法
  • 文件存储:UUID或MD5
  • 消息ID:雪花算法

需要我帮你针对具体业务场景做方案设计吗?

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