V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
VKMEPR
V2EX  ›  PHP

请教 PHP 高效生成简短唯一随机数方法

  •  
  •   VKMEPR · 2019-04-18 22:06:44 +08:00 · 5696 次点击
    这是一个创建于 2080 天前的主题,其中的信息可能已经有所发展或是发生改变。

    兼顾高效、尽量不重复。md5 最短 16 位数,uniqid 13 位(这样长度其实也可以,就是重复概率能不能再减低,不要求绝对唯一)

    打算时间戳+截取几个 uniqid 字符,有比这更好方法吗?

    19 条回复    2019-05-05 11:11:44 +08:00
    newmind
        1
    newmind  
       2019-04-18 22:10:38 +08:00
    hashids
    gz911122
        2
    gz911122  
       2019-04-18 22:29:43 +08:00
    各种开源的随机数生成器都可以啊
    MetalCore
        3
    MetalCore  
       2019-04-18 23:16:53 +08:00
    雪花算法
    flyingghost
        4
    flyingghost  
       2019-04-18 23:42:30 +08:00
    hashids + snowflake + GUID 简直绞杀我所有关于 id 的需求。
    wdlth
        5
    wdlth  
       2019-04-19 00:55:53 +08:00
    openssl_random_pseudo_bytes
    random_bytes
    可以试试
    vibbow
        6
    vibbow  
       2019-04-19 01:36:17 +08:00   ❤️ 2
    不要用 uniqid()

    当 CPU 足够快的时候,uniqid() 必重复
    lihongjie0209
        7
    lihongjie0209  
       2019-04-19 10:27:12 +08:00
    md5 是 hash 算法,不是随机数算法
    lihongjie0209
        8
    lihongjie0209  
       2019-04-19 10:28:11 +08:00
    时间戳在分布式环境中不太好维护吧, 要保证所有机器的时间都一致
    lihongjie0209
        9
    lihongjie0209  
       2019-04-19 10:29:49 +08:00
    uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] ) : string
    获取一个带前缀、基于当前时间微秒数的唯一 ID。

    uniqid 本来就是基于时间戳的, 所以你说的

    打算时间戳+截取几个 uniqid 字符

    不成立
    caoyouming
        10
    caoyouming  
       2019-04-19 10:30:54 +08:00
    拿去,之前写的一个。
    function genToken( $len = 32, $md5 = true ) {
    # Seed random number generator
    # Only needed for PHP versions prior to 4.2
    mt_srand( (double)microtime()*1000000 );
    # Array of characters, adjust as desired
    $chars = array(
    'Q', '@', '8', 'y', '%', '^', '5', 'Z', '(', 'G', '_', 'O', '`',
    'S', '-', 'N', '<', 'D', '{', '}', '[', ']', 'h', ';', 'W', '.',
    '/', '|', ':', '1', 'E', 'L', '4', '&', '6', '7', '#', '9', 'a',
    'A', 'b', 'B', '~', 'C', 'd', '>', 'e', '2', 'f', 'P', 'g', ')',
    '?', 'H', 'i', 'X', 'U', 'J', 'k', 'r', 'l', '3', 't', 'M', 'n',
    '=', 'o', '+', 'p', 'F', 'q', '!', 'K', 'R', 's', 'c', 'm', 'T',
    'v', 'j', 'u', 'V', 'w', ',', 'x', 'I', '$', 'Y', 'z', '*'
    );
    # Array indice friendly number of chars;
    $numChars = count($chars) - 1; $token = '';
    # Create random token at the specified length
    for ( $i=0; $i<$len; $i++ )
    $token .= $chars[ mt_rand(0, $numChars) ];
    # Should token be run through md5?
    if ( $md5 ) {
    # Number of 32 char chunks
    $chunks = ceil( strlen($token) / 32 ); $md5token = '';
    # Run each chunk through md5
    for ( $i=1; $i<=$chunks; $i++ )
    $md5token .= md5( substr($token, $i * 32 - 32, 32) );
    # Trim the token
    $token = substr($md5token, 0, $len);
    } return $token;
    }
    DavidNineRoc
        11
    DavidNineRoc  
       2019-04-19 11:31:08 +08:00
    26 小写 + 26 大写 + 10 个数字 = 62. 直接排列字符串, 然后要生成几位直接生成一个下标数组
    $randoms = 'xxxxxxxx'
    $wants = [1, 14, 15, 11, 11, 10];
    $randoms{1} . $randoms{14} . $randoms{15} . xxxxx

    62 的 6 次方大约是 568 亿
    62 的 7 次方大约是 3 5216 亿
    所以, 10 次方肯定不用想.
    mamahaha
        12
    mamahaha  
       2019-04-19 11:33:07 +08:00
    ```php
    function genToken( $len = 32, $md5 = true ) {
    # Seed random number generator
    # Only needed for PHP versions prior to 4.2
    mt_srand( (double)microtime()*1000000 );
    # Array of characters, adjust as desired
    $chars = array(
    'Q', '@', '8', 'y', '%', '^', '5', 'Z', '(', 'G', '_', 'O', '`',
    'S', '-', 'N', '<', 'D', '{', '}', '[', ']', 'h', ';', 'W', '.',
    '/', '|', ':', '1', 'E', 'L', '4', '&', '6', '7', '#', '9', 'a',
    'A', 'b', 'B', '~', 'C', 'd', '>', 'e', '2', 'f', 'P', 'g', ')',
    '?', 'H', 'i', 'X', 'U', 'J', 'k', 'r', 'l', '3', 't', 'M', 'n',
    '=', 'o', '+', 'p', 'F', 'q', '!', 'K', 'R', 's', 'c', 'm', 'T',
    'v', 'j', 'u', 'V', 'w', ',', 'x', 'I', '$', 'Y', 'z', '*'
    );
    # Array indice friendly number of chars;
    $numChars = count($chars) - 1; $token = '';
    # Create random token at the specified length
    for ( $i=0; $i<$len; $i++ )
    $token .= $chars[ mt_rand(0, $numChars) ];
    # Should token be run through md5?
    if ( $md5 ) {
    # Number of 32 char chunks
    $chunks = ceil( strlen($token) / 32 ); $md5token = '';
    # Run each chunk through md5
    for ( $i=1; $i<=$chunks; $i++ )
    $md5token .= md5( substr($token, $i * 32 - 32, 32) );
    # Trim the token
    $token = substr($md5token, 0, $len);
    } return $token;
    }
    ```
    最近在学 markdown,帮 10 楼整理下看看是啥效果
    lihongjie0209
        13
    lihongjie0209  
       2019-04-19 14:15:10 +08:00
    @caoyouming 还真有人使用自制的随机数生成器?
    caoyouming
        14
    caoyouming  
       2019-04-19 15:31:10 +08:00
    @lihongjie0209 我在之前写一个系统使用邮箱重置密码的时候用的这个生成 token。觉得还不错。
    lihongjie0209
        15
    lihongjie0209  
       2019-04-19 15:56:15 +08:00
    @caoyouming 随机数和加密算法一样,最好不要用自制的,使用全球公开并且认可的算法
    windgreen
        16
    windgreen  
       2019-04-19 17:33:05 +08:00
    唯一性,拿雪花来精简一下好了。
    1.如果没有分布式,机器 id 可以省掉了
    2.时间戳可以精简成从现在的一个时间开始,而不是从 1970 年开始,算下用几年,能省下几个字节
    3.自增部分看自己要求的每秒生成的最大数量,如果小的话也能省几个字节
    最后可以用 base58 而不用 hex,又可以省下几个字符
    muchengxue
        17
    muchengxue  
       2019-04-19 18:52:50 +08:00
    4ark
        18
    4ark  
       2019-04-20 09:59:20 +08:00 via Android
    session_create_id
    chouqiu
        19
    chouqiu  
       2019-05-05 11:11:44 +08:00
    uniqid() . mt_rand(1000000, 9999999)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2630 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 05:12 · PVG 13:12 · LAX 21:12 · JFK 00:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.