Mysql数据库自增id、uuid与雪花id实例分析

发布时间:2023-02-28 14:50:26 作者:iii
来源:亿速云 阅读:228

Mysql数据库自增id、uuid与雪花id实例分析

在数据库设计中,主键的选择是一个非常重要的决策。主键不仅用于唯一标识表中的每一行数据,还直接影响到数据库的性能、可扩展性和数据一致性。本文将详细分析MySQL数据库中三种常见的主键生成方式:自增ID、UUID和雪花ID(Snowflake ID),并通过实例探讨它们的优缺点及适用场景。

1. 自增ID

1.1 概述

自增ID是MySQL中最常见的主键生成方式。它通过在表中定义一个自增字段(通常为整数类型),每次插入新记录时,数据库会自动为该字段生成一个唯一的递增值。

1.2 实现方式

在MySQL中,可以通过以下方式定义一个自增ID字段:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

在这个例子中,id字段被定义为自增主键。每次插入新记录时,id字段的值会自动递增。

1.3 优点

  1. 简单易用:自增ID的实现非常简单,数据库会自动处理ID的生成,开发者无需关心ID的生成逻辑。
  2. 性能高:自增ID是连续的整数,存储和索引效率高,查询性能好。
  3. 空间占用小:自增ID通常使用整数类型(如INTBIGINT),占用的存储空间较小。

1.4 缺点

  1. 可预测性:自增ID是连续的,容易被猜测,存在一定的安全风险。
  2. 分布式系统不友好:在分布式系统中,多个数据库实例可能同时生成自增ID,导致ID冲突。
  3. 扩展性差:当数据量非常大时,自增ID可能会达到上限(如INT类型的上限为2147483647),需要进行扩展。

1.5 适用场景

自增ID适用于单机数据库或小规模分布式系统,尤其是在需要高性能和简单实现的场景下。

2. UUID

2.1 概述

UUID(Universally Unique Identifier)是一种全局唯一的标识符,通常由32个十六进制数字组成,形式为8-4-4-4-12的字符串。UUID的生成不依赖于数据库,可以在应用层生成。

2.2 实现方式

在MySQL中,可以通过以下方式定义一个UUID字段:

CREATE TABLE users (
    id CHAR(36) PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

在这个例子中,id字段被定义为UUID类型。每次插入新记录时,应用层需要生成一个UUID并插入到id字段中。

2.3 优点

  1. 全局唯一:UUID在全球范围内是唯一的,几乎不可能出现重复。
  2. 分布式系统友好:UUID不依赖于数据库,可以在分布式系统中生成,避免ID冲突。
  3. 安全性高:UUID是随机生成的,不易被猜测,安全性较高。

2.4 缺点

  1. 存储空间大:UUID通常以字符串形式存储,占用的存储空间较大(36个字符)。
  2. 索引效率低:UUID是随机生成的,插入时会导致索引树的频繁分裂,影响查询性能。
  3. 可读性差:UUID是一串无意义的字符,可读性较差,不利于调试和维护。

2.5 适用场景

UUID适用于分布式系统或需要全局唯一标识的场景,尤其是在安全性要求较高的系统中。

3. 雪花ID(Snowflake ID)

3.1 概述

雪花ID是Twitter开源的一种分布式ID生成算法,生成的ID是一个64位的整数,结构如下:

| 1 bit | 41 bits | 10 bits | 12 bits |
|-------|---------|---------|---------|
| sign  |  timestamp | machine ID | sequence |

3.2 实现方式

雪花ID的生成通常由应用层实现,以下是一个简单的Java实现示例:

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L; // 起始时间戳
    private final long workerIdBits = 5L; // 机器ID位数
    private final long datacenterIdBits = 5L; // 数据中心ID位数
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大机器ID
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 最大数据中心ID
    private final long sequenceBits = 12L; // 序列号位数
    private final long workerIdShift = sequenceBits; // 机器ID左移位数
    private final long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心ID左移位数
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间戳左移位数
    private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列号掩码

    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than " + maxWorkerId + " or less than 0");
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("datacenter Id can't be greater than " + maxDatacenterId + " or less than 0");
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

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

3.3 优点

  1. 全局唯一:雪花ID在分布式系统中是唯一的,避免了ID冲突。
  2. 性能高:雪花ID是64位整数,存储和索引效率高,查询性能好。
  3. 时间有序:雪花ID包含时间戳,生成的ID是按时间顺序递增的,有利于按时间范围查询。

3.4 缺点

  1. 依赖系统时钟:雪花ID的生成依赖于系统时钟,如果系统时钟回拨,可能会导致ID重复。
  2. 实现复杂:雪花ID的生成逻辑相对复杂,需要在应用层实现。

3.5 适用场景

雪花ID适用于分布式系统,尤其是在需要高性能和全局唯一ID的场景下。

4. 实例分析

4.1 自增ID实例

假设我们有一个用户表users,使用自增ID作为主键:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

插入数据时,数据库会自动生成id

INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');
INSERT INTO users (username, email) VALUES ('bob', 'bob@example.com');

查询结果:

SELECT * FROM users;
id username email
1 alice alice@example.com
2 bob bob@example.com

4.2 UUID实例

假设我们有一个用户表users,使用UUID作为主键:

CREATE TABLE users (
    id CHAR(36) PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

插入数据时,应用层生成UUID并插入:

INSERT INTO users (id, username, email) VALUES (UUID(), 'alice', 'alice@example.com');
INSERT INTO users (id, username, email) VALUES (UUID(), 'bob', 'bob@example.com');

查询结果:

SELECT * FROM users;
id username email
550e8400-e29b-41d4-a716-446655440000 alice alice@example.com
550e8400-e29b-41d4-a716-446655440001 bob bob@example.com

4.3 雪花ID实例

假设我们有一个用户表users,使用雪花ID作为主键:

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

插入数据时,应用层生成雪花ID并插入:

SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
long id1 = idGenerator.nextId();
long id2 = idGenerator.nextId();

// 插入数据
INSERT INTO users (id, username, email) VALUES (id1, 'alice', 'alice@example.com');
INSERT INTO users (id, username, email) VALUES (id2, 'bob', 'bob@example.com');

查询结果:

SELECT * FROM users;
id username email
12345678901 alice alice@example.com
12345678902 bob bob@example.com

5. 总结

在MySQL数据库中,自增ID、UUID和雪花ID各有优缺点,适用于不同的场景:

在实际应用中,开发者应根据具体需求选择合适的主键生成方式,以确保数据库的性能、可扩展性和数据一致性。

推荐阅读:
  1. 怎么用php+mysql实现聊天室功能
  2. PHP数据库连接mysql与mysqli的对比

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mysql id uuid

上一篇:np.zeros()函数如何使用

下一篇:np.array()函数如何使用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》