一、概述

分布式 ID 生成算法的有很多种,Twitter 的 SnowFlake 就是其中经典的一种。

SnowFlake 算法生成 ID 的结果是一个 64bit 大小的整数,它的结构如下图:

  • 1 位,不用。二进制中最高位为 1 的都是负数,但是我们生成的 id 一般都使用整数,所以这个最高位固定是 0。
  • 41 位,用来记录时间戳(毫秒)。41 位可以表示 2^41 个数字;如果只用来表示正整数(计算机中正数包含 0),可以表示的数值范围是:0 至 2^41−1,也就是说 41 位可以表示 2^41 个毫秒的值,转化成单位年则是 2^41 / (1000 * 60 * 60 * 24 * 365) = 69年。
  • 10 位,用来记录工作机器 id,可以部署在 2^10 = 1024个节点,包括 5 位 datacenterId 和 5 位 workerId ;5 位(bit)可以表示的最大正整数是2^5-1 = 31,即可以用 0、1、2、3、....31 这 32 个数字,来表示不同的 datecenterId 或 workerId。
  • 12 位,序列号,用来记录同毫秒内产生的不同 id。12位(bit)可以表示的最大正整数是 2^12-1 =4095,即可以用 0、1、2、3、....4095 这 4096 个数字,来表示同一机器同一时间截(毫秒)内产生的 4096 个ID序号。

由于在 Java 中 64 bit 的整数是 Long 类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 Long 来存储的。

SnowFlake 可以保证:

  • 所有生成的 ID 按时间趋势递增;
  • 整个分布式系统内不会产生重复id(因为有 datacenterId 和 workerId 来做区分);

二、SnowFlake 算法的 JAVA 实现

public class SnowFlake {

