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函数的区间尽量去放大。

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

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

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

相关推荐
CodeSheep4 小时前
同事去年绩效是C,提离职领导死活不让走,后来领导私下说他走了,就没人背这个绩效了
前端·后端·程序员
怕浪猫4 小时前
第19章:Go语言工具链与工程实践
后端·go·编程语言
BingoGo5 小时前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·laravel
风象南5 小时前
WHAT? AI把我仓库内容全删了!!!
后端
摸鱼的春哥5 小时前
春哥的Agent通关秘籍12:本地RAG实战(中下)向量化与落库
前端·javascript·后端
JaguarJack5 小时前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
摸鱼的春哥5 小时前
专家实验让AI做战争决策,AI的选择太暴力了
前端·javascript·后端
Victor35612 小时前
MongoDB(15) 如何在MongoDB中启用身份验证?
后端
Victor35612 小时前
MongoDB(14)如何修改MongoDB的默认端口?
后端
怒放吧德德14 小时前
Spring Boot 实战:RSA+AES 接口全链路加解密(防篡改 / 防重放)
java·spring boot·后端