PHP分布式ID生成策略与实现在分布式系统中生成全局唯一的ID是一个常见需求。今天说说PHP中实现分布式ID的各种方法。自增ID是最简单的方式但在分布式环境下不能使用。php// Redis自增IDclass RedisIdGenerator{private Redis $redis;private string $prefix;private int $batchSize;public function __construct(Redis $redis, string $prefix id:, int $batchSize 100){$this-redis $redis;$this-prefix $prefix;$this-batchSize $batchSize;}public function next(string $key): int{return $this-redis-incr({$this-prefix}{$key});}public function nextBatch(string $key): array{$start $this-redis-incrBy({$this-prefix}{$key}, $this-batchSize);$ids [];for ($i 0; $i $this-batchSize; $i) {$ids[] $start - $i;}return $ids;}}?雪花算法Snowflake是分布式ID生成的标准方案。它由时间戳、机器ID和序列号组成。phpclass SnowflakeIdGenerator{private int $workerId;private int $datacenterId;private int $sequence 0;private int $lastTimestamp -1;// 位偏移private int $workerIdBits 5;private int $datacenterIdBits 5;private int $sequenceBits 12;// 最大值private int $maxWorkerId;private int $maxDatacenterId;// 位偏移量private int $workerIdShift;private int $datacenterIdShift;private int $timestampShift;// 起始时间戳2024-01-01private int $epoch 1704067200;public function __construct(int $workerId 0, int $datacenterId 0){$this-maxWorkerId -1 ^ (-1 $this-workerIdBits);$this-maxDatacenterId -1 ^ (-1 $this-datacenterIdBits);if ($workerId $this-maxWorkerId || $workerId 0) {throw new InvalidArgumentException(Worker ID {$workerId} 超出范围 (0-{$this-maxWorkerId}));}if ($datacenterId $this-maxDatacenterId || $datacenterId 0) {throw new InvalidArgumentException(Datacenter ID {$datacenterId} 超出范围 (0-{$this-maxDatacenterId}));}$this-workerId $workerId;$this-datacenterId $datacenterId;$this-workerIdShift $this-sequenceBits;$this-datacenterIdShift $this-workerIdShift $this-workerIdBits;$this-timestampShift $this-datacenterIdShift $this-datacenterIdBits;}public function nextId(): int{$timestamp $this-getTimestamp();if ($timestamp $this-lastTimestamp) {throw new RuntimeException(时钟回退拒绝生成ID);}if ($timestamp $this-lastTimestamp) {$this-sequence ($this-sequence 1) (-1 ^ (-1 $this-sequenceBits));if ($this-sequence 0) {// 当前毫秒序列号用完等待下一毫秒$timestamp $this-waitNextMillis($timestamp);}} else {$this-sequence 0;}$this-lastTimestamp $timestamp;return (($timestamp - $this-epoch) $this-timestampShift)| ($this-datacenterId $this-datacenterIdShift)| ($this-workerId $this-workerIdShift)| $this-sequence;}public function parseId(int $id): array{$timestamp ($id $this-timestampShift) $this-epoch;$datacenterId ($id $this-datacenterIdShift) $this-maxDatacenterId;$workerId ($id $this-workerIdShift) $this-maxWorkerId;$sequence $id (-1 ^ (-1 $this-sequenceBits));return [id $id,timestamp $timestamp,datetime date(Y-m-d H:i:s, $timestamp),datacenter_id $datacenterId,worker_id $workerId,sequence $sequence,];}private function getTimestamp(): int{return (int)(microtime(true) * 1000);}private function waitNextMillis(int $currentTimestamp): int{while ($currentTimestamp $this-lastTimestamp) {usleep(100);$currentTimestamp $this-getTimestamp();}return $currentTimestamp;}}$generator new SnowflakeIdGenerator(1, 1);$ids [];for ($i 0; $i 10; $i) {$ids[] $generator-nextId();}echo 生成的ID:\n;foreach ($ids as $id) {$info $generator-parseId($id);echo {$id} - {$info[datetime]} (DC:{$info[datacenter_id]}, W:{$info[worker_id]}, Seq:{$info[sequence]})\n;}?ULID是另一种分布式ID生成方案比UUID更短且可以排序。phpclass UlidGenerator{private static string $encoding 0123456789ABCDEFGHJKMNPQRSTVWXYZ;public static function generate(): string{$time microtime(true);$timestamp (int)($time * 1000);$random random_bytes(10);// 时间戳部分10字符$timeChars ;for ($i 9; $i 0; $i--) {$timeChars self::$encoding[$timestamp % 32] . $timeChars;$timestamp (int)($timestamp / 32);}// 随机部分16字符$randomChars ;$rand unpack(C*, $random);foreach ($rand as $byte) {$randomChars . self::$encoding[$byte % 32];}return strtolower($timeChars . $randomChars);}public static function extractTime(string $ulid): float{$timeChars substr($ulid, 0, 10);$timestamp 0;for ($i 0; $i 10; $i) {$pos strpos(self::$encoding, strtoupper($timeChars[$i]));if ($pos false) continue;$timestamp $timestamp * 32 $pos;}return $timestamp / 1000;}}$ulids [];for ($i 0; $i 5; $i) {$ulids[] UlidGenerator::generate();}echo ULID:\n;foreach ($ulids as $ulid) {$time UlidGenerator::extractTime($ulid);echo {$ulid} - . date(Y-m-d H:i:s, (int)$time) . \n;}sort($ulids);echo \n排序后:\n;foreach ($ulids as $ulid) {echo {$ulid}\n;}?UUID作为最简单的方案但性能和可读性不如雪花算法和ULID。分布式ID生成方案的选择取决于具体需求。雪花算法性能高、可排序但依赖时钟。ULID可排序、纯随机但不含机器信息。UUID生成简单但不具备业务意义。Redis自增适合小规模应用。选择合适的方案能避免很多分布式环境下的ID冲突问题。