ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

雪花算法生成主键

2021-08-20 21:31:07  阅读:217  来源: 互联网

标签:final 雪花 long 主键 算法 static private id SnowflakeUtil


在分布式系统中,为了保证数据的主键全局唯一且自增,可以使用Twitter的雪花算法(SnowFlake),它可按时间趋势递增.

1)算法原理

其算法生成的ID是一个64bit大小的整数,换成long类型是19位,它的结构如下图

 从左向右进行说明:

1)第1位(1bit)表示符号位。由于一般id都是正数,故此位是0;

2)第2-42位(41bit)表示时间戳,记录的是毫秒级的时间戳,大概可以使用69年;

3)第43-53位(10bit)表示工作机器的id(5位datacenterId+5位workerId);

4)第54-64位(12bit)表示序列化号,记录同毫秒内产生的不同id

2)代码实现

package com.zys.example.util;

import java.security.SecureRandom;

public class SnowflakeUtil {
    //初始时间戳
    private static final long EPOCH_STAMP = 1262275200000L;
    //序列号id长度
    private static final long SEQUENCE_BIT = 12L;
    //数据id长度
    private static final long MACHINE_BIT = 5L;
    //工作id长度
    private static final long DATA_CENTER_BIT = 5L;
    //序列号最大值
    private static final long MAX_SEQUENCE_NUM = -1L ^ (-1L << SEQUENCE_BIT);
    //工作id需要左移的位数
    private static final long MACHINE_LEFT = SEQUENCE_BIT;
    //数据id需要左移位数
    private static final long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    //时间戳需要左移位数
    private static final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT + DATA_CENTER_BIT;
    //上次时间戳,初始值为负数
    private static long lastTimestamp = -1L;
    //机器id默认值
    private static final long MACHINE_ID = 1l;
    //数据id默认值
    private static final long DATACENTER_ID = 1l;
    //序列号默认值
    private static long sequence = 0L;

    //异步获取下一个值
    private static synchronized long getNextValue(Long machineId, long dataCenterId) throws Exception {
        String os = System.getProperty("os.name");
        SecureRandom secureRandom;
        if (os.toLowerCase().startsWith("win")) {
            // windows机器用
            secureRandom = SecureRandom.getInstanceStrong();
        } else {
            // linux机器用
            secureRandom = SecureRandom.getInstance("NativePRNGNonBlocking");
        }
        long currentTimeMillis = currentTimeMillis();
        //获取当前时间戳,如果当前时间戳小于上次时间戳,则时间戳获取出现异常
        if (currentTimeMillis < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", (lastTimestamp - currentTimeMillis)));
        }
        //如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始
        if (currentTimeMillis == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE_NUM;
            if (sequence == 0) {
                sequence = secureRandom.nextInt(Long.valueOf(SEQUENCE_BIT).intValue());
                currentTimeMillis = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = secureRandom.nextInt(Long.valueOf(SEQUENCE_BIT).intValue());
        }
        lastTimestamp = currentTimeMillis;
        long nextId = ((currentTimeMillis - EPOCH_STAMP) << TIMESTAMP_LEFT)
                | (dataCenterId << DATA_CENTER_LEFT)
                | (machineId << MACHINE_LEFT)
                | sequence;

        return nextId;
    }

    //获取时间戳,并与上次时间戳比较
    private static long tilNextMillis(long lastTimestamp) {
        long currentTimeMillis = currentTimeMillis();
        while (currentTimeMillis <= lastTimestamp) {
            currentTimeMillis = currentTimeMillis();
        }
        return currentTimeMillis;
    }

    //获取系统时间戳
    private static long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    public static synchronized long nextValue() throws Exception {
        return getNextValue(MACHINE_ID, DATACENTER_ID);
    }

    public static synchronized long nextValue(long machineId) throws Exception {
        return getNextValue(machineId, DATACENTER_ID);
    }

    public static synchronized long nextValue(long machineId, long dataCenterId) throws Exception {
        return getNextValue(machineId, dataCenterId);
    }


}

3)代码调用

public static void main(String[] args) throws Exception {
     SnowflakeUtil.nextValue();
//        SnowflakeUtil.nextValue(1);
//        SnowflakeUtil.nextValue(1, 1);
 }

以上三种方法均可,一般使用第一种即可。

4)注意实现

由于其生成的long类型19位,在与前端交互时精度会丢失,也有解决办法

 

标签:final,雪花,long,主键,算法,static,private,id,SnowflakeUtil
来源: https://www.cnblogs.com/zys2019/p/15163982.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有