雪花算法生成主键
2021/8/20 22:05:43
本文主要是介绍雪花算法生成主键,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
在分布式系统中,为了保证数据的主键全局唯一且自增,可以使用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位,在与前端交互时精度会丢失,也有解决办法。
这篇关于雪花算法生成主键的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-29RocketMQ底层原理资料详解:新手入门教程
- 2024-11-29RocketMQ源码资料解析与入门教程
- 2024-11-29[开源]6.1K star!这款电视直播源神器真的太赞啦!
- 2024-11-29HTTP压缩入门教程:轻松提升网页加载速度
- 2024-11-29JWT开发入门指南
- 2024-11-28知识管理革命:文档软件的新玩法了解一下!
- 2024-11-28低代码应用课程:新手入门全攻略
- 2024-11-28哪些办公软件适合团队协作,且能够清晰记录每个阶段的工作进展?
- 2024-11-28全栈低代码开发课程:零基础入门到初级实战
- 2024-11-28拖动排序课程:轻松掌握课程拖动排序功能