PHP中生成随机数遇到的重复问题

最近项目做了部分新功能上线了,大清早来到单位屁股刚坐下还没放热,就在群里被领导各种@。卡券的编号存在大量重复的。让我们一起走进这起特大史诗级bug


场景描述

项目开发中很多地方需要用到唯一编码,比如说订单、卡券、邀请码等等。这些编号是需要严格保证唯一性的,因为如果系统中订单的编号存在俩一模一样的,那造成很多数据的错乱。实现唯一编码的方式也是很多的,一般是通过底层的一些随机函数 mt_rand() uniqid()等等。

探索实现方式一

perl 复制代码
    public static function uniqidNumberCode($length = 10)
    {
        $time = time() . '';
        if ($length < 10) $length = 10;
        $string = ($time[0] + $time[1]) . substr($time, 2) . rand(0, 9);
        while (strlen($string) < $length) $string .= rand(0, 9);
        return $string;
    }

上面这个是用的三方包中的生成方式。参数中可以设置当前生成的编号长度。以当前时间的时间戳为基础数据,计算出10位随机数字。当我们单次去执行的时候,生成的数据是非常正确的。

但是,当我们需要批量的生成一堆的随机编号的时候

可以发现,以肉眼可见的速度出现了一堆的重复数据。

探索方式二

请教了一下chatgpt,让它生成一个方法如下

ini 复制代码
function generateUniqueNumber($length = 10) {
    // 生成一个唯一标识符
    $uniqueId = uniqid();

    // 移除标识符中的前缀并截取前10位
    $uniqueNumber = substr($uniqueId, strlen($uniqueId) - 10);

    return $uniqueNumber;
}

还是测试一下批量生成的场景。

虽然生成的数据看着正确,但是数据格式不是怎么的让人可以接受。还是希望生成10位纯数字的。

探索方式三

再一次请教了一下chatgpt。

ini 复制代码
function generateUniqueNumber($length = 10) {
    // 获取当前时间戳
    $timestamp = time();

    // 生成一个随机数,确保足够的随机性
    $randomNumber = mt_rand(100, 999);

    // 组合时间戳和随机数,并截取前10位
    $uniqueNumber = substr($timestamp . $randomNumber, 0, 10);

    return $uniqueNumber;
}

这下可好,生成的数据完全的毫无区别了。看来虽然gpt能帮助我们很多,但是选择还是需要谨慎些。一些关键逻辑如果出现严重问题可是不太好交差的。

探索方式四

我们尝试用php底层的生成随机数的函数 mt_rand() 对数据进行生成。

ini 复制代码
function generateUniqueNumber($length = 10) {
    $uniqueNumber = mt_rand(1000,9999);

    return $uniqueNumber;
}

发现在某些特殊的情况下还是会出现重复数据的可能。

实现方式一

php 复制代码
//生成10位随机数
function generateUniqueNumber() {
    list($usec, $sec) = explode(' ', microtime());
    $seed = (float) $sec + ((float) $usec * 100000);
    mt_srand($seed);
    return mt_rand(1000000000,9999999999);
}

在mt_rand函数的基础上,生成随机数之前。对随机数发生器的种子也用毫秒的值进行重新生成。然后再将mt_rand函数的区间尽量去放大。

这样基本可以满足需求,但是却是无法保证万无一失。程序这东西嘛,保证数据准确才是永远的追求。为了准确,我又给数据库的唯一编号字段加了唯一索引。这样就在数据库层面上保证了生成的编号肯定是唯一的。

当然,如果在生成编号以后,程序再主动去数据库中查询一遍字段是否存在,然后再执行下面的业务这就更好了。

以上是整个分析过程,不足之处或者更好方案欢迎给我留言。

相关推荐
程序猿DD1 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
余槐i4 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8264 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林5 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne6 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF6 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li7 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala7 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna7 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
码事漫谈7 小时前
因为太想讨好你,所以AI会骗你
后端