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

数据库表中唯一主键 id 用 32 位的 md5 算出来的值是否可行?

  •  1
     
  •   kerb15 · 2020-05-05 15:12:58 +08:00 · 5883 次点击
    这是一个创建于 1670 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如:

    用户表中,将用户邮箱+密码的值去做 MD5,得出来的值作为 userId

    订单表中,将订单数据+时间戳的值去做 MD5,得出来的值作为订单 Id

    主要的疑问是,32 位的 id 是否过于占用空间?

    以及其他缺点希望各位 V 友能发表下看法。

    66 条回复    2020-05-06 15:06:21 +08:00
    Takamine
        1
    Takamine  
       2020-05-05 15:16:24 +08:00 via Android
    ……这样做的意义是什么,强行想让主键 id 和业务含义关联起来吗。
    ajaxfunction
        2
    ajaxfunction  
       2020-05-05 15:18:04 +08:00
    要是用户密码改了呢? 之前生成的 userid 和现在的 userid 不一样,系统不就乱了吗?
    简单的问题复杂化了啊!
    creedowl
        3
    creedowl  
       2020-05-05 15:18:32 +08:00 via Android
    要是用户改密码怎么办,如果是想要隐藏真实 id 可以用 hashid
    订单可以用雪花算法
    hbolive
        4
    hbolive  
       2020-05-05 15:18:45 +08:00
    @Takamine 这样的 id 看起来很酷。。
    nvkou
        5
    nvkou  
       2020-05-05 15:21:27 +08:00 via Android
    uuid 也是这么做的。只不过人家用一去不回头的时间做种子之一。
    没有必要重复发明轮子
    kerb15
        6
    kerb15  
    OP
       2020-05-05 15:21:51 +08:00
    @Takamine
    @ajaxfunction
    @creedowl
    只是想简单的生成一个唯一 id 而已,用户改密码没什么影响,id 还是那个 id
    luopengfei14
        7
    luopengfei14  
       2020-05-05 15:23:18 +08:00
    用户一旦修改邮箱、密码,你就找不回来 userId,而且 md5 生成的 userId 对索引不友好。
    简单点直接用自增 id 当作 userId,考虑多的可以用雪花算法生成 userId,一般够用。

    还有 md5 、uuid 字符串理论上避免不了重复的可能
    yukiloh
        8
    yukiloh  
       2020-05-05 15:23:30 +08:00
    用户登陆用账号行不,手机行不,你要做 3 个 userid 吗
    kerb15
        9
    kerb15  
    OP
       2020-05-05 15:24:56 +08:00
    @nvkou 倒也没有说想重复造轮子,不过确实没有想过直接调用已有的算法去生成,是个不错的方式
    chendy
        10
    chendy  
       2020-05-05 15:26:18 +08:00
    负载不大自增就够用了
    至于 “32 位过于占用空间?”…emm ???
    kerb15
        11
    kerb15  
    OP
       2020-05-05 15:26:49 +08:00
    好吧,我的表达存在歧义,其实这个 userid 并不作为对外公开的值,也不提供给用户登录使用,只是内部对用户的一个标识,可以理解为就是用户表的主键 id
    murmur
        12
    murmur  
       2020-05-05 15:28:12 +08:00
    uuid 也比 md5 好啊,你的用户量多少,少的话可以,多的话得考虑下防装,你估计是看了 mongo 那个主键,别人是有算法的,可以保证在数据结构上的一些优化
    murmur
        13
    murmur  
       2020-05-05 15:29:13 +08:00
    @Takamine 有些业务需要随机化主键,否则破解你一个接口 id 直接递增数据就爬一遍了
    kerb15
        14
    kerb15  
    OP
       2020-05-05 15:31:27 +08:00
    @murmur 想请教一下防装是什么意思
    linvon
        15
    linvon  
       2020-05-05 15:32:29 +08:00
    @kerb15 防撞吧
    changePro
        16
    changePro  
       2020-05-05 15:34:09 +08:00 via Android
    “md5 生成的 userId 对索引不友好” 少误导其他人
    wangyzj
        17
    wangyzj  
       2020-05-05 15:36:17 +08:00
    不是不行, 百害无一利
    changePro
        18
    changePro  
       2020-05-05 15:37:34 +08:00 via Android
    首先,为什么要这样做?主键 ID 几乎都是 fixed length,磁盘内存、又便宜,索引不是问题。md5 也可以看作是 UUID,用 UUID 做主键大部分是为了解决分布式的一些问题,题主是为了折腾?
    Jacky23333
        19
    Jacky23333  
       2020-05-05 15:39:59 +08:00 via Android   ❤️ 3
    你这样做会严重影响插入性能,因为会频繁导致页分裂跟行移动,产生空间碎片
    Jacky23333
        20
    Jacky23333  
       2020-05-05 15:41:26 +08:00 via Android   ❤️ 1
    @changePro 我觉得没毛病啊
    msg7086
        21
    msg7086  
       2020-05-05 15:52:31 +08:00 via Android
    主键不与业务相关。你看看你的想法是否违反了这条惯例。
    xuanbg
        22
    xuanbg  
       2020-05-05 15:55:41 +08:00
    占空间倒没什么,现在存储这么便宜……

    问题是:
    1 、效率低,算一个哈希值也是有开销的。
    2 、建立索引效率受影响。

    然后,直接用 uuid 他不香吗?
    changePro
        23
    changePro  
       2020-05-05 16:32:24 +08:00
    @Jacky23333 会导致性能下降,如果缓存不命中,导致大量磁盘 IO 。因为相同的数据,page 更多了。buffer pool 设计好的话的话,其实也还好
    PopRain
        24
    PopRain  
       2020-05-05 16:34:06 +08:00
    如果不想用 uuid, 推荐 Twitter 雪花(SnowFlake)算法,一个 64 位整数
    l3n641
        25
    l3n641  
       2020-05-05 16:53:23 +08:00
    不建议这样做,主键越长 索引也占用空间.如果数据量大的话,添加和更新数据会很大影响性能.如果是 mysql 的 inodb 的话.其他的普通索引都会包含主键键值.这时候会更加占用空间.
    saulshao
        26
    saulshao  
       2020-05-05 17:06:15 +08:00
    通常情况下,主键字段就选择单纯的整数 /大整数 /无符号整数就行。
    凡是需要某种业务相关计算来"产生"主键值的动作,都是花费很高并且没什么收益的方案。
    yinzhili
        27
    yinzhili  
       2020-05-05 17:11:28 +08:00
    不建议。这样的订单号过长,且可读性几乎等于 0 。
    PHPer233
        28
    PHPer233  
       2020-05-05 17:33:08 +08:00 via Android
    一个简单的 id 让你搞得这么复杂
    yjxjn
        29
    yjxjn  
       2020-05-05 17:38:25 +08:00
    雪花算法不香么?
    yjxjn
        30
    yjxjn  
       2020-05-05 17:39:42 +08:00
    @kerb15 防止撞库。说人话的意思就是防止让人家穷举猜到了
    joooooker21
        31
    joooooker21  
       2020-05-05 18:09:55 +08:00
    单体应用可以用自增主键
    分布式应用可以用雪花算法或 uuid

    你说的这种方法没有意义且消耗资源
    xiangyuecn
        32
    xiangyuecn  
       2020-05-05 18:16:39 +08:00
    设计 ID 结构最核心的一点,我觉得应当是时间粗略有序,uuid 、hash 都达不到时间粗略有序,参考楼上的雪花算法

    你希望你的 id 排序之后是毫无规律的随机排序(似乎会影响插入性能), 还是按照插入顺序先后排序(也就是插入时间),普通自增 id 天然有序。id 里面用时间因子作为前缀妥妥的,后面随便拼上 uuid 、md5 之类的,就基本上时间粗略有序了
    silvernoo
        33
    silvernoo  
       2020-05-05 19:27:50 +08:00 via Android
    md5 不是 128 位吗
    Jacky23333
        34
    Jacky23333  
       2020-05-05 19:38:49 +08:00 via Android
    @changePro 我是说你这句话 “-“md5 生成的 userId 对索引不友好” 少误导其他人”
    Jacky23333
        35
    Jacky23333  
       2020-05-05 19:39:15 +08:00 via Android
    @Jacky23333 我没觉得这句话误导他人了啊
    jugelizi
        36
    jugelizi  
       2020-05-05 20:54:51 +08:00
    似乎是 php 程序员
    aflow
        37
    aflow  
       2020-05-05 21:05:11 +08:00
    你这是在给后面埋坑,单体自增主键就好,md5 有重复的可能,不应该用来做唯一 id 。
    不要自作聪明
    v2Geeker
        38
    v2Geeker  
       2020-05-05 22:04:02 +08:00
    自增 id 好,这样建立的索引占用空间小,维护索引成本低,速度更快!
    dallaslu
        39
    dallaslu  
       2020-05-05 22:25:23 +08:00 via iPhone   ❤️ 1
    用户名 [email protected] 密码 forecast
    用户名 [email protected] 密码 recast

    所以还是乖乖用 UUID 吧。

    另外,自增 id 和 UUID 可以同时用。主键自增,UUID 唯一约束。对外想用哪个就用哪个。

    索引和存储空间的问题真不用担心。用户表邮箱、手机、用户名都有唯一约束,多一个也不见得会满。至于空间问题,最便宜的就是存储了……
    n0tyet
        40
    n0tyet  
       2020-05-06 00:39:08 +08:00 via Android
    把高性能 mysql 看完再继续思考
    tairan2006
        41
    tairan2006  
       2020-05-06 00:57:04 +08:00 via Android
    无意义,自增或者 snowflake

    MySQL 的话别用 uuid
    Mithril
        42
    Mithril  
       2020-05-06 01:13:37 +08:00
    这么多人都没说到点子上。。
    问题是 MD5 或者其它的摘要算法都是会冲突的啊。。。
    算法设计目的是尽量阻止人为制造冲突,而不是完全没有冲突,毕竟只是摘要而已。小概率事件并不等同于不可能事件。
    UUID 是直接在里面包含时间戳从而避免冲突的,你这个连时间戳都一块摘要了。。。
    除非你自行设计了解决办法,不然完全不应该直接用 Hash 作为数据库主键。
    3dwelcome
        43
    3dwelcome  
       2020-05-06 01:23:20 +08:00 via Android
    @Mithril 冲突可以切换到 64 位,128 位 hash,这种毕竟是少数,在一定数据量下属于可控范围,可以特殊处理。
    icegreen
        44
    icegreen  
       2020-05-06 08:42:50 +08:00
    同意上面一位老哥的说法;
    需要考虑你的数据库引擎以及索引结构, 以 mysql 的 innodb 来说, 主键无序,在插入的时候,会导致大量页分裂,降低索引效率;
    cheng6563
        45
    cheng6563  
       2020-05-06 08:44:47 +08:00 via Android
    非有序的主键对索引不太友好吧
    zchlwj
        46
    zchlwj  
       2020-05-06 09:28:52 +08:00
    MD5 可是会冲突的
    securityCoding
        47
    securityCoding  
       2020-05-06 10:33:17 +08:00
    不要把问题复杂化 , 直接使用雪花算法就行了
    Felldeadbird
        48
    Felldeadbird  
       2020-05-06 10:41:13 +08:00
    雪花算法可以满足楼主的需求了。
    ppyzzz
        49
    ppyzzz  
       2020-05-06 10:49:33 +08:00
    @hbolive 老哥,你这个回复是我在所有评论里面最接近真相的
    tailf
        50
    tailf  
       2020-05-06 10:56:51 +08:00
    主键使用这么长的字符串,确实会影响性能
    lanterboy
        51
    lanterboy  
       2020-05-06 11:39:47 +08:00
    19L 正解,如果楼主用的是 InnoDB 引擎,主键是聚簇的,乱序主键非常影响性能,建议把主键和业务唯一键分开,主键可以用雪花
    inktiger
        52
    inktiger  
       2020-05-06 11:46:06 +08:00
    占用空间是肯定的,问题复杂化了也是肯定的,做之前先想想目的是什么,我是觉得一切根据自身原因来
    tabris17
        53
    tabris17  
       2020-05-06 11:48:20 +08:00
    rimutuyuan
        54
    rimutuyuan  
       2020-05-06 12:03:55 +08:00
    主键 id 不要动,新建 user_id 字段唯一索引就能满足
    jsq2627
        55
    jsq2627  
       2020-05-06 12:37:47 +08:00
    1. MySQL innodb 主键是聚族索引,用 uuid / md5() / base58 / base64 等生成的无序串做主键,数据量增长后插入性能急剧下降
    2. 可以选择主键使用 AUTO_INCREMENT 整数,新增一列存储 uuid 等无序串并加索引。不过大查询量下,性能肯定不如主键这种聚族索引,可以通过缓存解决
    3. uuid 也不完全是无序串,毕竟它是基于时间戳生成的,按一定规则进行解构重组,也能变得“有序”,作为主键据说性能也还行
    https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
    4. snowflake 完美解决了所有问题
    jsq2627
        56
    jsq2627  
       2020-05-06 12:42:58 +08:00
    至于用 md5 还是 uuid,楼主自己觉得开心请随意选择。只要保证没有冲突就好。君不见 b 站现在都用变种 base56 了
    jsq2627
        57
    jsq2627  
       2020-05-06 12:43:22 +08:00
    @jsq2627 * 变种 base58
    Heebe
        58
    Heebe  
       2020-05-06 13:17:20 +08:00
    我想问下,MD5 是你想出来的,还是有人教你的?如果有人教你的,赶紧删除了他,因为他可能让你损失 1 个亿。

    我猜楼主是想做成一个无法被人类读取的订单号,来防止别人猜测业务量?
    这叫分布式 ID,随便搜索下就有一堆的方案。
    Mithril
        59
    Mithril  
       2020-05-06 13:38:16 +08:00
    @3dwelcome 插入数据的时候主键重复了。。这怎么特殊处理。。
    Cmdhelp
        60
    Cmdhelp  
       2020-05-06 13:40:55 +08:00
    建议看一波 al 爪洼开发手册
    3dwelcome
        61
    3dwelcome  
       2020-05-06 13:55:59 +08:00
    @Mithril 主键重复,就对自身加盐 hash,变成第二个,第三个 hash,循环多试几次,最后总能插进去的。
    查询也是同样操作,查出来数据需要二次校对,如果订单不对就进行二次 /三次查询。

    当然这些都是有前提的,表数据量必须在可控范围内。万一数据一多,冲突越多,肯定没戏。
    sujin190
        62
    sujin190  
       2020-05-06 14:03:17 +08:00
    id 的话,感觉有顺序的更好一点吧,空间的感觉无所谓吧,反正内存磁盘也不值钱
    Mithril
        63
    Mithril  
       2020-05-06 14:08:26 +08:00
    @3dwelcome 你这个前提是他不拿主键直接做查询,关键是既然不查询那为啥不直接自增 id 。。。
    而且冲突就只是个概率,并不是真的可控,只是大概率不出问题。运气不好一直冲突也不是没可能的。
    3dwelcome
        64
    3dwelcome  
       2020-05-06 14:17:49 +08:00
    @Mithril 我也不知道楼主为啥放着自增长 ID 不用,要另辟蹊径。也许就是楼上有人猜测的,防止对数据库暴力查询。因为实在想不出,这样做有别的什么理由。
    至于谈到冲突,以前测试过对 MD5 的散列分布图,图上来看还是挺均匀的。
    cloudzhou
        65
    cloudzhou  
       2020-05-06 14:18:58 +08:00
    如果不使用自增 id 的话,uuid 不行吗? snowflake 也可以
    jasonding
        66
    jasonding  
       2020-05-06 15:06:21 +08:00
    已经确认存在两个不同的值经过 MD5 运算后可能会导致结果一致,万一不幸碰上了你准备怎么处理
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2709 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 11:57 · PVG 19:57 · LAX 03:57 · JFK 06:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.