    /**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L; /**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
private final static long MACHINE_BIT = 5; //机器标识占用的位数
private final static long DATACENTER_BIT = 5;//数据中心占用的位数 /**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT); /**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; private long datacenterId; //数据中心
private long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳 public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
} /**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
} if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
} lastStmp = currStmp; return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
} private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
} private long getNewstmp() {
return System.currentTimeMillis();
} public static void main(String[] args) {
SnowFlake snowFlake = new SnowFlake(2, 3); long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
System.out.println(snowFlake.nextId());
}
System.out.println(System.currentTimeMillis() - start);
}
}

三、SnowFlake 算法的生产实践

通过前面的讲述我们知道 SnowFlake 算法能产生全局唯一 ID 主要是通过 5 位 datacenterId 和 5 位 workerId 来做区分,而在生产实践中,我们常常将机器 ID(hostid)来当成 datacenterId,将机器上运行的实例ID(pid)来当成 workerId。

如今的分布式部署都在强调无状态化,那么给每台机器绑定一个 hostid 显然不太现实,假设又是在容器化环境下,没有固定的 IP,并且容器实例每次重新启动后的唯一 ID 还不一致。综上,基于机器 ID 的思路不可行。所以经常的做法是,利用 ZK/Redis/DB 等作为一个全局的 datacenterId/workerId 发号器,由发号器来分配唯一的 datacenterId/workerId 。(可以参考百度分布式唯一 ID 生成器 UidGenerator)

四、SnowFlake 算法的问题思考

1. 时间回拨问题

由于机器的时间是动态的调整的,有可能会出现时间跑到之前几毫秒,如果这个时候获取到了这种时间,则会出现数据重复。

这个问题的解决方案是采用“历史时间”。在进程启动后,我们会将当前时间(实际处理采用了延迟10ms启动),作为该业务这台机器进程的时间戳中的起始时间字段,后续的自增是在序列号自增到最大值时,时间戳增 1,而序列号重新归为 0。

2. 机器 id 分配及回收

目前机器 id 需要每台机器不一样,这样的方式分配需要有方案进行处理,同时也要考虑,如果该机器宕机了,对应的 datacenterId/workerId 分配后的回收问题。

这个问题的解决方案是:每个实例启动,扩容,直接从 ZK/Redis/DB 等发号器取一个 id 作为 datacenterId/workerId,下线不销毁;并且维护一个活动节点队列,在地址空间耗尽的时候,指针指回队列头部,当分配的 id 存在于活动节点队列则跳过取下一个可用空间,达到复用原地址空间的目的。

分布式 ID 生成算法 — SnowFlake的更多相关文章

  1. 理解分布式id生成算法SnowFlake

    理解分布式id生成算法SnowFlake https://segmentfault.com/a/1190000011282426#articleHeader2 分布式id生成算法的有很多种,Twitt ...

  2. 美团技术分享:深度解密美团的分布式ID生成算法

    本文来自美团技术团队“照东”的分享,原题<Leaf——美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息ID生成算法和生成策略 ...

  3. Twitter的SnowFlake分布式id生成算法

    二进制相关知识回顾 1.所有的数据都是以二进制的形式存储在硬盘上.对于一个字节的8位到底是什么类型 计算机是如何分辨的呢? 其实计算机并不负责判断数据类型,数据类型是程序告诉计算机该如何解释内存块. ...

  4. java 分布式id生成算法

    import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkI ...

  5. SnowFlake --- 分布式id生成算法

    转载自:https://segmentfault.com/a/1190000011282426 概述 SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进 ...

  6. SnowFlake分布式ID生成及反解析

    概述 分布式id生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种,SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进制中最高位为 ...

  7. 细聊分布式ID生成方法

    细聊分布式ID生成方法 https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=403837240&idx=1&sn=ae9 ...

  8. Leaf:美团分布式ID生成服务开源

    Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家.数学家莱布尼茨的一句话:“There are no two identical leaves in the world.”L ...

  9. 分布式系统的唯一id生成算法你了解吗?

    在分库分表之后你必然要面对的一个问题,就是id咋生成? 因为要是一个表分成多个表之后,每个表的id都是从1开始累加自增长,那肯定不对啊. 举个例子,你的订单表拆分为了1024张订单表,每个表的id都从 ...

随机推荐

  1. Java中字符串替换方法

    replaceAll方法 public String replaceAll(String regex, String replacement) replace方法 public String repl ...

  2. 推荐一款最强Python自动化神器!不用写一行代码!

    搞过自动化测试的小伙伴,相信都知道,在Web自动化测试中,有一款自动化测试神器工具: selenium.结合标准的WebDriver API来编写Python自动化脚本,可以实现解放双手,让脚本代替人 ...

  3. 一次数独生成及解题算法的剖析(Java实现)

    数独生成及解题算法剖析(Java实现) 关键词 数独9x9 数独生成算法 数独解题算法 序言 最近业务在巩固Java基础,编写了一个基于JavaFX的数独小游戏(随后放链接).写到核心部分发现平时玩的 ...

  4. 面试 16-01.MVVM

    16-01.MVVM #前言 MVVM的常见问题: 如何理解MVVM 如何实现MVVM 是否解读过Vue的源码 题目: 说一下使用 jQuery 和使用框架的区别 说一下对 MVVM 的理解 vue ...

  5. Hadoop核心-HDFS

    上一篇我们熟悉了hadoop,本篇讲解一下hadoop第一个核心HDFS. 一.概述 HDFS是一个分布式文件存储系统,以流式数据访问模式存储超大文件,将数据分块存储到一个商业硬件集群内的不同机器上, ...

  6. pytest接口测试轻松入门

    通过Postman请求结果如下图: 那我们怎么用pytest进行测试呢? 在接口测试,我们要用到requests包,实现代码如下: import pytest import allure import ...

  7. 【进程/作业管理】篇章一:Linux进程及管理(专用内存监控类工具)------【vmstat、pmap】

    主要讲解专用内存监控工具的使用:vmstat.pmap命令的使用. 命令概览: vmstat 显示虚拟内存状态 pmap 报告进程与内存映射关系 vmstat命令是最常见的Linux/Unix监控工具 ...

  8. 使用 CoreDNS 来应对 DNS 污染

    原文链接:https://fuckcloudnative.io/posts/install-coredns-on-macos/ CoreDNS 是 Golang 编写的一个插件式 DNS 服务器,是 ...

  9. flowable 实现多实例-会签-动态配置人员 参考demo

    会签 即多人执行当前任务 设置判断数 通过 例如:设置了是半数通过即可通过当前节点 如果当前是4人那就是2人即通过 如果是6人那就是三人即通过 如果是5人 即三人通过 看各位的判断值是如何书写 这个值 ...

  10. Selenium多浏览器测试

    在浏览器的兼容性测试中,会测试产品在不同浏览器上的兼容性,比较主流的浏览器有IE.Firefox.Chrome,Opera,Safari等.还有其它如360.QQ.遨游.百度等浏览器都是基于IE或者c ...