当前位置: 首页 > news >正文

雪花算法详解及源码分析

雪花算法的简介:

雪花算法用来实现全局唯一ID的业务主键,解决分库分表之后主键的唯一性问题,所以就单从全局唯一性来说,其实有很多的解决方法,比如说UUID、数据库的全局表的自增ID

但是在实际的开发过程中,我们的id除了唯一性以外,还需要去满足有序递增,高性能,高可用,以及需要时间戳等这样一些特征,而雪花算法就是一个比较符合这个一类特征的全局唯一算法。

雪花算法结构的详解:

它是一个通过64个bit位 组成的一个long类型的数字,可以将它分为四个部分,根据这四个部分的规则,生成对应的bit位的一个数据,然后组装在一起,形成一个全局的唯一id。

第一部分:是一个bit:这个是正负号,正常情况下为零,通常无意义

1)不用 1bit:是不用的

因为二进制里第一个bit位如果是1,那么都是复数,但是我们生成的id都是正数,所以第一个bit统一都是0

第二部分:是41个bit:表示的是时间戳

2)时间戳 41bit:表示的是时间戳,单位是毫秒

41bit表示的数字多达2^41-1,也就是可以标识2^41-1个毫秒值,换算成年表示就是69年的时间。

第三、四部分:是5+5个bit:表示的是机房id以及机器id、

3)+4)工作机器Id 10bit:记录工作机器的id,表示的是这个服务最多可以部署在2^10台机器上,也就是1024台机器。

但是10bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2^个机房(32个机房),每个机房可以代表2^5和机器(32台机器),也可以根据实际情况确定

第五部分:是12个bit:表示的序号,就是某个机房中某个机器上这一毫秒内同时生成的id的序号,0000 0000 0000

12bit可以代表的最大正整数是2^12-1=4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id。

源码:

public class SnowFlakeUtil01 {// 起始时间戳 (可以自定义)private final long twepoch = 1288834974657L;// 机器ID所占的位数private final long workerIdBits = 5L;// 数据中心ID所占的位数private final long datacenterIdBits = 5L;// 支持的最大机器ID,结果是31 (这个移位算法可以计算最大值:-1L ^ (-1L << workerIdBits))private final long maxWorkerId = -1L ^ (-1L << workerIdBits);// 支持的最大数据中心ID,结果是31private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 序列在ID中占的位数private final long sequenceBits = 12L;// 机器ID左移位数private final long workerIdShift = sequenceBits;// 数据中心ID左移位数private final long datacenterIdShift = sequenceBits + workerIdBits;// 时间戳左移位数private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;// 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)private final long sequenceMask = -1L ^ (-1L << sequenceBits);// 工作机器ID(0~31)private long workerId;// 数据中心ID(0~31)private long datacenterId;// 毫秒内序列(0~4095)private long sequence = 0L;// 上次生成ID的时间戳private long lastTimestamp = -1L;// 构造函数public SnowFlakeUtil01(long workerId, long datacenterId) {// 检查workerId是否在合法范围内if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}// 检查datacenterId是否在合法范围内if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获得下一个ID (该方法是线程安全的)* @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}// 如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {// 如果毫秒相同,则从0递增生成序列号sequence = (sequence + 1) & sequenceMask;// 毫秒内序列溢出if (sequence == 0) {// 阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}// 时间戳改变,毫秒内序列重置else {sequence = 0L;}// 上次生成ID的时间戳lastTimestamp = timestamp;// 移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) // 时间戳部分| (datacenterId << datacenterIdShift)       // 数据中心部分| (workerId << workerIdShift)               // 机器ID部分| sequence;                                 // 序列号部分}// 阻塞到下一个毫秒,直到获得新的时间戳protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}// 返回当前时间,以毫秒为单位protected long timeGen() {return System.currentTimeMillis();}//    public static void main(String[] args) {
//        SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil(0, 0);
//        for (int i = 0; i < 100; i++) {
//            long id = snowFlakeUtil.nextId();
//            System.out.println(id);
//        }
//    }
}

相关文章:

  • 爬虫面试手册
  • HTML基本元素包含HTML表单验证
  • 自友科技破解走班教育排课难题
  • 尚品汇项目
  • c++与c
  • 云原生和“可移植性”到底意味着什么
  • @vue-office/excel 解决移动端预览excel文件触发软键盘
  • docker命令 docker ps -l 命令在 Docker 中用于列出最近一次创建的容器
  • Vue3生命周期
  • 【深度学习】温故而知新4-手写体识别-多层感知机+CNN网络-完整代码-可运行
  • 没有知网资源如何快速下载知网论文
  • 号称超级增程电动,领克07EM-P带来技术变革?
  • 【Linux内核-编程指南】
  • 小程序集arcgis地图显示自定义坐标的功能实现记录!(学习笔记)
  • 云原生架构案例分析_3.某快递公司核心业务系统云原生改造
  • [笔记] php常见简单功能及函数
  • [译] 怎样写一个基础的编译器
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • canvas 绘制双线技巧
  • git 常用命令
  • Idea+maven+scala构建包并在spark on yarn 运行
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • js递归,无限分级树形折叠菜单
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • Spring Boot快速入门(一):Hello Spring Boot
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • springboot_database项目介绍
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 从tcpdump抓包看TCP/IP协议
  • 复杂数据处理
  • 跨域
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • # include “ “ 和 # include < >两者的区别
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • (PADS学习)第二章:原理图绘制 第一部分
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (篇九)MySQL常用内置函数
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (转)Unity3DUnity3D在android下调试
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .NET HttpWebRequest、WebClient、HttpClient
  • .net SqlSugarHelper
  • .NET分布式缓存Memcached从入门到实战
  • .net连接MySQL的方法
  • .Net实现SCrypt Hash加密
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • .Net语言中的StringBuilder:入门到精通
  • @EventListener注解使用说明