# snowflake **Repository Path**: jobim/snowflake ## Basic Information - **Project Name**: snowflake - **Description**: 生成雪花算法服务 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-01-19 - **Last Updated**: 2022-01-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: 雪花算法 ## README # 雪花算法(SnowFlake) > 雪花算法:雪花算法是解决分布式id的一个高效的方案。 **SnowFlake算法生成id的结果是一个64bit大小的整数** ![image-20220119105611224](https://gitee.com/jobim/blogimage/raw/master/img/20220119105611.png) * **1bit**,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。 * **41bit-时间戳,用来记录时间戳,毫秒级** * 41位可以表示**2^41^-1**个数字, * 如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:**0 至 2^41^-1**,减1是因为可表示的数值范围是从0开始算的,而不是1。 * 也就是说41位可以表示**2^41^-1**个毫秒的值,转化成单位年则是**(2^41^-1)/(1000 * 60 * 60 * 24 * 365) = 69**年 * **10bit-工作机器id**,用来记录工作机器id。 * 可以部署在**2^10^ = 1024** 个节点,包括5位datacenterId和5位workerId * 5位(bit)可以表示的最大正整数是**2^5^ - 1 = 31**,即可以用0、1、2、3、....31这32个数字,来表示不同的datecenterId或workerId * **12bit-序列号**,序列号,用来记录同毫秒内产生的不同id。 * 12位(bit)可以表示的最大正整数是**2^12^ - 1 = 4095**,即可以用0、1、2、3、....4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。 **Java算法实现:** ```java public class SnowflakeIdWorker { // ==============================Fields================== /** 开始时间截 (2019-08-06) */ private final long twepoch = 1565020800000L; /** 机器id所占的位数 */ private final long workerIdBits = 5L; /** 数据标识id所占的位数 */ private final long datacenterIdBits = 5L; /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** 支持的最大数据标识id,结果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** 序列在id中占的位数 */ private final long sequenceBits = 12L; /** 机器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** 数据标识id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** 时间截向左移22位(5+5+12) */ 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; //==============================Constructors==================== /** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */ public SnowflakeIdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } 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; } // ==============================Methods================================= /** * 获得下一个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) { sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** 测试 */ public static void main(String[] args) { SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); for (int i = 0; i < 10; i++) { long id = idWorker.nextId(); System.out.println(Long.toBinaryString(id)); System.out.println(id); } } } ``` **获取雪花id的服务代码(利用hutool工具包):**[https://gitee.com/jobim/snowflake](https://gitee.com/jobim/snowflake)