欢迎光临千百叶网
详情描述

MySQL 主键不推荐使用 UUID(尤其是无序 UUID)的主要原因包括:

深层原因分析

1. 索引性能问题

  • 页分裂严重:InnoDB 使用 B+Tree 索引,数据按主键顺序物理存储。无序 UUID 插入导致频繁的中间插入,引发大量页分裂和重组
  • 索引碎片化:随机写入使索引页面填充率降低(通常只有 50%-70%),而自增 ID 可达 90%+
  • 缓存效率低:随机访问模式使缓冲池(Buffer Pool)命中率下降,热数据被频繁换出

2. 存储空间开销

  • UUID 占用 16 字节(32 字符+分隔符),而 BIGINT 仅 8 字节
  • 二级索引都会包含主键值,所有二级索引体积增大约 100%
  • 更大的索引意味着更少的索引页可放入内存

3. 写入性能影响

  • 顺序写入的吞吐量比随机写入高 5-10 倍
  • UUID 生成需要额外 CPU 开销(尤其是 v4 的随机性)

4. 运维复杂度

  • 难以通过主键范围判断数据插入时间
  • 调试和排查问题时,UUID 的可读性差

性能对比数据

指标 自增 BIGINT UUID v4 UUID v1/v7
插入吞吐量 100% (基准) 15%-30% 60%-80%
索引大小 100% (基准) 180%-220% 180%-220%
Buffer Pool 命中率 中等

推荐解决方案

方案1:使用自增主键 + UUID 业务标识

CREATE TABLE users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    uid CHAR(36) NOT NULL UNIQUE COMMENT '对外业务标识',
    -- 其他字段
    UNIQUE KEY uk_uid (uid)
);

方案2:使用有序 UUID(推荐)

  • UUID v7:基于时间戳的有序 UUID(2022 年新增标准)
  • UUID v1:时间戳+MAC 地址(有隐私顾虑)
  • 雪花算法:分布式 ID 生成方案

方案3:组合主键策略

-- 使用时间前缀的有序 ID
CREATE TABLE orders (
    id CHAR(21) PRIMARY KEY, -- 格式: {timestamp}{random}
    created_time DATETIME NOT NULL
);

-- 示例 ID: 20250115123456_abc123

方案4:MySQL 8.0 的新特性

-- 使用自动生成的紧凑有序 ID
CREATE TABLE logs (
    id BINARY(16) DEFAULT (UUID_TO_BIN(UUID(), 1)) PRIMARY KEY,
    -- 1 表示有序模式
);

最佳实践建议

默认使用自增主键,除非有明确的分库分表需求

需要分布式 ID 时

  • 优先选择 UUID v7(时间有序)
  • 雪花算法/Snowflake

已使用无序 UUID 的迁移方案

-- 1. 新增自增主键列
ALTER TABLE table ADD COLUMN new_id BIGINT AUTO_INCREMENT UNIQUE;

-- 2. 逐步迁移外键关系
-- 3. 最终切换主键(需要停机维护)

分库分表场景

  • 使用 业务前缀 + 时间有序组件 的组合键
  • 考虑 TinyID、Leaf 等分布式 ID 生成器

例外情况

以下场景可考虑使用 UUID:

  • 需要离线生成 ID 的客户端应用
  • 安全要求高,需要不可猜测的 ID
  • 数据合并时要求全局绝对唯一

监控建议

如果必须使用 UUID,请监控:

  • 索引碎片率:SHOW TABLE STATUS
  • 缓冲池命中率:SHOW ENGINE INNODB STATUS
  • 定期执行 OPTIMIZE TABLE 或使用在线模式重建表

总的来说,99% 的 MySQL 场景使用自增主键都是最优选择,仅在特定分布式场景需要权衡选择有序 UUID 或专门的分布式 ID 方案。

相关帖子