当前位置:网站首页>架构设计——ID生成器「建议收藏」
架构设计——ID生成器「建议收藏」
2022-07-02 16:18:00 【全栈程序员站长】
大家好,又见面了,我是你们的朋友全栈君。
一、分布式ID发号器
要求很明确:不同机器同一时间生成不同ip;同一机器不同时间生成不同IP;
所以根据需求,可选变量有: 机器(网卡、IP)+时间,随机数
二、Why not UUID?
UUID的实现:算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成UUID。
优势:保证唯一性;本地调用,不需要rpc
UUID的缺陷:
1.UUID较长,占用内存空间;往往用字符串表示,作为主键建立索引查询效率低,常见优化方案为“转化为两个uint64整数存储”或者“折半存储”(折半后不能保证唯一性)
2.不具有有序性,无法保证趋势递增
三、依赖DB自增可行么?
实现:自增+设定步长;8个服务节点8个实例(类似取模);
缺陷:
1.不利于服务节点扩充;服务节点固定,步长固定,难以进行水平服务节点的扩展。
2.依赖DB,对数据库造成额外压力
四.全局唯一ID生成器如何设计?考量因素有哪些
1.全局唯一;有序性(不一定连续,但趋势递增);可反解信息;
2.业务性能要求:高性能、高可用
3.接入方式:嵌入式、中心服务发布、rest发布模式
设计一 :ID数据结构
64位
1.机器ID 10位;2的10次方最多可支持1k+台机器;如果每台机器TPS为1W/s,10台服务器则可以有10W/s的TPS,基本满足大部分业务要求。
2.序列号 10位或20位;同理,计算2的20次方,每秒平均可产生百万个ID
3.时间戳 30或40位(分别对应秒级、毫秒级)-时间保证趋势递增
其他可根据业务特点,加上定制的不同生产方式(pom、rest)或版本信息
设计二 并发
为支持大量并发的网络请求,ID生成服务一般采用多线程支持,对于竞争时间和序列(time sequence)采用juc中ReentrantLock或synchronized或atomic变量支持(推荐)。
实现
1.定义id实体结构(机器+时间+种子(生成随机数))
//时间戳
private Long time;
//序列号
private Integer sequence;
//生成随机数的种子
private Integer seed;
//机器ip
private Integer clusterIp;
2.根据结构获取对应请求数据,组装实体数据(获取时间戳(毫秒或秒级)os获取本机ip、mac等数据),生成一个不重复的id
//synch保证线程安全
public synchronized long getId()
计算(左移拼装,)【时间戳-初始时间】左移两位集群ip-sequence
3.解析ip,获取对应机器ip、时间戳等信息,则右移取指定位即可
五、Twitter的snowflake算法
Twitter 利用 zookeeper 实现了一个全局ID生成的服务 Snowflake:https://github.com/twitter-archive/snowflake
- 1位符号位:
由于 long 类型在 java 中带符号的,最高位为符号位,正数为 0,负数为 1,且实际系统中所使用的ID一般都是正数,所以最高位为 0。
- 41位时间戳(毫秒级):
需要注意的是此处的 41 位时间戳并非存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 – 起始时间戳),这里的起始时间戳一般是ID生成器开始使用的时间戳,由程序来指定,所以41位毫秒时间戳最多可以使用 (1 << 41) / (1000x60x60x24x365) = 69年
。
- 10位数据机器位:
包括5位数据标识位和5位机器标识位,这10位决定了分布式系统中最多可以部署 1 << 10 = 1024
s个节点。超过这个数量,生成的ID就有可能会冲突。
- 12位毫秒内的序列:
这 12 位计数支持每个节点每毫秒(同一台机器,同一时刻)最多生成 1 << 12 = 4096个ID
加起来刚好64位,为一个Long型。
- 优点:高性能,低延迟,按时间有序,一般不会造成ID碰撞
- 缺点:需要独立的开发和部署,依赖于机器的时钟
实现:
public class IdWorker {
/**
* 起始时间戳 2017-04-01
*/
private final long epoch = 1491004800000L;
/**
* 机器ID所占的位数
*/
private final long workerIdBits = 5L;
/**
* 数据标识ID所占的位数
*/
private final long dataCenterIdBits = 5L;
/**
* 支持的最大机器ID,结果是31
*/
private final long maxWorkerId = ~(-1L << workerIdBits);
/**
* 支持的最大数据标识ID,结果是31
*/
private final long maxDataCenterId = ~(-1 << 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(12+5+5)位
*/
private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;
/**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = ~(-1L << sequenceBits);
/**
* 数据标识ID(0~31)
*/
private long dataCenterId;
/**
* 机器ID(0~31)
*/
private long workerId;
/**
* 毫秒内序列(0~4095)
*/
private long sequence;
/**
* 上次生成ID的时间戳
*/
private long lastTimestamp = -1L;
public IdWorker(long dataCenterId, long workerId) {
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}
/**
* 获得下一个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 (timestamp == lastTimestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = nextMillis(lastTimestamp);
}
} else {//时间戳改变,毫秒内序列重置
sequence = 0L;
}
lastTimestamp = timestamp;
//移位并通过按位或运算拼到一起组成64位的ID
return ((timestamp - epoch) << timestampShift) |
(dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long nextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = lastTimestamp;
}
return timestamp;
}
}
实现注意点:
1.由于存在对时进行修改,snowflake算法应对时钟回拨的做法是直接抛出异常。
2.其次是12位序列号溢出的问题,即1ms内请求生成的ID个数超过了2的12次方=4096个id。snowflake算法对此的做法是,等到下一ms再生成ID。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/148260.html原文链接:https://javaforall.cn
边栏推荐
- win10 kms activator
- [how to connect the network] Chapter 5 explore the server
- Laravel文档阅读笔记-Custom Authentication Login And Registration Using Laravel 8
- Design of the multi live architecture in different places of the king glory mall
- 辉芒微IO单片机FT60F11F-MRB
- 透过华为军团看科技之变(六):智慧公路
- 2 juillet: BitTorrent est sorti; L'acquisition du système commercial linspire; Sony Deployment PlayStation now
- Chapter 15 string localization and message Dictionary (1)
- Yingguang pmc131 SOP16 16pin eight bit MCU
- Edgenext hit a mixed punch: a lightweight architecture integrating CNN and transformer
猜你喜欢
Use Zadig to build a continuous delivery platform from 0 to 1
515. 在每个树行中找最大值
Virtual lab basic experiment tutorial -7 Polarization (1)
Deep understanding of ThreadLocal
Daily question - xiaolele changes the number
[golang | grpc] generate certificates using OpenSSL
Microsoft LDAP 配置页中输入有效的用户名及密码,microsoft ldap 配置页中输入有效的用户名
Embedded ~ introduction
What is the experience of maintaining Wanxing open source vector database
Mysql - opérations de base de la base de données
随机推荐
Experience Alibaba cloud character recognition OCR
Development and application case of pms134 scheme of Yingguang single chip microcomputer with original packaging
Longest non repeating subarray
Problems needing attention in the development and debugging of Yingguang single chip microcomputer
Finally detailed explanation
Troubleshooting ideas that can solve 80% of faults
Outsourcing for five years, abandoned
[golang | grpc] generate certificates using OpenSSL
Daily question - xiaolele changes the number
应广单片机003烧录器自定义封装使用技巧
pytorch支持32位吗?
Modbus protocol communication exception
vimium映射键
Yingguang single chip microcomputer development specification pmc131 with AD chip to detect battery voltage single chip microcomputer sop8/14
能解决 80% 故障的排查思路
easyAI笔记——机器学习
[golang | grpc] use grpc to realize simple remote call
自定义一个loading指令
基数排序的简单理解
Keras' deep learning practice -- gender classification based on vgg19 model