导读:本期聚焦于小伙伴创作的《为什么MySQL不建议使用UUID做主键?探讨随机IO对B+树索引页分裂的影响》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《为什么MySQL不建议使用UUID做主键?探讨随机IO对B+树索引页分裂的影响》有用,将其分享出去将是对创作者最好的鼓励。

在MySQL表结构设计中,主键的选择直接影响数据库的读写性能和索引维护成本。UUID作为全局唯一的标识符,常被用于分布式场景下的主键生成,但MySQL官方并不推荐将其作为InnoDB引擎表的主键,这背后的核心原因和UUID的随机特性、B+树索引的工作机制密切相关。

为什么MySQL不建议使用UUID做主键?探讨随机IO对B+树索引页分裂的影响

UUID作为主键的核心问题

UUID是128位的无序字符串,常见的版本4 UUID由随机数字生成,本身没有顺序性。当使用UUID作为InnoDB表的主键时,会带来两个核心问题:随机IO和B+树索引页分裂。

随机IO的影响

InnoDB的主键索引是聚簇索引,数据行直接存储在主键索引的叶子节点中。如果主键是有序的,新插入的数据会追加到索引的末尾,只需要访问最新的索引页即可,属于顺序IO,性能很高。

但UUID是无序的,新插入的主键值可能远小于之前插入的值,也可能远大于,插入位置是随机的。这意味着每次插入都需要随机访问不同的索引页,产生大量随机IO。随机IO的寻道成本远高于顺序IO,会直接降低写入性能。

B+树索引页分裂的影响

InnoDB的B+树索引页默认大小是16KB,每个索引页存储一定数量的索引记录。当索引页满了之后,再插入新的记录就会触发页分裂:将一个页的数据拆分成两个页,同时上层索引节点需要新增记录指向新的页。

由于UUID是无序的,新插入的记录很可能插入到已经写满的索引页中间,频繁触发页分裂。页分裂会带来三个负面影响:

  • 分裂过程需要移动大量数据,消耗额外的CPU和IO资源
  • 分裂后会产生碎片空间,降低索引页的存储空间利用率
  • 上层索引节点也可能因为新增记录触发分裂,甚至引发连锁分裂,进一步增加开销

对比自增主键的优势

自增主键是有序递增的,插入时永远追加到索引末尾,几乎不会触发页分裂,同时写入是顺序IO,性能远高于UUID主键。我们可以通过简单的测试对比两者的性能差异。

测试表结构

创建两个结构相同的表,一个使用自增主键,一个使用UUID主键:

-- 自增主键表
CREATE TABLE `user_auto` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- UUID主键表
CREATE TABLE `user_uuid` (
  `id` varchar(36) NOT NULL,
  `name` varchar(50) DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

批量插入测试

向两个表分别插入10万条测试数据,观察插入耗时:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.UUID;

public class InsertTest {
    public static void main(String[] args) throws Exception {
        // 自增主键插入测试
        long start1 = System.currentTimeMillis();
        insertAutoIncrement();
        long end1 = System.currentTimeMillis();
        System.out.println("自增主键插入10万条耗时:" + (end1 - start1) + "ms");

        // UUID主键插入测试
        long start2 = System.currentTimeMillis();
        insertUUID();
        long end2 = System.currentTimeMillis();
        System.out.println("UUID主键插入10万条耗时:" + (end2 - start2) + "ms");
    }

    private static void insertAutoIncrement() throws Exception {
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8", "root", "123456");
        PreparedStatement ps = conn.prepareStatement("INSERT INTO user_auto (name, age) VALUES (?, ?)");
        for (int i = 0; i < 100000; i++) {
            ps.setString(1, "user" + i);
            ps.setInt(2, i % 100);
            ps.executeUpdate();
        }
        ps.close();
        conn.close();
    }

    private static void insertUUID() throws Exception {
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8", "root", "123456");
        PreparedStatement ps = conn.prepareStatement("INSERT INTO user_uuid (id, name, age) VALUES (?, ?, ?)");
        for (int i = 0; i < 100000; i++) {
            ps.setString(1, UUID.randomUUID().toString());
            ps.setString(2, "user" + i);
            ps.setInt(3, i % 100);
            ps.executeUpdate();
        }
        ps.close();
        conn.close();
    }
}

实际测试中,自增主键的插入耗时通常在1-2秒,而UUID主键的插入耗时可能达到5-10秒,性能差距非常明显。同时查看UUID主键表的索引碎片率,也会远高于自增主键表。

特殊场景的替代方案

如果是分布式场景,确实需要全局唯一的主键,也不建议直接使用随机UUID,可以考虑以下替代方案:

  • 使用有序的UUID,比如版本7的UUID,基于时间戳生成,具备有序性,减少随机IO和页分裂
  • 使用雪花算法生成有序的分布式ID,兼顾唯一性和顺序性
  • 使用自增主键作为物理主键,同时新增一个UUID字段作为业务唯一标识,给业务字段加唯一索引即可

总之,在MySQL InnoDB引擎下,除非有特殊业务需求,否则优先选择自增主键或者有序的分布式ID作为主键,避免使用无序的UUID,才能最大程度发挥B+树索引的性能优势。

MySQLUUID主键B+树索引页分裂修改时间:2026-07-19 20:06:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。