订单号、流水号是业务系统的高频需求,要求可读性好、重复率低、便于按时间检索。最朴素的方案就是日期时间加随机数,配合足够长的随机段,碰撞概率在单机场景下可以忽略。

实现思路

前缀加 date 秒级时间保证可读,random_int 生成真随机 6 位数抗碰撞,再拼上毫秒低三位进一步拉开同一秒内的间距;通过 length 参数可统一控制总长度,方便与第三方接口对齐位数。

完整代码

<?php
/**
 * 生成业务单号:前缀 + yyyymmddHHMMSS + 6位随机 + 3位毫秒
 * @param string $prefix 业务前缀,如 RE 充值 / DD 订单
 * @param int    $length 单号总长度,超出自动截断
 */
function createOrderNo(string $prefix = "DD", int $length = 32): string
{
    $date = date("YmdHis");
    $rand = str_pad((string)random_int(0, 999999), 6, "0", STR_PAD_LEFT);
    $msec = str_pad((string)((int)(microtime(true) * 1000) % 1000), 3, "0", STR_PAD_LEFT);
    $no = $prefix . $date . $rand . $msec;
    return substr($no, 0, $length);
}

echo createOrderNo("RE") . PHP_EOL;
// 输出示例:RE20260905103025012345678901
?>

防重与扩展

  • 同一毫秒内的理论碰撞需两次随机完全相同,概率约百万分之一;
  • 要求全局严格唯一的场景,可在此基础上用 Redis INCR 维护每日自增段,如 DD + 日期 + 自增号;
  • 写库时对单号字段建唯一索引,极端碰撞时捕获 Duplicate Entry 重新生成一次即可兜底。

小结

该方案零依赖、性能好、可读性强,适合绝大多数业务系统;需要跨机器严格有序时可再叠加分布式 ID 组件。