mysql主键id/uid抉择:从 UUID 到 UUIDv7演化之路
Author:zhoulujun Date:
比如我这个网站系统的,文章与栏目都是id,因为mysql 自增id太好使
但是,比如微信等,经常看到openID 与UID等信息,因为出于安全等考虑,不让用户通过id 自增,去定向爆破
这个时候资源对外id,业务上统一使用uid 或者bizId(businessID)
id存储uid 或者把id字段改为uid
那么这数据库层,直接让id主键存储 uid 字符串,或者 使用直接把id 改为uid,是否ok呢?
理论是是可以的(实际可以,就不用看下文了!)
就这样,我直接使用uid作为主键,更加方便明了
如何生成UID
mysql自带呀——简单便捷:直接在SQL语句中使用UUID(),无需额外代码。而且可以设置默认值呀!
mysql 生成uid函数
MySQL 8.0以上或者MariaDB 10.8以上,使用UUID_TO_BIN(UUID(), 1)可将时间戳前置,生成二进制有序UUID,显著提升索引性能。
CREATE TABLE example ( uid BINARY(16) PRIMARY KEY DEFAULT UUID_TO_BIN(UUID(), 1), );
这样就okay了,但是,如过你真的在生产上这么搞,稍微正规点的团队,老板都要屌死你——由于 UUID 值的大小和未排序,使用 UUID 值可能会导致性能问题。
使用uuid和自增id的索引结构对比
使用自增id的内部结构
自增的主键的值是顺序的,所以Innodb把每一条记录都存储在一条记录的后面。当达到页面的最大填充因子时候(innodb默认的最大填充因子是页大小的15/16,会留出1/16的空间留作以后的修改):
下一条记录就会写入新的页中,一旦数据按照这种顺序的方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费
新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行的位置而做出额外的消耗
减少了页分裂和碎片的产生

别人一旦爬取你的数据库,就可以根据数据库的自增id获取到你的业务增长信息,很容易分析出你的经营情况
对于高并发的负载,innodb在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争
Auto_Increment锁机制会造成自增锁的抢夺,有一定的性能损失——Auto_increment的锁争抢问题,如果要改善需要调优innodb_autoinc_lock_mode的配置
使用uuid的索引内部结构
因为uuid相对顺序的自增id来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以innodb无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。这个过程需要做很多额外的操作,数据的毫无顺序会导致数据分布散乱,将会导致以下的问题:
写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机IO
因为写入是乱序的,innodb不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上
由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片
在把随机值(uuid和雪花id)载入到聚簇索引(innodb默认的索引类型)以后,有时候会需要做一次OPTIMEIZE TABLE来重建表并优化页的填充,这将又需要一定的时间消耗。
对于此,有蛮多的测试案例,关于
user_auto_key,user_uuid,user_random_key,
MySql为什么不推荐使用UUID做主键 https://blog.csdn.net/chenwiehuang/article/details/123420278
使用雪花id或uuid作为Mysql主键,被媳妇怼了一顿 https://zhuanlan.zhihu.com/p/493842904
面试官:为什么不推荐使用 uuid 作为 mysql 主键? https://www.zhihu.com/question/634168677/answer/3329212239
从 UUID 到 UUIDv7
UUID 不断发展,以满足对时间敏感的应用需求。最常用的版本包括:
UUIDv1:利用时间和节点信息,包含时间戳和 MAC 地址。虽然能有效保证唯一性,但由于 MAC 地址会暴露敏感信息,因此会带来隐私问题。
UUIDv4:随机生成,提供了简单性和隐私性,但代价是潜在的(尽管极不可能发生)碰撞。该版本被广泛应用于顺序排列并不重要的场合。
UUIDv3 和 UUIDv5:利用哈希算法(v3 使用 MD5,v5 使用 SHA-1)从命名空间标识符和名称推导出 UUID,确保相同输入的结果具有确定性。
RFC 9562 中引入的更新版本带来了重大改进:
UUIDv6:v1 的重组版本,具有更强的私密性,并针对时间顺序排序进行了优化。
UUIDv7:旨在提供基于时间的顺序排序,是数据库索引和分布式系统的理想选择。
UUIDv8:允许自定义应用特定元数据字段,提供无与伦比的灵活性。
UUIDv7 解决了早期版本的主要缺陷,尤其是在数据库索引和分布式系统方面。通过使用有时间顺序的结构,可以确保:
高效索引:基于时间的顺序排列减少了数据库索引中的碎片,从而提高了查询性能。
高可扩展性:适用于需要唯一、有序标识符的分布式环境。
隐私性:避免包含 MAC 地址等敏感信息。
UUIDv8 引入了一项突破性功能:针对特定应用需求的自定义位。该版本允许在 UUID 中直接嵌入元数据,使其具有很强的适应性:
物联网设备:嵌入设备特定信息
跨系统数据传输:包含上下文元数据,便于跟踪。
自定义应用:根据特定领域需求定制 UUID。
| 版本 | 构造方式 | 主要功能 | 用例 |
|---|---|---|---|
| v1 | 时间 + MAC地址 | 高唯一性, 隐私问题 | 传统系统, 内部工具 |
| v4 | 随机 | 简单, 高隐私性 | Web应用, 通用功能 |
| v6 | 基于时间(重构的) | 有序, 隐私增强 | 现代数据库 |
| v7 | 时间有序(RFC 9562) | 索引优化 | 分布式系统, 日志 |
| v8 | 自定义字段 | 高灵活性 | IoT, 特定应用 |
这些,目前的库都很成熟了,比如:https://www.npmjs.com/package/uuid
超越 UUID:替代方案与灵感
ULID:将基于时间戳的排序与随机性相结合,确保单调性。
Snowflake:由 Twitter 提出,包含时间戳、机器 ID 和序列号。
KSUID:为分布式系统优化的 K 排序唯一标识符。
虽然这些替代方案在特定情况下很有效,但 UUID 为大部分应用提供了标准化、跨平台的解决方案
参考文章:
从 UUID 到 UUIDv7:唯一标识符的演进 https://zhuanlan.zhihu.com/p/16649819723
转载本站文章《mysql主键id/uid抉择:从 UUID 到 UUIDv7演化之路》,
请注明出处:https://www.zhoulujun.cn/html/webfront/server/nestjs/9537.html