实现高效加密!打造全能雪花算法工具类

一、雪花算法简介

雪花算法是一种高效、分布式全局唯一 ID 生成器,能够生成的 ID 具有时间有序、可排序、趋势递增等特点,实现简单,生成的 ID 唯一性和稳定性得到保障,广泛应用于分布式系统中。 在对雪花算法深入了解之前,我们先来看一下一个非常基础的 ID 生成器。

public class IdGenerator {
    private static Long id = 0L;

    public static synchronized Long nextId() {
        return ++id;
    }
}

这个 IdGenerator 是简单的单机 ID 生成器,通过同步保证了 ID 的唯一性。但是,这种做法有两个问题。首先,由于 synchronized 关键字的使用,多线程场景下,性能会大打折扣;其次,由于是单机,可能出现重复 ID,当然这样的概率相对较低,但是在大量生成 ID 的场景中,也无法避免。因此,需要一种分布式的、简单的,高效的可以提升整体系统的性能和稳定性的 ID 生成器。

二、雪花算法详解

雪花算法的核心是一个 64 位的 long 型数字,其中,第 1 个 bit 为不用,接下来的 41 个 bit 用于表示时间戳,最高位为符号位不用,其中高 5 个 bit 是数据中心标识,5 个 bit 是机器标识,低 12 个 bit 是序号。

public class SnowflakeIdGenerator {
    // 数据中心ID
    private long dataCenterId;
    // 机器ID
    private long machineId;
    // 序列号
    private long sequence = 0L;
    // 上一次时间戳
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATACENTER_ID || dataCenterId  MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_ID or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long currentTimestamp = timeGen();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                currentTimestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
                | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
                | (machineId << MACHINE_ID_LEFT_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 idWorker = new SnowflakeIdGenerator(1, 1);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }
    }
}

这个 SnowflakeIdGenerator 是一个典型的 Java 实现,它具有选取数据中心 ID 和机器 ID 的功能。这个算法的核心是 nextId() 方法,是一个同步方法,主要包括四个部分。

第一部分获取当前时间戳,并检查时间戳是否被改过。

第二部分是与上一个时间戳比较,保证没有时间上的回拨,并计算序列号。

第三部分则是更新时间戳、序号。

第四部分则是拼装成 64 位 long 型 ID。

三、雪花算法优化

在实际应用中,我们可能需要对雪花算法进行迭代和优化,出于不同的业务场景,也许会有不同的优化方式。这里我们提出一些常见的优化方式。

1. 高效的防止时钟回拨策略

有时,时钟回拨是不可避免的。为了消除回拨带来的影响,我们可以采用 NTP 同步系统时钟的方式。但是在极端情况下,毫秒级别的时钟回拨的确会发生。比如说,在云服务器上,有时由于机器迁移或者其他原因时钟可能会回拨,而这种回拨比较微小和短暂,但对于雪花算法而言影响却比较大。因此,为了解决这个问题,我们需要对时钟回拨进行优化。

