PHP项目订单编号如何生成不重复

wen PHP项目 24

本文目录导读:

PHP项目订单编号如何生成不重复

  1. 基于时间的唯一ID(推荐)
  2. 使用数据库自增ID
  3. Redis 自增(高并发推荐)
  4. UUID(最简单但最长)
  5. 雪花算法(Snowflake)- 分布式推荐
  6. 最佳实践建议
  7. 数据库唯一索引兜底

在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

记得在代码中添加重试机制,防止极小概率的重复情况。

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