本文目录导读:

在PHP项目中生成不重复的订单编号,常见且有效的方法有这几种:
基于时间的唯一ID(推荐)
时间戳 + 随机数
function generateOrderNo() {
$time = date('YmdHis'); // 20240115143025
$rand = str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT); // 随机4位数字
return 'ORD' . $time . $rand;
}
// 输出:ORD202401151430251234
毫秒时间戳 + 用户ID
function generateOrderNo($userId) {
list($micro, $sec) = explode(' ', microtime());
$milliSec = str_pad(round($micro * 1000), 3, '0', STR_PAD_LEFT);
$userIdStr = str_pad($userId, 6, '0', STR_PAD_LEFT);
return date('YmdHis') . $milliSec . $userIdStr;
}
使用数据库自增ID
MySQL + 业务前缀
// 先插入订单获取自增ID
$sql = "INSERT INTO orders (status, created_at) VALUES ('pending', NOW())";
$db->query($sql);
$orderId = $db->insert_id;
// 生成订单号
$orderNo = date('Ymd') . str_pad($orderId, 8, '0', STR_PAD_LEFT);
Redis 自增(高并发推荐)
function generateOrderNo() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$today = date('Ymd');
$key = "order_no_$today";
// 原子自增,每天重置
$num = $redis->incr($key);
$redis->expire($key, 86400); // 24小时过期
return 'ORD' . $today . str_pad($num, 8, '0', STR_PAD_LEFT);
}
UUID(最简单但最长)
function generateOrderNo() {
return 'ORD' . strtoupper(uniqid());
// 输出:ORD65A4B3C2D1E0F
}
// 或使用更标准的UUID v4
function generateUUIDv4() {
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
雪花算法(Snowflake)- 分布式推荐
class Snowflake {
private $workerId;
private $sequence = 0;
private $lastTimestamp = -1;
const EPOCH = 1700000000000; // 自定义开始时间戳
const WORKER_BITS = 5;
const SEQUENCE_BITS = 12;
public function __construct($workerId = 1) {
$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) & 4095; // 2^12 - 1
if ($this->sequence == 0) {
while (($timestamp = $this->getTimestamp()) <= $this->lastTimestamp) {
usleep(100);
}
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << 22) | ($this->workerId << 12) | $this->sequence;
}
private function getTimestamp() {
return floor(microtime(true) * 1000);
}
}
// 使用
$snowflake = new Snowflake(1);
$orderNo = 'ORD' . $snowflake->nextId();
最佳实践建议
电商系统推荐方案:
class OrderNoGenerator {
public static function generate($userId = 0) {
// 格式:前缀(3) + 日期(8) + 时间(6) + 用户(4) + 随机(4)
$prefix = 'ORD';
$date = date('Ymd');
$time = date('His');
$userPart = str_pad(substr($userId, -4), 4, '0', STR_PAD_LEFT);
$randPart = str_pad(mt_rand(0, 9999), 4, '0', STR_PAD_LEFT);
return $prefix . $date . $time . $userPart . $randPart;
}
}
数据库唯一索引兜底
无论采用哪种方法,都应给订单号字段添加唯一索引:
ALTER TABLE orders ADD UNIQUE INDEX idx_order_no (order_no);
选择建议:
- 简单项目:时间+随机数
- 高并发:Redis自增
- 分布式系统:雪花算法
- 无需人工记忆:UUID
记得在代码中添加重试机制,防止极小概率的重复情况。