返回

掘金是如何生成文章ID的?SnowFlake揭秘

后端

数据库中的主键:为你的数据赋予身份

数据库是存储和管理数据的关键工具,而主键是确保数据完整性和可靠性的核心要素。本文将深入探讨数据库主键的设计,并特别关注 UUID 和 Snowflake 雪花算法在掘金文章 ID 生成中的应用。

主键的重要性

主键是数据库表中唯一标识每一行的字段。选择一个有效的为主键至关重要,它直接影响数据库的性能和可靠性。常见的类型包括:

  • 整数自增主键: 简单易用,通过递增的整数为每一行分配唯一标识符。
  • UUID: 通用唯一标识符,由 32 个十六进制数字组成,全球范围内唯一。
  • Snowflake 雪花算法: 分布式主键生成算法,生成具有全局唯一性的 ID。

UUID 和 Snowflake 雪花算法的较量

UUID 和 Snowflake 雪花算法都是生成唯一 ID 的常用方法,但两者之间存在差异:

  • UUID: 高度唯一性,但较长且无序。
  • Snowflake 雪花算法: 唯一性高,有序,且相对较短。

掘金文章 ID 的生成机制

掘金采用 Snowflake 雪花算法来生成文章 ID。这种算法由 Twitter 开发,利用时间戳、机器 ID 和序列号生成全局唯一且有序的 ID。掘金使用这些 ID 作为文章的主键,确保文章的唯一性和按时间顺序的排列。

Snowflake 雪花算法的优势

  • 唯一性: 全局唯一,即使在分布式系统中也是如此。
  • 有序性: 按时间顺序生成,适合存储时间序列数据。
  • 短小: 通常只有 64 位,存储空间占用小。
  • 易于实现: 实现相对简单,易于集成。

Snowflake 雪花算法的不足

  • 存储空间: 较长的 ID 可能会占用更多的存储空间。
  • 计算资源: 生成 ID 需要额外的计算资源,可能影响系统性能。

代码示例:使用 Java 生成 Snowflake ID

import java.util.UUID;

public class SnowflakeIdGenerator {

    private static final long EPOCH = 1420070400000L;
    private static final long DATA_CENTER_ID_BITS = 5L;
    private static final long WORKER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS;
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS + DATA_CENTER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + DATA_CENTER_ID_BITS + WORKER_ID_BITS;

    private static final long DATA_CENTER_ID_MAX = ~(-1L << DATA_CENTER_ID_BITS);
    private static final long WORKER_ID_MAX = ~(-1L << WORKER_ID_BITS);
    private static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BITS);

    private final long dataCenterId;
    private final long workerId;
    private long sequence;
    private long lastTimestamp;

    public SnowflakeIdGenerator(long dataCenterId, long workerId) {
        if (dataCenterId > DATA_CENTER_ID_MAX || dataCenterId < 0) {
            throw new IllegalArgumentException("dataCenterId must be between 0 and " + DATA_CENTER_ID_MAX);
        }
        if (workerId > WORKER_ID_MAX || workerId < 0) {
            throw new IllegalArgumentException("workerId must be between 0 and " + WORKER_ID_MAX);
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
        this.sequence = 0L;
        this.lastTimestamp = -1L;
    }

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

        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟被调整过,故抛出异常
        if (timestamp < lastTimestamp) {
            throw new IllegalStateException("Clock moved backwards.");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & SEQUENCE_MAX;
            // 序列号溢出
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 如果时间戳改变,则将序列号重置为0
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence;
    }

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

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

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

结论

Snowflake 雪花算法在掘金文章 ID 的生成中扮演着至关重要的角色。它提供了高唯一性、有序性和相对较短的 ID,确保文章数据的可靠性和可管理性。随着数据库技术的不断发展,主键的选择和生成算法将继续发挥关键作用,为数据管理提供高效和可靠的基础。

常见问题解答

  1. UUID 和 Snowflake 雪花算法哪个更好?

在唯一性、有序性和存储空间等方面,Snowflake 雪花算法略胜一筹。

  1. 主键在数据库中的作用是什么?

主键唯一标识每一行数据,确保数据的完整性和可管理性。

  1. Snowflake 雪花算法是如何工作的?

它将 ID 划分为时间戳、机器 ID 和序列号,并通过组合它们来生成唯一 ID。

  1. 掘金为什么选择 Snowflake 雪花算法?

它提供了高唯一性、有序性和较短的 ID,满足掘金文章管理的需求。

  1. 如何使用 Snowflake 雪花算法生成 ID?

您可以使用提供的 Java 代码示例,或使用其他可用的实现。