V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
Alucns
V2EX  ›  PHP

求 16 位纯数字订单号产生算法

  •  
  •   Alucns · Dec 26, 2020 · 7534 views
    This topic created in 1948 days ago, the information mentioned may be changed or developed.
    最近业务方要求订单号不能大于 16 位,以前写的 18 位算法不能用了,改成 16 位重复的概率太高了,问问大家有什么好的算法分享!

    现在用的 18 位:

    <?php declare(strict_types=1);
    date_default_timezone_set('Etc/GMT-8');
    $time = microtime(true);
    function order()
    {
    $ids = 0;
    $aes = substr(microtime(), 2, 6);
    $pid = $aes . getmypid() . mt_rand(0, 999999);
    for($in = 0; $in < 18; $in++) {
    $ids += (int) (substr($pid, $in, 1));
    }
    return time() . $aes . str_pad(strval($ids), 2, '0', STR_PAD_LEFT);
    }

    $arr = array();
    for($i = 0; $i < 1000000; $i++) {
    $arr[] = order();
    }
    $arrs = array_count_values($arr);
    echo 'COUNT ' . count($arrs);
    echo '<br>';
    echo 'RAND ' . array_rand($arrs);
    echo '<br>';
    arsort($arrs);
    $as = [];
    foreach($arrs as $ids => $aos) {
    if ($aos > 1) {
    $as[] = $ids;
    echo $ids . ' - ' . $aos . '<br>';
    }
    }
    echo 'COUNT ' . count($as);
    echo '<br>';
    echo 'Time ' . number_format((microtime(true) - $time), 3);
    43 replies    2020-12-28 13:09:11 +08:00
    hbolive
        1
    hbolive  
       Dec 26, 2020
    单纯依靠算法,16 还是 18 位,都没法保证吧重复吧。
    所以应该有其他机制校验下生成的 ID 是否重复。
    laminux29
        2
    laminux29  
       Dec 26, 2020
    为啥订单号就不能老老实实从 1 开始自增?为啥订单号就不能常见的无符号 32 位整型?
    Alucns
        3
    Alucns  
    OP
       Dec 26, 2020
    @hbolive 是有校验,产生的订单写入 Redis 了,产生的时候从 Redis 检查一次。
    pxw2002
        4
    pxw2002  
       Dec 26, 2020 via Android
    有几位根据日期时间生成
    就不会重复了吧
    bleutee
        5
    bleutee  
       Dec 26, 2020 via iPhone
    @laminux29 萬一人家想操數據呢?一五一十的順序排列,咋操
    Alucns
        6
    Alucns  
    OP
       Dec 26, 2020
    @pxw2002 用毫秒加随机 2 位,每秒并发 1 万容易重复的。
    Alucns
        7
    Alucns  
    OP
       Dec 26, 2020
    @laminux29 自增要先写数据库,很多多业务是先拿订单号请求接口成功后才写入数据库。
    还有就是自增从 1 开始,容易被人查数据。
    jimmyismagic
        8
    jimmyismagic  
       Dec 26, 2020
    业务方的订单号和你内部的订单号完全可以分开啊,重复就重复呗
    Alucns
        9
    Alucns  
    OP
       Dec 26, 2020
    @jimmyismagic 这个就是内部订单号,不能重复的。
    jimmyismagic
        10
    jimmyismagic  
       Dec 26, 2020
    @Aluhao 所以他到底是甲方还是乙方?乙方糊弄过去就行了,甲方就怼回去,不懂瞎指导
    bsg1992
        11
    bsg1992  
       Dec 26, 2020
    时间戳 不就解决了吗
    crclz
        12
    crclz  
       Dec 26, 2020   ❤️ 3
    你说的 16 位是“个十百千万”的位吧,不是 bit 吧。

    如果是 16 个字符,那么约束我猜就是前端 js 的 number 超过 53~54bit 就会损失精度吧。

    一个解决方案是传字符串。

    如果非要数字,也不是不可以。你去看看 snowflake 算法。https://github.com/beyondfengyu/SnowFlake/blob/master/SnowFlake.java 把第 18 行里面的几个东西改一下,就可以应对不同的位数,本质是(使用年限,机器数量,数据中心数量,每秒并发数量)的权衡。当然首先得去看看 showflake 的结构再来改。
    Jooooooooo
        13
    Jooooooooo  
       Dec 26, 2020
    用时间戳+机器码

    每个机器上维护一个当前时间戳可用的序号

    你并发不至于那么大
    Alucns
        14
    Alucns  
    OP
       Dec 26, 2020
    @jimmyismagic 要拿内部唯一订单号去关联对方订单信息,要先请求传 16 位订单号过去。
    xuanbg
        15
    xuanbg  
       Dec 26, 2020   ❤️ 1
    魔改雪花算法,41 位时间戳不要动,10 位设备去掉,差不多就是 16 位数字了。

    但是,我估计你要的是 yyyyMMdd+2 位业务代码+2 位 xx+4 位流水号,流水号还要加密的那种。在我的 github 里面有,请自取
    DarkCat123
        16
    DarkCat123  
       Dec 26, 2020
    @laminux29 容易被人遍历数据,分析…… 而且不利于分布式数据库的 hash 分区。

    原来的 18 位用的是 snowflake?
    我建议要不要魔改下 snowflake, 牺牲一下时间来换位数降低(我记得原来的 snowflake 可以用 69 年)。
    要不然考虑找个 md5 sha1 之类的 hash 之后转 hex 看看能不能到 16 位吧。
    Alucns
        17
    Alucns  
    OP
       Dec 26, 2020
    @Jooooooooo 主要是只限制 16 个数字,长一点都好解决。
    DarkCat123
        18
    DarkCat123  
       Dec 26, 2020
    @xuanbg 老哥好巧,我们同时提到了魔改 snowlflake……
    jimmyismagic
        19
    jimmyismagic  
       Dec 26, 2020
    @Aluhao 那你从 1 开始不就行了,对方唯一,需要你传的唯一,对方根本不关心你传的什么,楼上的 snowfake 一般用的很多,分布式 id 用的比较多,单机也没什么问题,16 位 50 多个比特完全够了
    dorothyREN
        20
    dorothyREN  
       Dec 26, 2020
    13 位时间戳 加 3 位随机数或者 3 位自增数字
    lmmortal
        21
    lmmortal  
       Dec 26, 2020 via Android   ❤️ 1
    可以借鉴一下身份证号最后一位的校验算法
    lin07hui
        22
    lin07hui  
       Dec 26, 2020
    10 进制转 16 进制,不香吗
    lawler
        23
    lawler  
       Dec 26, 2020
    @dorothyREN #20 就这个方案里的自增就可以了。
    @Aluhao #7 redis incr,mq


    要觉得不行,不妨把解决 10w/tps 的方案分享出来?
    要觉得可以遍历,不妨把不能遍历的纯数字索引方案分享出来?
    ytmsdy
        24
    ytmsdy  
       Dec 26, 2020 via iPhone
    时间戳,加两个随机数,外加一个校验位。绰绰有余了!
    yuzo555
        25
    yuzo555  
       Dec 26, 2020
    “容易被人遍历数据”这个不是自增的问题,是你们程序权限控制的问题
    Soar360
        26
    Soar360  
       Dec 26, 2020
    20-1226-19-123456
    dorothyREN
        27
    dorothyREN  
       Dec 26, 2020
    @lawler #23 13 位时间戳+3 位数字,1 毫秒 1000 个单号,肯定够用了
    jzmws
        28
    jzmws  
       Dec 26, 2020
    @crclz 用雪花自己技术不过硬 然后 js 超出精度 坑死自己了 , 用这个算出来的是会操作 js 精度的
    jimmyismagic
        29
    jimmyismagic  
       Dec 26, 2020
    @jzmws js 太傻了,前端技术不行让后端想办法
    dream7758522
        30
    dream7758522  
       Dec 26, 2020 via Android   ❤️ 1
    很简单啊,时间戳→md5,16 位。→生成的 md5 中的 abcdef 用 123456 改写。
    举例:0123456789012345→md5,16 位→199aa7dcadfdb4e4→其中的字母字母用 123456 替换→1991174314642454 。
    觉得容易反算的话,可以加盐
    eyeix
        31
    eyeix  
       Dec 26, 2020
    @laminux29 #2 自增订单号会被竞争对手分析流水
    Lemeng
        32
    Lemeng  
       Dec 27, 2020
    重复 16 位 18 位都少。
    catror
        33
    catror  
       Dec 27, 2020 via Android
    任何一个生成 int64 ID 的算法都行,int64 的 16 进制编码结果就是 16 位
    mxT52CRuqR6o5
        34
    mxT52CRuqR6o5  
       Dec 27, 2020 via Android
    加密一个自增序列可以得到看上去随机的序列,不会重复,魔改加密算法让密文空间和明文空间在 16 位纯数字内
    mxT52CRuqR6o5
        35
    mxT52CRuqR6o5  
       Dec 27, 2020 via Android
    要是没有随机性的要求的话直接魔改 snowflake 就是了
    hijoker
        36
    hijoker  
       Dec 27, 2020
    13 位毫秒数+3 位随机数, 数据库这个字段设置类似唯一约束的东西, 如果真有重复,插入报错,根据报错信息重新生成随机数并重试插入
    itechify
        37
    itechify  
    PRO
       Dec 27, 2020 via Android
    @jzmws #28 我公司 long 类型都转字符串出去给前端了
    mostkia
        38
    mostkia  
       Dec 27, 2020
    一般都时间戳+盐保证唯一性
    neptuno
        39
    neptuno  
       Dec 27, 2020
    时间戳+(用户 id 转成 3 位数)?是不是最简单+重复率低
    ciddechan
        40
    ciddechan  
       Dec 28, 2020
    @dream7758522 这种的会不会分不出是真的 123456 还是替换的 123456
    wangritian
        41
    wangritian  
       Dec 28, 2020
    1.雪花算法:毫秒时间戳+集群号+机器号+进程号+自增序号
    这里最关键的是进程常驻,需要 swoole/workerman 插件
    2.额外写一个单实例部署的 id 生成器,加锁单线程运行,性能没有 1 好
    nano91
        42
    nano91  
       Dec 28, 2020
    16 位 MD5 ?拿老 18 位订单号做一次转换
    afewok
        43
    afewok  
       Dec 28, 2020
    但凡深入调研下业界和 github,都不会问出这个问题。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1020 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 141ms · UTC 19:06 · PVG 03:06 · LAX 12:06 · JFK 15:06
    ♥ Do have faith in what you're doing.