分布式ID——雪花算法
背景
随着现在业务量的越来越大,数据库的划分也变的越来越细,分库分表的理念也渐渐的落地,自增主键或者序列之类的主键id生成方式已经不再满足需求,所以分布式ID的生成就应运而生,总的来说就是生成规则更加负责,减少重复的概率。
一、雪花算法
雪花算法的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。
自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。
算法描述:
- 最高位是符号位,始终为0,不可用。
- 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
- 10位的机器标识,10位的长度最多支持部署1024个节点。
- 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
二、时间部分
时间部分的逻辑起始很简单,就是规定一个起始时间戳,然后用当前时间戳减去起始时间戳,这两个数的差就是我们要的结果,但是有一个问题如果系统的时间出错了怎么办,毕竟我们正常取的都是服务器的时间,所以需要校验
//如果当前时间小于上次ID生成的时间,说明系统回退过抛出异常if (now < LAST_TIME_STAMP) {log.info("系统时间异常,请检查!");throw new RuntimeException("系统时间异常!");}
三、机器信息部分
机器信息,占10位。我们这里把机器信息分成两部分,一部分是数据中心id,占5位,一部分是机器id,占5位。这两个id可以在部署项目的时候根据不同的机器自定义不同的id,这样能人为的保障每个id都不同。jdk库中,有api可以获取本地机器的hostname和hostaddress,可以把hostname的信息作为数据中心id,把hostaddress的信息作为机器id,如何把两个字符串改为两个数字id呢?其实很简单。获取字符串的字节数组,然后把数组的每个数字相加,对节点数的最大值取余。因为都是5位,所以最大值是31,需要对32取余。那么雪花算法可以部署的机器总数就是32*32=1024个,这是机器信息的限制。
private static int getDataId() {try {return getHostId(Inet4Address.getLocalHost().getHostName(), DATA_MAX_NUM);} catch (UnknownHostException e) {return new Random().nextInt(DATA_RANDOM);}}private static int getWorkId() {try {return getHostId(Inet4Address.getLocalHost().getHostAddress(), WORK_MAX_NUM);} catch (UnknownHostException e) {return new Random().nextInt(WORK_RANDOM);}}private static int getHostId(String str, int max) {byte[] bytes = str.getBytes();int sums = 0;for (int b : bytes) {sums += b;}return sums % (max + 1);}
四、毫秒内序列
毫秒内的序列。什么意思呢?我们在生成时间部分获取时间戳的时候,使用 long now = System.currentTimeMillis(); 获取,是个毫秒级的时间戳,但是即使是这么短的时间,对于电脑来说也足够生成很多个id,所以很多id可能会在同一个毫秒内生成,也就是时间部分的数值一样。这个时候就要让同一个毫秒内生成的id加上数字序列标识,就是第三部分的序列。第三部分占的长度是12位,转成整数值就是4095,所以最后一部分的范围就是4095到0之间的数字。如果毫秒内访问的数量超过了这个限制怎么办?没法解决,只能强制等到下一毫秒再生产id。
五、实战
上面的讲述我们基本可以写出方法了,但是考虑到多线程,我们需要加锁保证。下面是完整代码。
@Slf4j
public class SnowflakeUtil {/*** 时间部分所占长度*/private static final int TIME_LEN = 41;/*** 数据中心id所占长度*/private static final int DATA_LEN = 5;/*** 机器id所占长度*/private static final int WORK_LEN = 5;/*** 毫秒内序列所占长度*/private static final int SEQ_LEN = 12;/*** 定义起始时间 2015-01-01 00:00:00*/private static final long START_TIME = 1420041600000L;/*** 上次生成id的时间戳*/private static long LAST_TIME_STAMP = -1L;/*** 时间部分向左移动的位数 22*/private static final int TIME_LEFT_BIT = 64 - 1 - TIME_LEN;/*** 自动获取数据中心id(可以手动定义0-31之间的任意数)*/private static final long DATA_ID = getDataId();/*** 自动获取机器id(可以手动定义0-31之间的任意数)*/private static final long WORK_ID = getWorkId();/*** 数据中心id最大值 31*/private static final int DATA_MAX_NUM = ~(-1 << DATA_LEN);/*** 机器id最大值 31*/private static final int WORK_MAX_NUM = ~(-1 << WORK_LEN);/*** 随机获取数据中心id的参数 32*/private static final int DATA_RANDOM = DATA_MAX_NUM + 1;/*** 随机获取机器id的参数 32*/private static final int WORK_RANDOM = WORK_MAX_NUM + 1;/*** 数据中心id左位移数 17*/private static final int DATA_LEFT_BIT = TIME_LEFT_BIT - DATA_LEN;/*** 机器id左位移数 12*/private static final int WORK_LEFT_BIT = DATA_LEFT_BIT - WORK_LEN;/*** 上一次毫秒内序列值*/private static long LAST_SEQ = 0L;/*** 毫秒内序列的最大值 4095*/private static final long SEQ_MAX_NUM = ~(-1 << SEQ_LEN);private final Object object = new Object();public synchronized static long getId() {long now = System.currentTimeMillis();//如果当前时间小于上次ID生成的时间,说明系统回退过抛出异常if (now < LAST_TIME_STAMP) {log.info("系统时间异常,请检查!");throw new RuntimeException("系统时间异常!");}if (now == LAST_TIME_STAMP) {LAST_SEQ = (LAST_SEQ + 1) & SEQ_MAX_NUM;if (LAST_SEQ == 0) {now = nextMillis(LAST_TIME_STAMP);}} else {LAST_SEQ = 0;}LAST_TIME_STAMP = now;return ((now - START_TIME) << TIME_LEFT_BIT) | (DATA_ID << DATA_LEFT_BIT) | (WORK_ID << WORK_LEFT_BIT) | LAST_SEQ;}private static long nextMillis(Long lastMillis) {long now = System.currentTimeMillis();while (now <= lastMillis) {now = System.currentTimeMillis();}return now;}private static int getDataId() {try {return getHostId(Inet4Address.getLocalHost().getHostName(), DATA_MAX_NUM);} catch (UnknownHostException e) {return new Random().nextInt(DATA_RANDOM);}}private static int getWorkId() {try {return getHostId(Inet4Address.getLocalHost().getHostAddress(), WORK_MAX_NUM);} catch (UnknownHostException e) {return new Random().nextInt(WORK_RANDOM);}}private static int getHostId(String str, int max) {byte[] bytes = str.getBytes();int sums = 0;for (int b : bytes) {sums += b;}return sums % (max + 1);}public static void main(String[] args) {
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// try {
// long time = dateFormat.parse("2015-01-01 00:00:00").getTime();
// System.out.println(time);
// } catch (ParseException e) {
// e.printStackTrace();
// }for (int i = 0; i <10 ; i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println("-------------");System.out.println(getId());}}).start();}}
}
学习专题:雪花算法(07)雪花算法最终版
https://www.cnblogs.com/Hollson/p/9116218.html
分布式ID——雪花算法相关推荐
- 分布式ID雪花算法-解析
SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0 41位,用来记录时 ...
- 理解分布式id生成算法--雪花算法(SnowFlake)
分布式ID生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种. 注: 1B就是1个字节. Byte.KB.B.MB.GB之间的关系是: Bit--比特 : B --字节:KB-- ...
- python雪花算法生成id_理解分布式id生成算法SnowFlake
分布式id生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种. 概述 SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进制中最高位 ...
- 分布式ID生成算法——leaf算法
leaf是美团在雪花算法的基础上提出的一种分布式ID生成算法,它具有全局唯一.高可用.高并发.低延迟.接入简单(http或公司内rpc)的优点. Twitter:世界上不存在两片一样的雪花. 美团:世 ...
- 美团技术分享:深度解密美团的分布式ID生成算法
本文来自美团技术团队"照东"的分享,原题<Leaf--美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息I ...
- java怎样生成32位全是整形的主键_你肯定会需要的分布式Id生成算法雪花算法(Java)...
最近公司正好在做数据库迁移从oracle到mysql,因为之前oracle主键是使用的 SYS_GUID() 这个oracle提供的函数来生成全球唯一的标识符(原始值)由16个字节组成. 不过由于my ...
- 存数据返回他的序列号id_雪花般的分布式唯一ID雪花算法
点击上方 Java老铁,并选择 设为星标 优质文章和资料会及时送达 导读:唯一ID可以标识数据的唯一性,在分布式系统中生成唯一ID的方案有很多,常见的方式大概有以下三种 依赖数据库,使用如MySQL自 ...
- 【分布式ID】键高并发 分布式 全局唯一 ID 雪花算法 snowflake
文章目录 1.概述 2.为什么 3.要求 3.1 软件要求 3.2 机器要求 4.区别 4.1 UUID 4.2 MySQL数据库自增ID 4.3 Redis数据库自增ID 5. 雪花算法 5.1 核 ...
- java不规则算法_分布式id生成算法 snowflake 详解
背景 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在支付流水号.订单号等,随者业务数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需 ...
最新文章
- 批处理 探测在线计算机,批处理(bat)用来监测Windows网络状态脚本
- php txt,PHP规范TXT文件
- jvm在不同系统中的最大内存空间地址
- 「知识表示学习」专题论文推荐 | 每周论文清单
- 关于Qt、Qt/E、Qtopia、qvfb、framebuffer、qpe等概念的对比介绍
- java中instanceof使用详细介绍
- 【C#学习笔记】使用C#中的Dispatcher
- c语言只能最大值不能最小值,用c语言编写输入10个无序的整数,去掉一个最大值和最小值,然后求其平均值...
- leetcode-reverse words in a string
- 来自对象字段的Python字典
- 设计模式---命令模式(C++实现)
- [转]国外英语教学网页
- xp win7 linux 三系统下载,打造xp+linux+win7三系统教程.doc
- ug12对计算机配置要求,ug12.0对电脑配置要求
- 《给青年的十二封信》 朱光潜 (摘录)
- rails erb_您需要知道Rails中的erb以及如何掌握它
- 猫哥教你写爬虫 003--数据类型转换
- Redis源码阅读笔记 ----数据结构部分 sds
- 如何编写无法维护的代码
- 山寨凤凰新闻菜单效果