Java实现高效雪花算法生成唯一ID

一、雪花算法介绍

雪花算法是Twitter公司开源的一种用于生成全局唯一ID的算法,它可以保证在分布式的情况下生成唯一ID,解决了传统的递增ID在分布式场景下可能重复的问题。

雪花算法生成的ID是一个64位的长整型,由以下几部分构成:

  1. 时间戳
  2. 机器ID
  3. 序列号

其中,时间戳占用41位,可以表示的时间范围为2^41/(1000*60*60*24*365),大约为69年。机器ID占用10位,可以表示的最大机器数为2^10-1,即1023个。序列号占用12位,可以表示的最大序号数为2^12-1,即4095个。

二、Java实现雪花算法

1、时间戳

首先,我们需要获取当前时间的时间戳。Java中可以使用System.currentTimeMillis()方法获取当前时间戳,不过它只能精确到毫秒级别,为了让我们的ID更加唯一,我们可以使用System.nanoTime()方法获取更加精确的时间戳,精确到纳秒级别。


/**
 * 获取当前时间戳,精确到纳秒级别
 * @return 当前时间戳(毫秒+纳秒)
 */
private static long getCurrentTimestamp() {
    long timestamp = System.nanoTime();
    return (timestamp - START_TIMESTAMP) / 1000000;
}

其中,START_TIMESTAMP是一个固定时间戳,用于计算时间戳的相对值。由于时间戳只占用了41位,所以时间戳的范围是有限的,为了避免时间戳满足之后,冲突概率变高的问题,我们可以将START_TIMESTAMP设置为一个固定的值,比如2018年1月1日。

2、机器ID

雪花算法中,机器ID是用于指示不同机器的一个标识符。在分布式系统中,不同的机器需要有不同的机器ID。可以通过配置文件等方式来获取机器ID,也可以使用网卡MAC地址等实际唯一的标识符来获取。


/**
 * 获取机器ID,可以根据实际情况进行获取
 * @return 机器ID
 */
private static long getMachineId() {
    // TODO 根据实际情况获取机器ID
    return 1;
}

3、序列号

序列号用于保证同一时间内,同一台机器生成不同的ID。为了避免序列号重复,我们需要在同一毫秒内,使用不同的序列号。另外,由于序列号只占用了12位,所以最多可以有4095个序列号,为了避免序列号使用完之后,需要等待到下一毫秒,我们可以将序列号的初始值设置为一个随机数。


/**
 * 获取序列号,同一毫秒内生成不同的序列号
 * @return 序列号
 */
private static long getSequence() {
    long sequence = sequenceGenerator.getAndIncrement();
    return sequence % SEQUENCE_LIMIT;
}

其中,sequenceGenerator是一个AtomicLong类型的变量,用于产生序列号,这个变量在程序启动时,会被初始化为一个随机数,用于保证每次重启程序时,序列号不会从0开始计数。

三、Java实现雪花算法示例代码

下面是一个完整的Java实现雪花算法的示例代码:


/**
 * 雪花算法生成全局唯一ID
 */
public class SnowflakeIdGenerator {
    // 起始的时间戳:2018-01-01
    private static final long START_TIMESTAMP = 1514736000000L;
    // 机器ID所占的位数
    private static final long MACHINE_ID_BITS = 10L;
    // 序列号所占的位数
    private static final long SEQUENCE_BITS = 12L;
    // 支持的最大机器ID,结果是1023
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
    // 支持的最大序列号,结果是4095
    private static final long SEQUENCE_LIMIT = ~(-1L < MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("MachineId can't be greater than " + MAX_MACHINE_ID + " or less than 0.");
        }
    }
    
    /**
     * 生成唯一ID
     * @return 返回64位的唯一ID
     */
    public static synchronized long generateId() {
        long timestamp = getCurrentTimestamp();
        if(timestamp < 0) {
            throw new IllegalStateException("Clock moved backwards, refuse to generate id.");
        }
        
        long sequence = getSequence();
        long id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) |
                (machineId << MACHINE_ID_SHIFT) |
                sequence;
        return id;
    }
    
    /**
     * 获取当前时间戳,精确到纳秒级别
     * @return 当前时间戳(毫秒+纳秒)
     */
    private static long getCurrentTimestamp() {
        long timestamp = System.nanoTime();
        return (timestamp - START_TIMESTAMP) / 1000000;
    }
    
    /**
     * 获取机器ID,可以根据实际情况进行获取
     * @return 机器ID
     */
    private static long getMachineId() {
        // TODO 根据实际情况获取机器ID
        return 1;
    }
    
    /**
     * 获取序列号,同一毫秒内生成不同的序列号
     * @return 序列号
     */
    private static long getSequence() {
        long sequence = sequenceGenerator.getAndIncrement();
        return sequence % SEQUENCE_LIMIT;
    }
}

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

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

相关推荐

  • Java JsonPath 效率优化指南

    本篇文章将深入探讨Java JsonPath的效率问题,并提供一些优化方案。 一、JsonPath 简介 JsonPath是一个可用于从JSON数据中获取信息的库。它提供了一种DS…

    编程 2025-04-29
  • java client.getacsresponse 编译报错解决方法

    java client.getacsresponse 编译报错是Java编程过程中常见的错误,常见的原因是代码的语法错误、类库依赖问题和编译环境的配置问题。下面将从多个方面进行分析…

    编程 2025-04-29
  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • 蝴蝶优化算法Python版

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

    编程 2025-04-29
  • Java腾讯云音视频对接

    本文旨在从多个方面详细阐述Java腾讯云音视频对接,提供完整的代码示例。 一、腾讯云音视频介绍 腾讯云音视频服务(Cloud Tencent Real-Time Communica…

    编程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介绍

    本文将详细介绍Java Milvus SearchParam withoutFields的相关知识和用法。 一、什么是Java Milvus SearchParam without…

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

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

    编程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java语言中的一个版本,于2014年3月18日发布。本文将从多个方面对Java 8中某一周的周一进行详细的阐述。 一、数组处理 Java 8新特性之一是Stream…

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

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

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29

发表回复

登录后才能评论