kaiki
V2EX  ›  问与答

有没有什么生成随机不重复的唯一 ID 且足够短的好办法?

  •  
  •   kaiki · Oct 16, 2020 · 11839 views
    This topic created in 2213 days ago, the information mentioned may be changed or developed.
    B 站的 bv 号那种就感觉可以,但是太长,我想生成只有几位的数字+大小写字母组合的随机字符表作为 ID,但是怕万一随机出以前出过的 ID
    67 replies  •  2020-10-18 02:15:31 +08:00
    lxk11153
    lxk11153
    Oct 16, 2020❤️1
    1
    搞一个 mysql 拿来自增 ID 不就好了
    opengps
    opengps
    Oct 16, 2020 via Android
    2
    足够短跟容量是有直接制约的。首先得估算出最小的容量,然后研究下组成,然后计算出最小多长。

    我当初设计过一款唯一访问 key,思路你参考下:
    key 的参与构造字符,我用的是数字 10,大写字母 26,小写字母 26,虽然还可以用一部分单位符号但我没选,这样我的 key 只要 8 位字符就是 62 的 8 次方,就远远超过了 imei 的 15 纯数字位组合,而且还留下了巨大的空间,至于去重,我是每次随机生成完都检查一下是否已存在实现的,如果你对查重有性能要求的话需要重点改造下这个地方
    lpts007
    lpts007
    Oct 16, 2020
    3
    @opengps 62^8 < 10^15
    ziseyinzi
    ziseyinzi
    Oct 16, 2020 via Android
    4
    kaiki
    kaiki
    OP
    Oct 16, 2020
    5
    @lxk11153 主要是想无规律,不可逆,如果位数都能保持一致就最好了
    meinjoy
    meinjoy
    Oct 16, 2020 via iPhone
    6
    我得方法是截取 uuid,随机一个起始位置比如 2,想要 5 位的 id,就 uuid.substring(2,7)就行了
    kaiki
    kaiki
    OP
    Oct 16, 2020
    7
    @meinjoy 和随机生成没什么区别,我打算随机生成几位数字大小写字母然后写入一个表中,绑定一个对应的数字 ID,查询的时候随机 ID 返回数字 ID,写入的时候查询随机 ID 是否存在,存在就重来一次生成过程。
    这样随机的 ID 就和数字 ID 没有任何规律可循了。
    594duck
    594duck
    Oct 16, 2020 via iPhone
    8
    数据足够多的时候又要来问怎么查得快
    black11black
    black11black
    Oct 16, 2020 via Android
    10
    我倒是很好奇,用 hash 确实是保证基本不重复了,冲撞了的情况该怎么办呢?
    Perry
    Perry
    Oct 16, 2020 via iPhone
    11
    @black11black 先了解冲撞的概率有多少再谈需要不需要考虑这种情况
    lazydog
    lazydog
    Oct 16, 2020 via Android
    12
    github 上应该一搜一大把吧,你可以搜搜看 unique id
    black11black
    black11black
    Oct 16, 2020 via Android
    13
    @Perry 很有趣的回答,难道系统的设计者可以这么思考问题:因为冲撞是极小概率发生事件,所以不需要考虑?
    Cooky
    Cooky
    Oct 16, 2020 via Android
    14
    预先生成检测重复,用的时候再取
    z7356995
    z7356995
    Oct 16, 2020 via Android
    15
    数字 id 后端加密加盐一下,使用前再解密,应该比在表中查快一点
    opengps
    opengps
    Oct 16, 2020 via Android
    16
    @lpts007 嗯,imei 用途中,有些位数不是数学里的所有数值都用的上,我是从实用角度足够用了,说的不严谨
    avenger
    avenger
    Oct 16, 2020 via iPhone
    17
    Snowflake 了解一下
    AndyAO
    AndyAO
    Oct 16, 2020
    18
    让我想起来了微软的全局标识符 GUID
    iConnect
    iConnect
    Oct 16, 2020 via Android❤️2
    19
    要短,要快,还要随机,满足三个条件,最简单就是随机预生成放号码池。
    Perry
    Perry
    Oct 16, 2020 via iPhone
    20
    @black11black 这不是很正常的一个 tradeoff ? GUID 也有极小概率能被碰撞,依据你的逻辑一个普通应用还要去做好这样的碰撞情况?
    0bit
    0bit
    Oct 16, 2020
    21
    用 uuid v4 生成,然后转成 short uuid
    yuzo555
    yuzo555
    Oct 16, 2020
    22
    楼主要求短,那么 hash 算法肯定重复概率高。
    又想要短,又想要无规律,只能用楼主自己在 #7 提出来的方案了。
    black11black
    black11black
    Oct 16, 2020 via Android
    23
    @Perry 服了,大神当然都是不处理边际情况的,不用回了 block 了
    QingXuJiaZhi
    QingXuJiaZhi
    Oct 16, 2020
    24
    第一考虑总量。另一个思路是考虑频率,比如,如果生成 ID 的间隔必然超过一秒,那用 timestamp 转 64 进制,再加个两位随机字符就可以了。
    QingXuJiaZhi
    QingXuJiaZhi
    Oct 16, 2020
    25
    @black11black 这个 block 冤枉了,忽略小概率情况是很常见的做法,小规模系统连密码库的 nonce (要求唯一性) 都可以采用随机数。
    Perry
    Perry
    Oct 16, 2020
    26
    @black11black 使用正确的情况下,大部分 hash 或者 GUID 的碰撞概率都是 astronomically low 的,如果你觉得这算是边际情况的话那你可以去处理。听到与自己观点不一样的声音就 block 对方这个行为有点巨婴了。
    black11black
    black11black
    Oct 16, 2020 via Android
    27
    @cmdOptionKana 问题是你不做,真撞了怎么办呢,比如现在有个坏东西,故意给你传两份 md5 一样的 hexstring
    workg
    workg
    Oct 16, 2020
    28
    Date.now().toString(36) + Math.random().toString(36).slice(2) ?
    QingXuJiaZhi
    QingXuJiaZhi
    Oct 16, 2020
    29
    @workg 看情况,Date.now()精确到毫秒,如果一秒内并发生成很少 ID,则可以除以 1000 精确到秒,这样 ID 可以短些。另外 toString 最高只能转为 36 进制,如果用其他方法转为 64 进制,还可以再缩短 ID 长度。
    Maboroshii
    Maboroshii
    Oct 16, 2020
    30
    snowflake 转 base32
    imdong
    imdong
    Oct 16, 2020
    31
    先给楼主分享自己有过的两个类似的经验吧:

    15 年,公司接了一个电商买公交乘车码的东西,乘客手机购买后生成数字序列号给司机验证后可乘车。

    由于需要手输,所以数字要短,当时做过一些研究,楼主可以参考一下结论(一定量情况下,直接随机数和瞎搞胡搞的看起来随机的没差):

    ![image.png]( https://i.loli.net/2020/10/16/7wYaKPWrDjBNgkt.png)

    ![image.png]( https://i.loli.net/2020/10/16/qFEaijgyPBhs7rD.png)

    然后附上我前段时间搞得一个离线 CDK,里面又一些思路楼主可以参考:

    顺序生成,但是通过在二进制上一定的变换,可以得到看似随机的结果,但和原值一一对应,不重复。

    https://www.qs5.org/Post/680.html

    ( 15 年时我还很菜,别喷,虽然我现在依然菜,但谁喷我 ,我就画圈圈骂谁。)
    mcluyu
    mcluyu
    Oct 16, 2020
    32
    uuidgen
    threebr
    threebr
    Oct 16, 2020 via Android
    33
    感觉就是找一个有序数列到同样长度无序数列能够一一对应的函数,不行就像 19l 说的,预生成这样的对应列表
    newtype0092
    newtype0092
    Oct 16, 2020❤️3
    34
    @black11black 你的每段代码都处理了因为 cpu 二极管电位异常导致计算结果不正确的异常了么?
    没有的话请 block 我,这种边际情况都不处理你不配和我们讨论~
    manhere
    manhere
    Oct 16, 2020 via Android
    35
    hashids
    shuqin2333
    shuqin2333
    Oct 16, 2020
    36
    short uuid
    gamexg
    gamexg
    Oct 16, 2020
    37
    https://hashids.org/
    https://github.com/pjebs/optimus-go

    都是直接对 id 进行可逆加密的办法。
    另外也可以考虑直接用 aes 等方式加密。
    QingXuJiaZhi
    QingXuJiaZhi
    Oct 16, 2020
    38
    @imdong 随机唯一码,如果不是事先生成,一般思路都是 时间+设备码+随机数,传统的 UUID 与现在流行的 snowflake 都是这个思路。如果不加入一些环境常数(比如随时间而变化、随设备而变化、随地点变化),那肯定不管怎么折腾都只是一个随机数(随机数+随机数=随机数)。
    cheng6563
    cheng6563
    Oct 16, 2020 via Android
    39
    全随机是真的会重的,一般要带上时间,但这样就长了。可以预先随机一些 ID 放池子里,用一个取一个,只是这样比较慢
    imdong
    imdong
    Oct 16, 2020
    40
    @cmdOptionKana 这个不是真正的随机,是可以用于数据库自增字段,然后对自增字段进行一定的处理,使其变得看起来像是随机数一样(结果定长)。

    而且连续两个数之间可能看不出规律(不知道处理方式的情况下)。
    a570295535
    a570295535
    Oct 16, 2020 via Android
    41
    就用楼上那些方式,
    1 、用代码生成 100 万个 id 或更多
    2 、去重复并放进数据库
    3 、用户来一个取一个
    完成!
    随取随用,只有一开始你自己麻烦一次,这样不仅短还不会重复!
    unixeno
    unixeno
    Oct 16, 2020 via Android
    42
    @black11black md5 这种已经能有快速碰撞算法的 hash 讨论这个就没意义了,你要用 sha256 还能撞吗?你要是现在能找到碰撞的 sha256,第一件事儿应该是去发个论文
    单论生日攻击,sha256 你要达到 10^-15 的碰撞概率需要 1.5*10^31 个数据,这个概率啥概念?大概是下一秒小行星撞地球然后人类灭绝的概率
    至于 10^31 啥概念?现在人类所有数码化保存的信息总量也才不到 10^22
    与其担心极小概率的碰撞,你不如先担心一下高能粒子打到你 CPU 上造成 bit 反转让你 hash 算错了
    kaiki
    kaiki
    OP
    Oct 16, 2020
    43
    重复问题上,再次随机就行了,大家没必要那么严肃。
    那么是实时查写号码池,还是提前准备号码池呢?
    imn1
    imn1
    Oct 16, 2020
    44
    如果你所说的场景,不必随机,只要时间序列做一个最小转换就行了,只要外部不知道转换算法,看上去就是随机的
    网站或者业务的一些 id,往往还包含了轻量的分类信息,这个就不是单纯随机就能完成的需求了
    popoer
    popoer
    Oct 16, 2020
    45
    snowflake 不香吗
    wyz123723
    wyz123723
    Oct 16, 2020
    46
    @black11black 你这问题就和有人真破解了 sha256 了怎么办
    wyz123723
    wyz123723
    Oct 16, 2020❤️1
    47
    hash 碰撞的概率比一坨量子突然汇聚成一个特别喜欢你的美女的概率还要小
    lavvrence
    lavvrence
    Oct 16, 2020
    48
    redis increment
    xuewuchen
    xuewuchen
    Oct 16, 2020
    49
    GUID 吧。。
    killergun
    killergun
    Oct 16, 2020
    50
    感觉你这个需求不就是短地址生成
    imhui
    imhui
    Oct 16, 2020
    51
    如果是单机服务,线型同余 或许可以试一下
    azoon
    azoon
    Oct 16, 2020
    52
    楼主要求不高,这么随便的话。就每次生成随机字符串时判断数据库是否存,存在就再次生成,就好了吧。
    我之前给别人做了个拍照预约系统的订单号就这么搞。
    xuanbg
    xuanbg
    Oct 16, 2020
    53
    @imdong 我搞了个编码生成器,就是顺序生成,但是通过查表变换,可以得到看似随机的结果,但和原值一一对应,不重复。

    代码很简单:
    for(int i = len - 1; isEncrypt && i > 0; code = garble(code)) {
    --i;
    }
    private static String garble(String str) {
    int len = str.length();
    String first = len > 2 ? str.substring(0, 1) : "";
    String high = len > 3 ? str.substring(1, len - 2) : "";
    String low = String.valueOf(set.get(str.substring(len - 2, len)));
    return high + low + first;
    }
    Jooooooooo
    Jooooooooo
    Oct 16, 2020
    54
    了解一下鸽笼原理
    tamer
    tamer
    Oct 16, 2020
    55
    @wyz123723 那么请问有没有办法能加快这样的汇聚呢,很急,在线等
    xuanbg
    xuanbg
    Oct 16, 2020
    56
    @xuanbg set 就是 00-99 的密码表,通过查表可以得到一个对应的预生成的随机值。加密后的数字理论上必需得到密码本、交换方法和交换次数才能还原加密前的数字,三者缺一不可。
    tabris17
    tabris17
    Oct 16, 2020
    57
    hashids
    iyangyuan
    iyangyuan
    Oct 16, 2020
    58
    预生成,最简单最可靠。
    假设长度 6 位,每一位可选[a-z][0-9],那么就是 20 亿的容量(不够用增加长度即可)。
    每次预生成,不能太多,也不能太少,那么我们可以控制前 2 位递增(00,01...zy,zz),每次生成固定前 2 位,后 4 位全量,这样就是百万级别的容量,应该够用一段时间,快消耗完时依此类推。
    这样就能满足生成快、读取快,不重复,足够短。
    TomVista
    TomVista
    Oct 16, 2020
    59
    36 进制 id,每次随机一个数字进行随机递增,产生随机 id.
    TomVista
    TomVista
    Oct 16, 2020
    60
    @TomVista 带大小写的话,就是 62 进制,8 位就是 2 百万亿,够用到天荒地老了,随机范围 1000 的话,还在 2 千亿级别.
    JCZ2MkKb5S8ZX9pq
    JCZ2MkKb5S8ZX9pq
    Oct 16, 2020
    61
    看了楼上的思路,忽然想到固定排列组合+伪随机,自定义个种子,是不是也可以?
    xcatliu
    xcatliu
    Oct 16, 2020❤️1
    62
    最近发现的一个 star 很多的 https://github.com/ai/nanoid,看了下功能很全,比如可以自定字典和长度:

    ```js
    import { customAlphabet } from 'nanoid'
    const nanoid = customAlphabet('1234567890abcdef', 10)
    model.id = nanoid() //=> "4f90d13a42"
    ```
    phithon
    phithon
    Oct 16, 2020
    63
    看到楼上有提到 hashids 的了
    https://hashids.org/
    black11black
    black11black
    Oct 17, 2020
    65
    @wyz123723
    @unixeno
    @newtype0092
    不是概率小不小的问题,我觉得你作为设计者,当然不可能允许设计范围内存在一种小概率情况让程序不知道怎么处理,设计外情况当然不算在内,电位异常不是你能控制的,但你能控制的范围内你肯定要处理,所以我就问碰撞了怎么处理,楼上有人也说过了碰撞了就重随机这种简单答案,或者有人说用 sha256 可以基本避免恶意设计碰撞,这都是解决方案,你有方案就贡献方案,没方案就不回,没什么必要杠来杠去的。

    再说你真设计一个系统,你当然要考虑设计安全性,像我说的你如果完全没有碰撞处理方案,那随便一个人稍微有点恶意,传了经过设计的特殊字串,就能让你的程序抛出异常,这是完全有可能发生的情况而不是什么极小概率事件,显然设计过系统的都没法接受。也不知道现在杠精这么多都在杠什么,如果你们写程序的时候就真不加碰撞处理的代码,那我们也不用讨论什么了,道不同不相为谋,没必要
    Deepseafish
    Deepseafish
    Oct 17, 2020 via Android
    66
    @black11black 「或者有人说用 sha256 可以基本避免恶意设计碰撞,这都是解决方案」
    这不按照你的逻辑还是没有考虑碰撞的情况吗?这不等于还是在说碰撞概率低,怎么就又是解决方案了。
    black11black
    black11black
    Oct 18, 2020
    67
    @Deepseafish 尝试跟杠精讨论是我的错,block 了不用回了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1356 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 109ms · UTC 17:11 · PVG 01:11 · LAX 09:11 · JFK 12:11
    ♥ Do have faith in what you're doing.