网站开发人员需求,百度网站收录查询地址,公司网址平台有哪些,带网站的电话MySQL 主键类型选型指南#xff1a;自增、UUID、雪花算法怎么选
主键#xff08;Primary Key, PK#xff09;不仅是“唯一标识一行数据”的约束#xff0c;更是 InnoDB 的物理组织方式#xff1a;InnoDB 的表数据按主键构建聚簇索引#xff08;Clustered Index#xff0…MySQL 主键类型选型指南自增、UUID、雪花算法怎么选主键Primary Key, PK不仅是“唯一标识一行数据”的约束更是 InnoDB 的物理组织方式InnoDB 的表数据按主键构建聚簇索引Clustered Index数据行存放在主键 BTree 的叶子节点上。这意味着主键的长度会放大所有二级索引Secondary Index的体积与维护成本二级索引叶子节点存的是“二级索引键 主键值”。主键的有序性决定插入是否“尾插”还是频繁“页分裂”page split直接影响写入吞吐、锁竞争和碎片。本文从工程实践出发比较自增主键、UUID 及常见分布式 ID并给出可落地的选型建议与 DDL 示例。1. 选主键的核心目标把主键选型拆成 6 个维度更容易做“最优方案”的工程权衡写入性能是否顺序递增、是否导致页分裂、是否放大二级索引维护。存储体积主键越大所有二级索引越大Buffer Pool 命中率越低。分布式唯一性多机房/多写入节点/离线导入是否需要“无中心生成”。可读性与调试是否方便排查、是否能按时间粗略排序。安全性ID 是否可枚举自增 ID 在外部暴露时容易被遍历。迁移与合并跨库合并、主从切换、双写、历史数据回灌是否容易冲突。2. 常见主键方案对比下面按“工程上最常见的几类主键”进行对比。2.1 自增主键BIGINT UNSIGNED AUTO_INCREMENT特点单库单写或主从复制环境下最简单、性能最好。值递增插入高度“尾插”对 InnoDB 友好。整数短通常 8 字节二级索引额外负担小。优点写入吞吐高页分裂少。索引紧凑Buffer Pool 利用率高。业务实现简单生态支持最好。缺点多写入节点多主/多活/分库分表下会有冲突需要统一发号器或分段策略。对外暴露时可枚举可能泄露业务规模与数据存在性。推荐场景单主写、多从读、绝大多数 OLTP 系统。主键不需要在应用侧提前生成。DDL 示例CREATETABLEorders(idBIGINTUNSIGNEDNOTNULLAUTO_INCREMENT,user_idBIGINTUNSIGNEDNOTNULL,statusTINYINTNOTNULL,created_atDATETIMENOTNULL,PRIMARYKEY(id),KEYidx_user_created(user_id,created_at))ENGINEInnoDB;2.2 UUID文本形态CHAR(36)/VARCHAR(36)特点典型值形态如550e8400-e29b-41d4-a716-446655440000。字符存储大36 字符 字符集开销对索引极不友好。UUID 近似随机插入在 BTree 上“到处落点”页分裂多。优点去中心化生成天然适合分布式唯一性。对外暴露不易枚举。缺点工程上很关键索引膨胀严重主键变大后所有二级索引都跟着变大。写入放大明显随机插入导致页分裂与碎片可能引发吞吐下降与延迟抖动。推荐场景非高写入、或数据量不大且强依赖跨系统唯一性的场景。更推荐使用二进制形态或可排序 UUID见后文。2.3 UUID二进制形态BINARY(16)特点存储 16 字节比CHAR(36)紧凑很多。仍可能是“随机插入”但索引体积显著降低。MySQL 8.0 推荐用法减少随机写MySQL 8.0 提供UUID_TO_BIN()/BIN_TO_UUID()并支持“交换时间字段”的优化使 UUID 更接近按时间递增减少页分裂CREATETABLEevents(idBINARY(16)NOTNULL,created_atDATETIMENOTNULL,payload JSONNOTNULL,PRIMARYKEY(id))ENGINEInnoDB;INSERTINTOevents(id,created_at,payload)VALUES(UUID_TO_BIN(UUID(),1),NOW(),JSON_OBJECT(k,v));SELECTBIN_TO_UUID(id,1)ASid_textFROMeventsLIMIT10;其中第二个参数1会对 UUID 的部分字节做重排让新生成的 UUID 在索引上更“顺序”。优缺点总结相比CHAR(36)空间与索引体积大幅改善。相比自增仍更复杂写入性能通常略弱尤其在高并发写入、热点二级索引多时。2.4 分布式递增 IDSnowflake/号段/时间序这类方案的目标是兼得全局唯一分布式可生成大体递增减少页分裂接近自增的写入局部性整数紧凑通常 64-bit常见实现雪花算法Snowflake时间戳 机器号 序列。号段模式Segment/Leaf集中服务批量发号应用侧内存递增。ULID/KSUID带时间前缀的可排序标识常用 128-bit。优点分布式唯一且近似递增写入更平滑。使用BIGINT8 字节时索引紧凑。缺点实现复杂度更高需要校时、机器号管理、容灾策略。如果强依赖严格递增顺序需要额外保证多数方案仅“趋势递增”。推荐场景分库分表、多活写入、跨系统合并。高写入 OLTP希望兼顾性能与全局唯一。DDL 示例以BIGINT存储分布式 IDCREATETABLEtrade(idBIGINTUNSIGNEDNOTNULL,buyer_idBIGINTUNSIGNEDNOTNULL,amountDECIMAL(18,2)NOTNULL,created_atDATETIMENOTNULL,PRIMARYKEY(id),KEYidx_buyer_created(buyer_id,created_at))ENGINEInnoDB;2.5 业务主键 / 联合主键Natural Key / Composite PK例如用(tenant_id, order_no)作为主键。优点语义强天然防重不需要额外唯一索引。某些查询天然命中主键。缺点主键更长二级索引被放大。业务字段一旦变更成本高主键更新在 InnoDB 中代价大。多字段比较更慢Join 成本更高。推荐实践更常见做法使用**短的代理主键Surrogate Key**作为 PK再对业务唯一字段建UNIQUE约束。CREATETABLEtenant_order(idBIGINTUNSIGNEDNOTNULLAUTO_INCREMENT,tenant_idBIGINTUNSIGNEDNOTNULL,order_noVARCHAR(64)NOTNULL,created_atDATETIMENOTNULL,PRIMARYKEY(id),UNIQUEKEYuk_tenant_orderno(tenant_id,order_no))ENGINEInnoDB;3. 为什么“主键越短、越顺序”通常越好3.1 二级索引会“携带”主键InnoDB 的二级索引叶子节点存储的是主键值而不是行指针所以主键从 8 字节变为 16 字节所有二级索引都会变“更胖”。主键从BIGINT变为CHAR(36)二级索引体积可能成倍增长。二级索引变胖会带来更高的磁盘与内存消耗、更低的缓存命中、更高的写放大。3.2 随机主键更容易页分裂递增主键新数据大多插在右侧BTree 追加为主。随机主键插入点分布在整棵树容易触发页分裂、页面搬迁造成碎片与抖动。这就是为什么“UUID 文本主键”在写入压力上去后往往比自增慢很多。4. 选型建议按场景给结论4.1 单库单写绝大多数业务首选BIGINT UNSIGNED AUTO_INCREMENT对外暴露 ID 的安全诉求外部展示用单独的“展示 ID”例如短链/编码内部仍用自增主键。或增加一列public_idUUID/ULID并加唯一索引作为对外标识。4.2 分布式多写 / 分库分表 / 多活首选趋势递增的分布式BIGINT雪花/号段作为主键。备选BINARY(16)的可排序 UUID/ULID写入与索引体积通常优于CHAR(36)。4.3 写入极高 二级索引较多强烈倾向8 字节整数主键自增或分布式BIGINT。尽量避免CHAR(36)UUID 主键。4.4 数据迁移、跨库合并频繁若需要无冲突合并分布式 ID雪花/号段或BINARY(16)UUID。若只在离线任务合并也可以保留自增主键但需要设计“映射表/重写主键”的迁移流程。4.5 业务天然有稳定且短的唯一键可以选择业务键做UNIQUE主键仍用短代理键。只有在确认“字段稳定、不会变更、且长度非常短”时才考虑业务键做 PK。5. 推荐的工程落地模板模板 A性能优先内部主键自增外部 ID 随机CREATETABLEuser_profile(idBIGINTUNSIGNEDNOTNULLAUTO_INCREMENT,public_idBINARY(16)NOTNULL,nicknameVARCHAR(64)NOTNULL,created_atDATETIMENOTNULL,PRIMARYKEY(id),UNIQUEKEYuk_public_id(public_id))ENGINEInnoDB;写入时INSERTINTOuser_profile(public_id,nickname,created_at)VALUES(UUID_TO_BIN(UUID(),1),alice,NOW());模板 B分布式优先分布式 BIGINT 主键CREATETABLEmessage(idBIGINTUNSIGNEDNOTNULL,room_idBIGINTUNSIGNEDNOTNULL,sender_idBIGINTUNSIGNEDNOTNULL,contentTEXTNOTNULL,created_atDATETIMENOTNULL,PRIMARYKEY(id),KEYidx_room_created(room_id,created_at))ENGINEInnoDB;6. 常见坑与细节不要用INT省空间大多数长期业务更建议从一开始就用BIGINT UNSIGNED避免未来扩容与迁移。不要把 UUID 文本当主键CHAR(36)主键通常是最差的组合大 随机。主键尽量不要有业务含义业务变化会让“带语义主键”变更成本极高。二级索引越多主键越要短因为每个二级索引都要携带主键。可排序 UUID 也不是完全顺序高并发下仍可能产生一定随机性但一般显著优于完全随机 UUID。7. 一句话决策表你的约束/目标更优选择单库单写、追求性能、实现简单BIGINT UNSIGNED AUTO_INCREMENT多写入节点、全局唯一、写入仍要稳分布式趋势递增BIGINT雪花/号段必须应用侧生成、且希望索引别太大BINARY(16)UUID_TO_BIN(UUID(), 1)要对外不可枚举但内部仍要高性能内部自增 PK 对外public_id唯一索引业务唯一键稳定且短PK 用代理键业务键加UNIQUE更常见8. 结论默认最优BIGINT UNSIGNED AUTO_INCREMENT单写场景简单、快、省。分布式最优趋势递增的分布式BIGINT雪花/号段兼顾唯一与写入性能。UUID 的正确打开方式尽量用BINARY(16)并使用可排序/重排策略避免CHAR(36)做主键。