
雪花算法(SnowFlake)-java
发布日期:2021-05-07 00:28:29
浏览次数:21
分类:技术文章
本文共 4675 字,大约阅读时间需要 15 分钟。
雪花算法
简介: 雪花算法 是 Twitter 开源的分布式id生成算法。使用一个64bit 的long型数字作为全局id。
优点:ID 自增,不会产生重复ID,在本地生成不会消耗网络效率高,存入数据库索引效率高。 缺点:依赖于系统时间的强一致性,如果系统时间回拨,或者改变,可能会造成生成重复id。结构图如下:

-
1bit :该bit 不使用
因为二进制中,最高位是符号位,1标识负数,0 标识 正数,生成的id 都是使用 正数标识,所以 该位 不使用,固定为 0. -
41bit - 时间戳:
41bit - 时间戳 = (当前时间戳(nowTimeMillis ) - 开始时间戳(beginTimeMillis)) 开始时间戳 :初始化的时候 是固定的,必须必nowTimeMillis 小。 41bit 位 最多可表示 (2^41 - 1 )个数字, 也就是 时间差为: (2^41 - 1 )个毫秒值,大概 69年多。 -
10bit : 包含 5 bit 数据中心 位 和 5 bit 机器位。
即:最多可部署 2^5 (32)个 机房, 每个机房 可以代表 2^5 (32)台机器。 -
12 bit:这个是用来记录同一个毫秒内产生的不同 id。
即:同一毫秒 内 最多可以生产 2^12 -1 = 4095 个不同id,超过该值,必须要等倒下一毫秒才能继续生产id。 -
最后,将 以上 四部分,通过位移 加上 或 运算 组拼成一个 64 bit 位 的数值 即可。
-
核心 算法
具体实现 如下
package com.nn.tools;/** * @ClassName Snow * @Author nn * @Date 2020/7/24 10:33 */public class MySnowFlake { /** * 开始时间 */ private long beginTimeMillis = 1585644268888L; /** * 上一次 生产id 的时间 */ private long lastTimeMillis = -1L; /** * 当前时间 */ private long nowTimeMillis; /** * 机房id */ private long machineRoomId; /** * 机房 id 所占 位数(总 64位) */ private long machineRoomIdBits = 5L; /** * 最大 支持机房id值 即:2^5 -1 = 31 (0-31) */ private long maxMachineRoomId = -1L ^ (-1L << machineRoomIdBits); /** * 机器id */ private long machineId; /** * 机器id 所占 位数(总 64位) */ private long machineIdBits = 5L; /** * 最大 支持机器id值 即:2^5 -1 = 31 (0-31) */ private long maxMachineId = -1L ^ (-1L << machineRoomIdBits); /** * 一毫秒内 最多允许生成id 个数 所占位数 */ private long sequenceBits = 12L; /** * 一毫秒内 最多允许生成id 个数 * (2^12 - 1) = 4095 , 一毫秒内 最多允许生产 4095 个id * 超过该值时 进入等待,下一毫秒才可以继续生产 * -1L ---> 2进制为:1...(62位)...1 64位都为1 * -1L ^ (-1L << sequenceBits) = (2^12 - 1) = 4095 */ private long MaxSequenceNumInOneMs = -1L ^ (-1L << sequenceBits); /** * 代表一毫秒内生成的最新序号 */ private long sequenceNum; /** * 时间戳部分 需要左移 的位数;42 */ private long timeLeftBits = machineRoomIdBits + machineIdBits + sequenceBits; /** * 机房部分 需要左移 的位数;17 */ private long machineRoomLeftBits = machineIdBits + sequenceBits; /** * 机器部分 需要左移 的位数;12 */ private long machineLeftBits = sequenceBits; public MySnowFlake(long machineRoomId, long machineId) { /** * 判断 输入的 machineRoomId 是否在[0-31]范围内 */ if (machineRoomId > maxMachineRoomId || machineRoomId < 0) { throw new IllegalArgumentException( String.format("worker Id can't be greater than %d or less than 0", machineRoomId)); } /** * 判断 输入的 machineId 是否在[0-31]范围内 */ if (machineId > maxMachineId || machineId < 0) { throw new IllegalArgumentException( String.format("datacenter Id can't be greater than %d or less than 0", machineId)); } this.machineRoomId = machineRoomId; this.machineId = machineId; } public synchronized long produceNextId() { /** * 获取 当前 时间戳 */ nowTimeMillis = getNowTimeStamp(); if (lastTimeMillis == nowTimeMillis) { /** * MaxSequenceNumInOneMs:sequenceNum 的 最大值 4095(2^12 - 1)其二进制为:0000 1111 1111 1111 * 当 sequenceNum = 4095 时(0000 1111 1111 1111) 加 1 得到-->2^12 (0001 0000 0000 0000) * 0001 0000 0000 0000 & 0000 1111 1111 1111 = 0 * * 即,当 sequenceNum = 0 的时候 就不能继续生产id,需要等待下一毫秒才可以继续生产id,即进入等待。 */ sequenceNum = (sequenceNum + 1) & MaxSequenceNumInOneMs; /** * 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID */ if (sequenceNum == 0) { nowTimeMillis = getNowTimeStamp(); while (nowTimeMillis == lastTimeMillis) { nowTimeMillis = getNowTimeStamp(); } } } else { sequenceNum = 0; } lastTimeMillis = nowTimeMillis; /** * id = 时间戳部分(41位) | 机房id(5位) | 机器id(5位) | 1 毫秒内 id序列号(12位) = 64 二进制位 值 */ long a = ((nowTimeMillis - beginTimeMillis) << timeLeftBits) | (machineRoomId << machineRoomLeftBits) | (machineId << machineLeftBits) | sequenceNum; return a; } /** * 获取当前时间戳 * * @return TimeMillis long */ private long getNowTimeStamp() { return System.currentTimeMillis(); } public static void main(String[] args) { MySnowFlake snow = new MySnowFlake(1, 1); for (int i = 0; i < 10000; i++) { long l = snow.produceNextId(); System.out.println(l); } }}
发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2025年03月19日 14时51分46秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Hibernate的查询方式——(2)对象导航查询
2019-03-04
剑指 offer之两个链表的第一个公共结点_java
2019-03-04
剑指 offer之二进制中1的个数_java
2019-03-04
排序算法
2019-03-04
Cookie案例(判断是否首次访问)
2019-03-04
MySQL.数据处理(数据的插入)
2019-03-04
超炫粒子漩涡
2019-03-04
HTML特效代码大全
2019-03-04
Java爬虫.HttpClient
2019-03-04
网页的基本页面实现 ---- 标签
2019-03-04
Java.数组算法(补充)
2019-03-04
Java.常用类.StringBuffer和StringBuilder
2019-03-04
RDD行动操作算子 --- fold(初始值)、reduce
2019-03-04
【Python数据分析与处理 实训02】 ---2012欧洲杯信息分析(数据过滤与排序)
2019-03-04
KeyError: “[‘xxxx‘] not found in axis“
2019-03-04
【Python数据分析与处理 实训05】--- 探索虚拟姓名数据(数据合并)
2019-03-04
java编程常见类型题 --- 面向对象编程、程序逻辑(金字塔)、多线程同步
2019-03-04