zoukankan      html  css  js  c++  java
  • 雪花算法生成主键

    在分布式系统中,为了保证数据的主键全局唯一且自增,可以使用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位,在与前端交互时精度会丢失,也有解决办法

    就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !
  • 相关阅读:
    Poj 3318 Matrix Multiplication( 矩阵压缩)
    Altium Designer PCB的时候 高亮显示引脚连线
    历次PCB板修改意见汇总
    贴片电阻有哪几类封装尺寸?
    AD10中创建材料清单(BOM表)
    深度优先搜索(2)
    AD中测量两点之间的距离
    AD中的library中有些文件的后缀有.intlib .schlib .pcblib 这些都是库文件,但有什么区别呢?
    1.TwoSum
    深度优先搜索
  • 原文地址:https://www.cnblogs.com/zys2019/p/15163982.html
Copyright © 2011-2022 走看看