编程 为什么要放弃UUID作为MySQL主键?

2024-11-18 23:33:07 +0800 CST views 618

为什么要放弃UUID作为MySQL主键?

引言

在数据库设计中,自增主键最为常见,但当面对数据删除等场景时,自增主键容易被猜测,从而存在安全隐患。因此,UUID(Universally Unique Identifier)被广泛采用,因其全球唯一性和不可预测性,成为不少开发者的选择。我最初的项目也是使用UUID作为MySQL主键,然而随着业务发展,我们逐渐替换成了雪花算法生成的ID。本文将深入探讨UUID和雪花算法的差异,并解释为什么在实际项目中,我们选择了雪花算法。

什么是UUID?

images
UUID是一种全球唯一的标识符,通常用于生成在分布式系统中不重复的标识。它具有以下特点:

  • 全局唯一性:理论上保证每个UUID都是唯一的。
  • 不可预测性:生成方式随机,难以预测。
  • 标准化:遵循RFC 4122标准。
  • 自包含:无需依赖外部系统,独立生成。

UUID由32个十六进制字符组成,常见格式为8-4-4-4-12,如:550e8400-e29b-41d4-a716-446655440000。常用的UUID版本包括:

  • Version 1:基于时间戳和MAC地址,可能会引发隐私问题。
  • Version 4:完全随机生成,重复的概率极低。
  • Version 3 & 5:基于命名空间的哈希算法生成,分别使用MD5和SHA-1。

生成UUID示例

在Java中,生成UUID的代码如下:

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID(); 
        System.out.println("Generated UUID: " + uuid);
    }
}

虽然UUID提供了全球唯一性,但随着业务发展和需求变化,我接触到了雪花算法,并改变了原有的观点。

什么是雪花算法?

雪花算法(Snowflake Algorithm)由Twitter开发,用于分布式系统中生成全局唯一且递增的长整型ID。其主要特点包括:

  • 全局唯一性:即使在网络分区的情况下,也能保证生成的ID不会重复。
  • 高效性:基于时间戳和机器标识生成,毫秒级响应,适合高并发环境。
  • 有序性:ID是递增的,有利于数据库的插入和查询优化。
  • 占用空间小:生成的ID为64位整数,仅占用8字节,比UUID小。
  • 易于扩展:支持分布式系统下的多节点扩展。

雪花算法生成器示例

以下是雪花算法的实现:

public class SnowflakeIDGenerator {
    private static final long EPOCH = 1722947753723L;  
    private final long workerId;
    private final long datacenterId;
    private final long sequence;
    private final AtomicLong lastTimestamp = new AtomicLong(EPOCH - 1L);

    public SnowflakeIDGenerator(long workerId, long datacenterId) {
        // 检查workerId和datacenterId的有效性
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = 0L;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp.get()) {
            throw new RuntimeException("Clock moved backwards.");
        }
        if (lastTimestamp.get() == timestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp.get());
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp.set(timestamp);
        return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) |
               (datacenterId << DATACENTER_ID_SHIFT) |
               (workerId << WORKER_ID_SHIFT) |
               sequence;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

使用示例

public static void main(String[] args) {
    SnowflakeIDGenerator generator = new SnowflakeIDGenerator(1, 1); 
    for (int i = 0; i < 10; i++) {
        System.out.println(generator.nextId());
    }
}

为什么选择雪花算法而不是UUID?

在MySQL中,使用雪花算法作为主键有多个好处,尤其在性能和存储效率上优于UUID。

1. 存储空间

  • UUID:UUID为128位(16字节),占用较多空间。
  • 雪花算法:生成的ID为64位(8字节),节省了一半的存储空间,尤其在大规模数据表中,存储效率更高。

2. 性能

  • UUID:UUID v4基于随机数生成,虽然速度快,但不如雪花算法稳定。而UUID v1/v2基于MAC地址和时间戳,可能会引发隐私问题。
  • 雪花算法:基于时间戳和简单的数学运算生成,速度极快,毫秒级响应,适合高并发场景。

3. 排序性能

雪花算法生成的ID是递增的,这有助于数据库索引的优化。MySQL使用B+树结构存储索引,当主键递增时,可以减少页面分裂,提高插入和查询性能。而UUID生成的ID是随机的,容易导致索引不连续,降低性能。

4. 并发性能

雪花算法非常适合高并发环境,它能够在多台服务器上生成唯一的ID,避免冲突。而UUID虽然也可以用于并发场景,但其生成机制效率不如雪花算法。

5. 数据库性能

MySQL在处理连续的整数主键时性能更好,雪花算法生成的递增ID利用了InnoDB的B+树结构,能够加快数据插入和检索操作。而UUID的随机性会导致索引分裂,影响插入和查询性能。

6. 可预测性和易理解性

雪花算法生成的ID可以根据时间戳解析出生成时间,便于调试和审计。UUID的随机性虽然确保了唯一性,但难以解读和调试。

总结

选择雪花算法作为MySQL主键的原因主要是出于性能、存储空间和数据库操作效率的考虑。尽管UUID是一种广泛使用的唯一标识符,但在高并发、分布式系统中,雪花算法凭借其全局唯一性、递增性和高效性,成为了主键生成的最佳选择。

推荐文章

Vue3中如何处理状态管理?
2024-11-17 07:13:45 +0800 CST
Gin 框架的中间件 代码压缩
2024-11-19 08:23:48 +0800 CST
Elasticsearch 的索引操作
2024-11-19 03:41:41 +0800 CST
使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
markdowns滚动事件
2024-11-19 10:07:32 +0800 CST
Golang Sync.Once 使用与原理
2024-11-17 03:53:42 +0800 CST
介绍 Vue 3 中的新的 `emits` 选项
2024-11-17 04:45:50 +0800 CST
Golang - 使用 GoFakeIt 生成 Mock 数据
2024-11-18 15:51:22 +0800 CST
Dropzone.js实现文件拖放上传功能
2024-11-18 18:28:02 +0800 CST
curl错误代码表
2024-11-17 09:34:46 +0800 CST
资源文档库
2024-12-07 20:42:49 +0800 CST
程序员茄子在线接单