1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 雪花算法之唯一ID生成器理解

雪花算法之唯一ID生成器理解

时间:2023-01-19 06:18:51

相关推荐

雪花算法之唯一ID生成器理解

雪花算法基本情况

雪花算法是一个分布式的唯一ID生成器。它应该具有高并发,以及高性能优点。基于时间戳,ID具有有序性,同时分布式下机器间时间差异过大(类似同一台机器时间回拨,一定会重复),会导致重复ID。基于机器码和操作中心id,ID具有不可重复性。它的ID是8字节64bit的一个Long长整型数据。

ID基本组成

ID基本组成:

不用:1bit,因为最高位是符号位,0表示正,1表示负,所以这里固定为0

时间戳:41bit,服务上线的时间毫秒级的时间戳(为当前时间-服务第一次上线时间),这里为(2^41-1)/1000/60/60/24/365 = 49.7年

工作机器id:10bit,表示工作机器id,用于处理分布式部署id不重复问题,可支持2^10 = 1024个节点

序列号:12bit,用于离散同一机器同一毫秒级别生成多条Id时,可允许同一毫秒生成2^12 = 4096个Id,则一秒就可生成4096*1000 = 400w个Id

这样看,在分布式时,通过时间戳+工作机器ID+散列序列号,几乎不会重复,当然你必须保证工作机器ID的准确配置。

源码简易分析以及注意事项

我们以mybatisplus实现的雪花算法为例:

IdWorker类:

注意:DefaultIdentifierGenerator()这个必须是单例模式的,否者在多线程高并发下,会有重复ID,而且很多。详见IDENTIFIER_GENERATOR.nextId(entity)这个方法实现

package com.baomidou.mybatisplus.core.toolkit;import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.UUID;import java.util.concurrent.ThreadLocalRandom;public class IdWorker {/**单例模式,否者会多个重复Id,详见IDENTIFIER_GENERATOR.nextId(entity).longValue()*/private static IdentifierGenerator IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator();public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");public IdWorker() {}/**获取Id*/public static long getId() {return getId(new Object());}/**获取Id,最终都会走这一步*/public static long getId(Object entity) {return IDENTIFIER_GENERATOR.nextId(entity).longValue();}/**机器码和数据中心的ID,参数不能重复*/public static void initSequence(long workerId, long dataCenterId) {IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator(workerId, dataCenterId);}}

IDENTIFIER_GENERATOR.nextId(entity).longValue() 方法

nextId是一个线程安全的方法,这也是多线程唯一Id生成的必要之一。

synchronized并不会直接转成重量级锁,因为java在1.6已经优化,因此在资源上没有过分占用,是从偏向锁,轻量级锁,再到重量级锁的过程。

public synchronized long nextId() {/** long timestamp = this.timeGen();这步代码一定要注意当你DefaultIdentifierGenerator是多例时,这里获取的timestamp不具有真正的唯一性,因为多个实例一起在工作,所以写工具类时不要写成多例 */long timestamp = this.timeGen();// 这里是时间异常,针对分布式下,不同机器时间的差异过大if (timestamp < this.lastTimestamp) {long offset = this.lastTimestamp - timestamp;if (offset > 5L) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));}try {this.wait(offset << 1);timestamp = this.timeGen();if (timestamp < this.lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));}} catch (Exception var6) {throw new RuntimeException(var6);}}if (this.lastTimestamp == timestamp) {this.sequence = this.sequence + 1L & 4095L;if (this.sequence == 0L) {// 散列序列号没了,必须转到下一毫秒,采用阻塞timestamp = this.tilNextMillis(this.lastTimestamp);}} else {// 散列序列号this.sequence = ThreadLocalRandom.current().nextLong(1L, 3L);}this.lastTimestamp = timestamp;// 或运算连接运算后的时间戳,数据中心,工作中心,序列号的bit位,会被自动返回为一个长整型的数据。return timestamp - 1288834974657L << 22 | this.datacenterId << 17 | this.workerId << 12 | this.sequence;}

雪花算法很好用,让我了解了更多的东西,学习了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。