public synchronized long nextId() {
    long currentTimestamp = timeGen();
    if (currentTimestamp < lastTimestamp) {
        long offset = lastTimestamp - currentTimestamp;
        if (offset < MAX_BACKWARD_MS) {
            try {
                wait(offset << 1);
                currentTimestamp = timeGen();
                if (currentTimestamp < lastTimestamp) {
                    throw new RuntimeException("Clock moved backwards. Refusing to generate id");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
    }
    if (currentTimestamp == lastTimestamp) {
        sequence = (sequence + 1) & SEQUENCE_MASK;
        if (sequence == 0) {
            currentTimestamp = tilNextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = currentTimestamp;
    return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
            | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
            | (machineId << MACHINE_ID_LEFT_SHIFT)
            | sequence;
}

可以看到,在 nextId() 方法中增加了 wait() 方法,同步等待一段时间,再次获取当前时间戳,如果时间戳依然小于上次时间戳,则抛出异常。这种方式虽然可以满足单数时钟回拨带来的影响,但是多次时钟回拨,会导致异常被抛出。因此,我们基于这个思路,可以采取更加高效的方式求解。

public synchronized long nextId() {
    long currentTimestamp = timeGen();
    if (currentTimestamp < lastTimestamp) {
        long offset = lastTimestamp - currentTimestamp;
        if (offset < MAX_BACKWARD_MS) {
            currentTimestamp = lastTimestamp;
        } else {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + offset + " milliseconds");
        }
    }
    if (currentTimestamp == lastTimestamp) {
        sequence = (sequence + 1) & SEQUENCE_MASK;
        if (sequence == 0) {
            currentTimestamp = tilNextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = currentTimestamp;
    return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
            | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
            | (machineId << MACHINE_ID_LEFT_SHIFT)
            | sequence;
}

可以看到,只需要将等待的时间减少一半,直接使用 lastTimestamp 替代 currentTimestamp 即可,这样的做法显著提升了程序的性能和安全性。

2. 更为严格的数据安全性

在一些情况下,为了严格保障数据生成的唯一性,我们可能需要将 SnowflakeIdGenerator 的接口单例化,也就是说,在一个 JVM 实例上,每一个工作对象都是唯一的。

public class SnowflakeIdGenerator {
    private static volatile SnowflakeIdGenerator instance;

    public static SnowflakeIdGenerator getInstance(int dataCenterId, int machineId) {
        if (instance == null) {
            synchronized (SnowflakeIdGenerator.class) {
                if (instance == null) {
                    instance = new SnowflakeIdGenerator(dataCenterId, machineId);
                }
            }
        }
        return instance;
    }
}

这样,一旦有重复的机器标识或者数据中心标识,就会抛出运行时异常。

四、小结

雪花算法是一种高效、分布式全局唯一 ID 生成器,能够生成的 ID 具有时间有序、可排序、趋势递增等特点,实现简单,生成的 ID 唯一性和稳定性得到保障,广泛应用于分布式系统中。而在雪花算法的实现过程中,我们也可以根据不同的具体业务场景,进行一些优化和改进。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/195919.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-02 20:37
下一篇 2024-12-02 20:37

相关推荐

  • 蝴蝶优化算法Python版

    蝴蝶优化算法是一种基于仿生学的优化算法,模仿自然界中的蝴蝶进行搜索。它可以应用于多个领域的优化问题,包括数学优化、工程问题、机器学习等。本文将从多个方面对蝴蝶优化算法Python版…

    编程 2025-04-29
  • Python字典去重复工具

    使用Python语言编写字典去重复工具,可帮助用户快速去重复。 一、字典去重复工具的需求 在使用Python编写程序时,我们经常需要处理数据文件,其中包含了大量的重复数据。为了方便…

    编程 2025-04-29
  • Python实现爬楼梯算法

    本文介绍使用Python实现爬楼梯算法,该算法用于计算一个人爬n级楼梯有多少种不同的方法。 有一楼梯,小明可以一次走一步、两步或三步。请问小明爬上第 n 级楼梯有多少种不同的爬楼梯…

    编程 2025-04-29
  • AES加密解密算法的C语言实现

    AES(Advanced Encryption Standard)是一种对称加密算法,可用于对数据进行加密和解密。在本篇文章中,我们将介绍C语言中如何实现AES算法,并对实现过程进…

    编程 2025-04-29
  • Harris角点检测算法原理与实现

    本文将从多个方面对Harris角点检测算法进行详细的阐述,包括算法原理、实现步骤、代码实现等。 一、Harris角点检测算法原理 Harris角点检测算法是一种经典的计算机视觉算法…

    编程 2025-04-29
  • 数据结构与算法基础青岛大学PPT解析

    本文将从多个方面对数据结构与算法基础青岛大学PPT进行详细的阐述,包括数据类型、集合类型、排序算法、字符串匹配和动态规划等内容。通过对这些内容的解析,读者可以更好地了解数据结构与算…

    编程 2025-04-29
  • 瘦脸算法 Python 原理与实现

    本文将从多个方面详细阐述瘦脸算法 Python 实现的原理和方法,包括该算法的意义、流程、代码实现、优化等内容。 一、算法意义 随着科技的发展,瘦脸算法已经成为了人们修图中不可缺少…

    编程 2025-04-29
  • 如何通过jstack工具列出假死的java进程

    假死的java进程是指在运行过程中出现了某些问题导致进程停止响应,此时无法通过正常的方式关闭或者重启该进程。在这种情况下,我们可以借助jstack工具来获取该进程的进程号和线程号,…

    编程 2025-04-29
  • 注册表取证工具有哪些

    注册表取证是数字取证的重要分支,主要是获取计算机系统中的注册表信息,进而分析痕迹,获取重要证据。本文将以注册表取证工具为中心,从多个方面进行详细阐述。 一、注册表取证工具概述 注册…

    编程 2025-04-29
  • 神经网络BP算法原理

    本文将从多个方面对神经网络BP算法原理进行详细阐述,并给出完整的代码示例。 一、BP算法简介 BP算法是一种常用的神经网络训练算法,其全称为反向传播算法。BP算法的基本思想是通过正…

    编程 2025-04-29

发表回复

登录后才能评论