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

惊呆了,看到公司内部系统的生成数据库主键 ID 的代码以后,我的三观毁了。内附 Python 代码

  •  
  •   v2exblog · 2021-04-25 18:29:36 +08:00 · 13562 次点击
    这是一个创建于 1068 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    import random
    
    
    class IDGenerator:
        @classmethod
        def gen_primary_id(cls, length=11):
            prefix = random.choice('123456789')
            return prefix + ''.join([random.choice('0123456789')
                                     for i in range(length)])
    
    

    据说系统运行了几年,没碰见过碰撞的情况。我感觉我数学没学好,谁能告诉我这是哪一块的知识

    101 条回复    2021-04-28 11:11:56 +08:00
    1  2  
    GM
        1
    GM  
       2021-04-25 18:36:40 +08:00
    你什么意思?明天来我办公室一下!

    /狗头
    xupefei
        2
    xupefei  
       2021-04-25 18:37:39 +08:00 via iPhone
    高中数学,排列组合。
    thevita
        3
    thevita  
       2021-04-25 18:39:19 +08:00
    数据量多吗
    0DBBFF
        4
    0DBBFF  
       2021-04-25 18:40:56 +08:00
    我数学也不太好,是 9 * 10 的 11 次方吗? 这么多感觉碰撞几率是不是也不高啊
    guisheng
        5
    guisheng  
       2021-04-25 18:41:35 +08:00 via iPhone
    随机用到了极致
    snw
        6
    snw  
       2021-04-25 18:41:36 +08:00 via Android
    不怕碰撞吗?生成 10 万个 ID 大约有百万分之一概率发生碰撞。
    skyleft
        7
    skyleft  
       2021-04-25 18:46:26 +08:00
    性能很差,b+tree 节点大量页分裂
    sujin190
        8
    sujin190  
       2021-04-25 18:48:02 +08:00
    好歹加个日期啊,不会随着使用时间变长碰撞概率变高,所以楼主你想说的其实是你们司系统没啥人用的意思么,哈哈哈
    EPr2hh6LADQWqRVH
        9
    EPr2hh6LADQWqRVH  
       2021-04-25 18:48:48 +08:00
    主键都唯一的,重复数据库就抛错了,用户一重试随机到别的就过了,不是没碰撞过,是没引起注意。
    ch2
        10
    ch2  
       2021-04-25 18:49:26 +08:00   ❤️ 7
    但凡他知道有个包叫 uuid
    wellsc
        11
    wellsc  
       2021-04-25 18:50:52 +08:00 via iPhone
    简单实用,不需要任何第三方包就解决需求
    janxin
        12
    janxin  
       2021-04-25 18:53:29 +08:00   ❤️ 8
    没毛病,不要动

    赶紧提离职
    labulaka521
        13
    labulaka521  
       2021-04-25 18:58:11 +08:00
    能跑就行
    fucku
        14
    fucku  
       2021-04-25 19:03:07 +08:00
    又不是不能用
    Vegetable
        15
    Vegetable  
       2021-04-25 19:10:10 +08:00   ❤️ 3
    哪怕是这个功能,我也会使用 str(randint(10**length,10**(length+1)))
    mixz
        16
    mixz  
       2021-04-25 19:10:16 +08:00
    简单快速,赞
    gstqc
        17
    gstqc  
       2021-04-25 19:11:32 +08:00 via Android   ❤️ 2
    贵司还没倒闭说明还是有两把刷子的
    CallMeReznov
        18
    CallMeReznov  
       2021-04-25 19:12:54 +08:00   ❤️ 4
    imjamespond2020
        19
    imjamespond2020  
       2021-04-25 19:23:23 +08:00 via Android
    改成 ubixtimestamp-uuid
    ksedz
        20
    ksedz  
       2021-04-25 19:33:17 +08:00
    @Vegetable randrange
    Jooooooooo
        21
    Jooooooooo  
       2021-04-25 19:34:49 +08:00
    可以用抛唯一键冲突重试的方法解决
    sillydaddy
        22
    sillydaddy  
       2021-04-25 19:47:44 +08:00 via Android
    @snw #6
    概率应该比百万分之一高多了。对应的概率计算参考“生日问题”:

    https://zh.m.wikipedia.org/wiki/%E7%94%9F%E6%97%A5%E5%95%8F%E9%A1%8C

    我估计生成 10 万个 id 发生碰撞的概率应该大于千分之一。
    jeffwcx
        23
    jeffwcx  
       2021-04-25 20:43:24 +08:00
    哈哈,这还不如 uuid 呢,如果需要 id 的顺序性可以用 snowflake
    snw
        24
    snw  
       2021-04-25 20:51:39 +08:00
    @sillydaddy
    我刚才公式拉错了。用 excel 又模拟了一遍,10 万个 id 碰撞概率大约 4.877%。
    mind3x
        25
    mind3x  
       2021-04-25 21:02:59 +08:00 via Android
    @sillydaddy 根据近似公式 1-1/exp(n^2/(2N)),n=10 万,N=900000000000,概率大于千分之 5 了。
    v2exblog
        26
    v2exblog  
    OP
       2021-04-25 21:05:03 +08:00
    @mind3x 太秀了,请问怎么学这个公式,哪本书上有
    mind3x
        27
    mind3x  
       2021-04-25 21:06:42 +08:00 via Android
    @v2exblog 就在 @sillydaddy 贴过的 Wikipedia 连接里
    pkupyx
        28
    pkupyx  
       2021-04-25 21:28:29 +08:00
    也许数据库上有唯一索引重复就回滚吧
    jdandelion573
        29
    jdandelion573  
       2021-04-25 22:09:40 +08:00 via Android
    说明数据库没人用
    ila
        30
    ila  
       2021-04-25 22:13:32 +08:00 via Android
    要看其它代码
    ytmsdy
        31
    ytmsdy  
       2021-04-25 22:47:36 +08:00   ❤️ 6
    冲突肯定是发生过的,只不过无法复现罢了!
    lightingtime
        32
    lightingtime  
       2021-04-26 00:07:31 +08:00
    判断一下使用场景啊,qps 如果只有几,那么碰撞了就重新生成 a
    george404
        33
    george404  
       2021-04-26 08:01:09 +08:00
    不得不佩服,大道至简,哈哈!!
    auh
        34
    auh  
       2021-04-26 08:12:32 +08:00
    这一看就是组长干的事情。往往小兵在没有征得老大同意的情况下,是不敢这样操作的。
    wunsch0106
        35
    wunsch0106  
       2021-04-26 08:48:31 +08:00
    我甚至觉得这是个段子
    jetyang
        36
    jetyang  
       2021-04-26 08:50:29 +08:00   ❤️ 1
    重复了会插入报错,报错就再生成一个,大家不都是这么做的吗?抠鼻
    LudwigWS
        37
    LudwigWS  
       2021-04-26 08:55:45 +08:00 via iPhone
    @snw excel 自己连 uuid 都没有,编程太不友好了。vba 很垃圾……
    securityCoding
        38
    securityCoding  
       2021-04-26 08:57:18 +08:00 via Android
    233 还不如直接时间戳呢
    dangyuluo
        39
    dangyuluo  
       2021-04-26 08:58:52 +08:00
    虽然代码很烂,但是公布自己就职公司的代码(勉强算得上是算法)好么?
    soulzz
        40
    soulzz  
       2021-04-26 09:00:16 +08:00
    赶紧跑路
    weizhen199
        41
    weizhen199  
       2021-04-26 09:02:45 +08:00
    @skyleft 你这么一说我突然好奇了。很多时候都会用 guid 来做主键。这样效率会很低?
    HashV2
        42
    HashV2  
       2021-04-26 09:06:15 +08:00
    return str(random.randint(10**10, 10**11-1))
    HashV2
        43
    HashV2  
       2021-04-26 09:06:41 +08:00
    快跑吧
    jinhan13789991
        44
    jinhan13789991  
       2021-04-26 09:19:42 +08:00
    失败了再重试啊,总会成功的。而且及时发现了问题,也很难复现。
    有可能这个项目是压缩了开发周期,当初做他的程序员为了尽早完成。
    Leviathann
        45
    Leviathann  
       2021-04-26 09:22:14 +08:00 via iPhone
    为什么就是不想用自增 id
    Felldeadbird
        46
    Felldeadbird  
       2021-04-26 09:24:41 +08:00
    没说数据量啊。到百万或者千万 应该会出现过碰撞啊。
    Rache1
        47
    Rache1  
       2021-04-26 09:26:15 +08:00
    python 这个 for 语法糖,真酸爽
    zjsxwc
        48
    zjsxwc  
       2021-04-26 09:29:06 +08:00
    id 冲突了就 rollback 吗

    2333
    MinQ
        49
    MinQ  
       2021-04-26 09:30:42 +08:00
    @ch2 uuid 太长了不适合做主键吧,我记得会导致 MySQL 性能下降
    MinQ
        50
    MinQ  
       2021-04-26 09:36:26 +08:00
    我自己的方案是 snowflake 的 timestamp 设定一个 19 年的时间,然后往前位移 N 位缩短长度,最后转 Base32 截取,这样只需要 1X 位字母数字就能拿来做主键了
    要是觉得 Base32 看着不爽可以对着码表自己做个转换
    tabris17
        51
    tabris17  
       2021-04-26 09:44:13 +08:00
    说明贵司业务量不大
    tankren
        52
    tankren  
       2021-04-26 09:53:33 +08:00
    uuid 有现成的吧
    ganbuliao
        53
    ganbuliao  
       2021-04-26 09:56:41 +08:00   ❤️ 1
    你们有日志吗?我觉得是碰撞到了了你们也不知道。就是插入数据报错,客户重试一下就好了还以为是网络抖动 、、):dog
    domodomo
        54
    domodomo  
       2021-04-26 10:03:43 +08:00
    说明你们公司挺小的……
    PDdavon
        55
    PDdavon  
       2021-04-26 10:20:55 +08:00
    @Vegetable #15 楼主公司的函数跟你这一行应该是等价的。。。。
    echowuhao
        56
    echowuhao  
       2021-04-26 10:23:57 +08:00
    如果每个表不大,看不出什么问题。

    拿出来讨论的都是大公司的问题,其实大部分是小公司,没有那么多数据,如果在分库分表,那就更少了。
    37Y37
        57
    37Y37  
       2021-04-26 10:36:15 +08:00
    简直神了,我都没想到这方法
    msg7086
        58
    msg7086  
       2021-04-26 10:42:35 +08:00
    随机生成主键嘛,除了代码烂一点以外没啥问题。
    我如果需要随机主键我也会这样生成(当然不会写成这屎样就是了)。
    碰撞是小问题,重试就行。
    Cy1
        59
    Cy1  
       2021-04-26 10:43:43 +08:00
    @weizhen199 guid 相比自增 ID 效率上肯定会低,但很低也不至于。既然选择了 guid 了,就别幻想有自增 ID 一样的性能了,底层的数据结构本身就决定了这个时候
    lichdkimba
        60
    lichdkimba  
       2021-04-26 10:49:38 +08:00
    用户够少就不会有任何问题!
    preach
        61
    preach  
       2021-04-26 11:00:36 +08:00
    握草 牛比 我居然还 copy 试了试
    Vegetable
        62
    Vegetable  
       2021-04-26 11:11:49 +08:00
    @PDdavon 这不是就是吐槽设计的差代码也差么...
    Vegetable
        63
    Vegetable  
       2021-04-26 11:14:47 +08:00
    其实我也用过差不多的逻辑,只要加上日期和简单的重试,的确是能用。

    https://imgur.com/a/qNx1UU4
    joyhub2140
        64
    joyhub2140  
       2021-04-26 11:37:10 +08:00
    加点时间戳啊还是能用的,但扛不住高并发,不过这个系统几年下来都没碰撞,应该没啥人用吧
    tairan2006
        65
    tairan2006  
       2021-04-26 11:39:01 +08:00
    @MinQ mysql8 已经有 uuid 和 binary 互转了
    Huelse
        66
    Huelse  
       2021-04-26 11:40:56 +08:00
    random 模块产生碰撞的概率还是很低的,其本身就能规避一段时间问题

    希望有大佬能通过数学方式考证下,楼上有几位的公式感觉少考虑了一些影响因素
    jsuly
        67
    jsuly  
       2021-04-26 11:48:38 +08:00
    我很好奇, 为什么要自己生成主键呀,不是都是主键,自增吗
    Latin
        68
    Latin  
       2021-04-26 11:50:47 +08:00
    人家离职的原因就是不想堆屎山了吧
    MinQ
        69
    MinQ  
       2021-04-26 11:57:02 +08:00
    @tairan2006 看到了,那个还是挺方便的,虽然公司还在用 5.7
    wsseo
        70
    wsseo  
       2021-04-26 12:37:27 +08:00
    楼主跑路把
    pepesii
        71
    pepesii  
       2021-04-26 12:37:57 +08:00
    你这就没明白了把,哈哈,这是明年的优化点,不然哪儿来的 KPI
    hahastudio
        72
    hahastudio  
       2021-04-26 13:12:34 +08:00
    说起来,这么写和直接随机数字的区别是什么?
    怎么直觉上这种会更容易被碰撞
    fareware
        73
    fareware  
       2021-04-26 13:36:33 +08:00
    这个设计的问题有两个
    1. 碰撞。这个还可以通过改代码来立即解决
    2. 数据库。使用 b+ 做索引的关系型数据库的性能问题,这个最严重,后期想优化更是难受。
    sss495088732
        74
    sss495088732  
       2021-04-26 13:40:26 +08:00
    0.0 说实话这种生成唯一主键的是不是大家都用雪花算法....还没有遇到过性能问题
    MinQ
        75
    MinQ  
       2021-04-26 14:10:12 +08:00
    @sss495088732 主要是好调好写,可以自己定制,最多依赖一个 zookeeper 或者 redis
    Cloutain
        76
    Cloutain  
       2021-04-26 14:12:16 +08:00
    无序主键对于数据库是个考验 哈哈哈
    oolovexx
        77
    oolovexx  
       2021-04-26 14:15:58 +08:00
    随机,可以的,牛批
    3dwelcome
        78
    3dwelcome  
       2021-04-26 14:16:17 +08:00
    相同的主键没办法写入两次,数据库没冲突,是正常的。
    楼主的代码应该只贴出了一小半,如果冲突,大概率会重新生成一次。这样速度也不慢。
    JokeEnd
        79
    JokeEnd  
       2021-04-26 14:37:01 +08:00
    用雪花算法
    TimPeake
        80
    TimPeake  
       2021-04-26 14:38:47 +08:00
    我一个前端都知道弄个时间戳还能一定程度防止重复。。。。。
    KouShuiYu
        81
    KouShuiYu  
       2021-04-26 15:35:29 +08:00   ❤️ 1
    说的应该就是我🐶

    ```js
    function getRandomStr(len) {
    const dictionary = '0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()_+~:;,./<>?';
    const dictionaryLen = dictionary.length;
    let str = '';
    for (let i = 0; i < len; i += 1) {
    str += dictionary[Math.floor(getRandom(dictionaryLen))];
    }
    return str;
    }
    ```
    xiaochong0302
        82
    xiaochong0302  
       2021-04-26 15:48:45 +08:00   ❤️ 1
    我懒鬼订单号:$this->sn = date('YmdHis') . rand(1000, 9999);

    没有大量用户高并发,这样又不是不能用,大不了后面再加一段随机数。
    nanjoyoshino
        83
    nanjoyoshino  
       2021-04-26 17:05:13 +08:00
    这个可真的是太强了,点进来之前我完全没想到是这种方法
    watzds
        84
    watzds  
       2021-04-26 17:06:03 +08:00
    怎么把核心代码公布出来了! 😄
    Wincer
        85
    Wincer  
       2021-04-26 17:25:00 +08:00
    第二天楼主因为公开核心代码被开除了
    roudancongji
        86
    roudancongji  
       2021-04-26 18:04:45 +08:00
    估计碰撞了就加个 retry,再来一遍
    johnsona
        87
    johnsona  
       2021-04-26 20:05:40 +08:00 via iPhone
    @weizhen199 不然别人发明各种分布式 id 干嘛
    johnsona
        88
    johnsona  
       2021-04-26 20:06:12 +08:00 via iPhone
    @roudancongji 好家伙 哈希碰撞策略被你学明白了
    l1ve
        89
    l1ve  
       2021-04-26 21:03:23 +08:00
    if repeat {
    retry()
    }
    print("get random successful")
    luwill
        90
    luwill  
       2021-04-26 22:06:00 +08:00
    uuid-4 变种
    opengps
        91
    opengps  
       2021-04-26 22:24:41 +08:00
    别说的一无是处,这种自造逻辑有个天然优势就是自带防猜解特点
    cabing
        92
    cabing  
       2021-04-26 22:37:16 +08:00
    插入失败 retry 就行。设计的有点东西,一般人绝对想不到如此有趣的设计。

    内网系统估计也没多少条数据。
    hanxiV2EX
        93
    hanxiV2EX  
       2021-04-26 22:38:47 +08:00 via Android
    如果这个是用来生成用户 id,且用户量不大,比如内部的后台系统,那没啥大毛病,注册失败再重试一下,哈哈哈哈。
    cogitoxin
        94
    cogitoxin  
       2021-04-27 07:51:35 +08:00 via iPhone
    叹为观止
    ccppgo
        95
    ccppgo  
       2021-04-27 09:18:56 +08:00
    @xiaochong0302 哈哈哈, 只要没有分库分表, 直接弄个唯一索引, 报错就重试
    wangyzj
        96
    wangyzj  
       2021-04-27 09:54:11 +08:00
    多大数量啊?
    echoZero
        97
    echoZero  
       2021-04-27 09:56:34 +08:00
    跑就对了,不然你就会知道 mysql 主键不是自增时插入效率到底有多低。DBA 天天追着你进行优化
    fanyingmao
        98
    fanyingmao  
       2021-04-27 20:26:42 +08:00
    又不是不能用,反正项目赚不赚钱和代码写得好坏关系不大。
    linyinma
        99
    linyinma  
       2021-04-28 09:29:34 +08:00
    抛开业务谈代码都是耍流氓,存在必有其合理性
    zhuweiyou
        100
    zhuweiyou  
       2021-04-28 10:15:17 +08:00
    公司内部系统, 一共就老板和测试两个人会访问, 没毛病
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1307 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 23:31 · PVG 07:31 · LAX 16:31 · JFK 19:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.