本文目录导读:

在 PHP 中生成“趋势唯一”的值,通常有几种不同的理解,我猜测您可能有以下几种需求,我分别给出解决方案:
基于时间戳的唯一ID(带趋势性)
如果您想要按时间递增的唯一ID(类似雪花算法),这样数据库索引效率高:
<?php
/**
* 生成趋势递增的唯一ID(类Twitter雪花算法)
* 64位:1位符号 + 41位时间戳 + 10位机器ID + 12位序列号
*/
class SnowflakeId {
private $workerId;
private $sequence = 0;
private $lastTimestamp = -1;
// 开始时间戳(2020-01-01)
private $twepoch = 1577808000000;
public function __construct($workerId = 0) {
$this->workerId = $workerId & 0x3FF; // 10位
}
public function nextId() {
$timestamp = $this->currentTimeMillis();
// 时钟回拨处理
if ($timestamp < $this->lastTimestamp) {
throw new Exception("Clock moved backwards");
}
if ($timestamp == $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & 0xFFF; // 12位序列号
if ($this->sequence == 0) {
// 等待下一毫秒
$timestamp = $this->waitNextMillis($this->lastTimestamp);
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
// 组合:时间戳 + 机器ID + 序列号
return (($timestamp - $this->twepoch) << 22) |
($this->workerId << 12) |
$this->sequence;
}
private function currentTimeMillis() {
return (int)(microtime(true) * 1000);
}
private function waitNextMillis($lastTimestamp) {
$timestamp = $this->currentTimeMillis();
while ($timestamp <= $lastTimestamp) {
$timestamp = $this->currentTimeMillis();
}
return $timestamp;
}
}
// 使用示例
$snowflake = new SnowflakeId(1);
$uniqueId = $snowflake->nextId();
echo $uniqueId . "\n";
echo "时间戳: " . (($uniqueId >> 22) + 1577808000000) . "\n";
带时间戳的字符串唯一ID(人类可读)
如果您想要格式化的、肉眼可见趋势的唯一字符串:
<?php
/**
* 生成带时间戳的唯一ID字符串
* 格式:20240101-154530-123456
*/
function generateTimedUniqueId($prefix = '') {
// 当前时间
$date = date('Ymd-His');
// 毫秒
$milliseconds = (int)(microtime(true) * 1000) % 1000;
// 随机数(防止同一毫秒冲突)
$random = bin2hex(random_bytes(4));
return $prefix . $date . '-' . str_pad($milliseconds, 3, '0', STR_PAD_LEFT) . '-' . $random;
}
// 使用示例
echo generateTimedUniqueId('USER_');
// 输出:USER_20240101-154530-123-3f8a2b1c
数据库友好的趋势ID(自增+时间戳)
如果您在数据库中使用,可以结合自增ID和时间戳:
<?php
/**
* 生成用于数据库的趋势性唯一ID
* 结构:时间戳(8位16进制) + 随机数(8位16进制) + 哈希
*/
function generateDatabaseTrendId($data = '') {
// 时间戳(秒 + 微秒)
$timestamp = dechex(time());
$microtime = dechex((int)(microtime() * 1000000) % 16);
// 基于数据的哈希
$hash = substr(hash('md5', $timestamp . $data . uniqid()), 0, 8);
// 随机数
$random = bin2hex(random_bytes(4));
return $timestamp . $microtime . $hash . $random;
}
轻量级通用方案(推荐简单场景)
如果您不需要太复杂,PHP自带函数组合:
<?php
/**
* 简易趋势唯一ID(推荐)
*/
function simpleTrendingId($prefix = '') {
$time = time(); // 当前秒级时间戳
$micro = microtime(true) * 1000000; // 微秒级时间戳
$uniqid = uniqid(); // 基于微时间的唯一ID
// 组合
$id = $prefix . $time . '-' .
str_pad($micro % 1000000, 6, '0', STR_PAD_LEFT) . '-' .
substr($uniqid, -8);
return $id;
}
高并发下基于 Redis 的实现
如果需要分布式高并发支持:
<?php
/**
* Redis 自增生成趋势ID
*/
class RedisTrendId {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
public function nextId($key, $prefix = '') {
// 用当前时间戳作为Redis key的一部分,保证趋势性
$today = date('Ymd'); // 每天一个计数器,或按小时/分钟
// 原子自增
$sequence = $this->redis->incr("id_gen:{$key}:{$today}");
// 设置过期时间(避免无限增长)
if ($sequence == 1) {
$this->redis->expire("id_gen:{$key}:{$today}", 2 * 86400);
}
// 组合:日期+序列号
return $prefix . $today . '-' . str_pad($sequence, 6, '0', STR_PAD_LEFT);
}
}
推荐选择
| 场景 | 推荐方案 |
|---|---|
| 数据库主键 | 雪花算法(方案1) |
| API响应ID | 简单趋势ID(方案4) |
| 用户可读 | 带时间戳字符串(方案2) |
| 分布式系统 | Redis实现(方案5) |
| 日志追踪 | 微秒+随机(方案3) |
简单场景最佳实践:
$uniqueId = uniqid('prefix_', true);
// 会生成类似:prefix_65ab1234c5d6e7.23456789
// 包含了微秒时间戳和随机性,且趋势递增
这些方案都保证了唯一性,并且ID具有趋势递增的特性,便于数据库索引优化,您根据具体需求选择最适合的方案即可。