使用雪花ID和UUID作为MySQL主键的影响分析
前言
在设计MySQL数据库表时,MySQL官方建议使用连续自增的主键ID(auto_increment
),而不是UUID或者雪花ID(Snowflake ID)。那么,为什么不推荐使用UUID?UUID究竟有哪些缺点?
本篇文章将分析这个问题,探讨内部原因,并通过实验数据来直观展示不同主键策略对数据库性能的影响。
目录
MySQL程序实例
1.1 创建测试表
为了验证不同主键策略的影响,我们建立三张表:
user_auto_key
:主键采用auto_increment
的自增ID。user_uuid
:主键采用UUID。user_random_key
:主键采用雪花ID(随机不连续的18位Long类型)。
表结构示例如下:
CREATE TABLE user_auto_key (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(50),
sex CHAR(1),
address VARCHAR(255),
city VARCHAR(100),
email VARCHAR(100),
state VARCHAR(10)
);
CREATE TABLE user_uuid (
id CHAR(36) PRIMARY KEY,
user_name VARCHAR(50),
sex CHAR(1),
address VARCHAR(255),
city VARCHAR(100),
email VARCHAR(100),
state VARCHAR(10)
);
CREATE TABLE user_random_key (
id BIGINT PRIMARY KEY,
user_name VARCHAR(50),
sex CHAR(1),
address VARCHAR(255),
city VARCHAR(100),
email VARCHAR(100),
state VARCHAR(10)
);
1.2 测试程序
为了测试插入数据的效率,我们使用Spring Boot + JDBC Template + JUnit + Hutool来完成数据插入操作。每张表插入相同数量的随机数据。
@Test
void testDBTime() {
StopWatch stopwatch = new StopWatch("SQL执行时间统计");
// 自增ID
final String insertSql = "INSERT INTO user_auto_key(user_name, sex, address, city, email, state) VALUES(?,?,?,?,?,?)";
List<UserKeyAuto> insertData = autoKeyTableService.getInsertData();
stopwatch.start("自增ID表插入");
jdbcTemplateService.insert(insertSql, insertData, false);
stopwatch.stop();
// UUID
final String insertSql2 = "INSERT INTO user_uuid(id, user_name, sex, address, city, email, state) VALUES(?,?,?,?,?,?,?)";
List<UserKeyUUID> insertData2 = uuidKeyTableService.getInsertData();
stopwatch.start("UUID表插入");
jdbcTemplateService.insert(insertSql2, insertData2, true);
stopwatch.stop();
// 雪花ID
final String insertSql3 = "INSERT INTO user_random_key(id, user_name, sex, address, city, email, state) VALUES(?,?,?,?,?,?,?)";
List<UserKeyRandom> insertData3 = randomKeyTableService.getInsertData();
stopwatch.start("雪花ID表插入");
jdbcTemplateService.insert(insertSql3, insertData3, true);
stopwatch.stop();
System.out.println(stopwatch.prettyPrint());
}
1.3 插入数据的结果
在测试插入100万条数据后,我们发现:
user_auto_key
表的插入速度最快。user_random_key
表次之。user_uuid
表最慢。
1.4 效率测试
插入数据量 100 万后,再追加 10 万数据,结果如下:
主键策略 | 100万数据插入时间 | 追加10万数据时间 |
---|---|---|
自增ID | 10.2 秒 | 1.1 秒 |
雪花ID | 12.4 秒 | 1.5 秒 |
UUID | 20.8 秒 | 3.6 秒 |
可以看到,UUID的插入性能在数据量增加后急剧下降。
UUID与自增ID的索引结构对比
2.1 自增ID的索引结构
自增ID是顺序递增的,InnoDB存储时会按顺序写入,避免了页分裂,提升了存储效率。
2.2 UUID的索引结构
UUID是随机的,导致数据分布不均匀,会出现以下问题:
- 频繁的页分裂,导致存储碎片。
- 需要大量的随机I/O,降低插入效率。
- 需要额外的磁盘空间。
2.3 自增ID的缺点
虽然自增ID有较好的插入性能,但也存在一些缺点:
- 信息泄露:如果数据库被爬取,可以推测业务增长情况。
- 并发争抢:高并发插入时,主键的上界可能成为争抢热点。
- 锁争用:
Auto_increment
锁机制可能影响性能。
总结
本篇文章通过实验分析了自增ID、UUID和雪花ID在MySQL中的插入效率,发现:
- 自增ID的插入效率最高,适用于大多数业务场景。
- UUID插入性能最差,容易导致索引碎片,不推荐使用。
- 雪花ID介于两者之间,适用于分布式系统。
在实际开发中,建议优先使用自增ID,但如果业务对分布式唯一性有强需求,可考虑雪花ID作为主键。
评论区