Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1)。

  那么一个Long类型的64个比特,

  twitter是这样分配的:正数位(占1比特)+时间戳(占41比特)+机械id(占5比特)+数据中心(占5比特)+自增值(占12比特),总共64比特组成的一个Long类型。

  时间戳(占41个比特):毫秒数,大约可以使使用69年

  机械id(占5个比特):即2的5次方等于32个机器

  数据中心id(占5个比特):即2的5次方等于32个数据中心

  自增值(占12比特):2的12次方等于4096。也就是说每毫秒最多可以生成4096个id,如果cpu生产id的速度大于每毫秒4096个,那么需要使线程进行等待到下一毫秒,重新计数获取自增值。

  snowflake算法的好处:

  # 生成的id是一个数字的Long类型

  # 无需链接数据库或者redis,超高性能。

  snowflake算法的弊端:

  # 每毫秒只能生成4096个id。随着cpu不断的进步,每毫秒4096个id将不能满足。可以不用担心,即便cpu性能超过了这个值,那么只需等待到下一个毫秒

  # 只能使用69年

  #每毫秒重新计数,空闲时间会浪费很多id空间。

  #系统时间不可回退,回退将会导致id重复。另:系统时间可以前进,不受影响。

  以上就是对snowflake的一些总结。

  snowflake算法改进1:

  针对空闲时间会浪费很多id空间,改进:咱们可以把时间戳的单位改为秒。使用31个比特的时间戳(秒),节约了10个比特,2的31次方等于2,147,483,648秒,约为69年。然后我们把节约出来的10个字节交给自增值,此时自增值(12+10=22比特),即2的22次方等于4,194,304。

  改进前的snowflake算法结构为:正数位(占1比特)+时间戳(占41比特)+机械id(占5比特)+数据中心(占5比特)+自增值(占12比特)

  改进后的snowflake算法结构为:正数位(占1比特)+时间戳(占31比特)+机械id(占5比特)+数据中心(占5比特)+自增值(占22比特)

  改进后的优点:# 避免空闲时间会浪费很多id空间,支持每秒生成419万个id。

  改进后的snowflake算法同样是使用69年,时间戳以秒为单位,每秒支持约419万个id生成。此时避免使用毫秒时间戳的浪费id空间的弊端。当然还可以继续改进,比如:使用分钟为单位的时间戳(要注意的是:使用分钟为单位的时间戳,如果服务器宕机,那么你需要等待1分钟后才能启动服务器,否则将会导致自增值归零重新计数,当前分钟内生成的id和宕机时生成的id会重复)。

  代码如下:

  /**

  * Twitter_Snowflake

  * SnowFlake的结构如下(每部分用-分开):

  * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

  * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

  * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)

  * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

  * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId

  * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

  * 加起来刚好64位,为一个Long型。

  * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

  */

  public class SnowflakeIdWorker {

  /** 开始时间截 (2015-01-01) */

  private final long twepoch = 1420041600000L;

  /** 机器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;

  //毫秒内序列溢出无锡妇科医院哪家好 http://mobile.xasgyy.net/

  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 < 1000; i++) {

  long id = idWorker.nextId();

  System.out.println(Long.toBinaryString(id));

  System.out.println(id);

  System.out.println(id);

  }

  }

  }

Twitter分布式自增ID算法snowflake原理解析(Long类型)的更多相关文章

  1. Twitter分布式自增ID算法snowflake原理解析

    以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...

  2. 详解Twitter开源分布式自增ID算法snowflake(附演算验证过程)

    详解Twitter开源分布式自增ID算法snowflake,附演算验证过程 2017年01月22日 14:44:40 url: http://blog.csdn.net/li396864285/art ...

  3. 分布式自增ID算法-Snowflake详解

    1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...

  4. Twitter的分布式自增ID算法snowflake

    snowflake 分布式场景下获取自增id git:https://github.com/twitter/snowflake 解读: http://www.cnblogs.com/relucent/ ...

  5. 基于.NET Standard的分布式自增ID算法--Snowflake

    概述 本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法.搞.NET开发的,数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier. 那么为何还要引 ...

  6. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  7. Twitter的分布式自增ID算法snowflake(雪花算法) - C#版

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的.有些时候我们希望能使用一种简 ...

  8. 分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  9. C# 分布式自增ID算法snowflake(雪花算法)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的.有些时候我们希望能使用一种简 ...

随机推荐

  1. 第2课第4节_Java面向对象编程_多态性_P【学习笔记】

    摘要:韦东山android视频学习笔记  面向对象程序的三大特性之继承性: 1.向上转换:只能定义被子类覆写的方法,不能调用在子类中定义的方法. class Father { private int ...

  2. egg.js搭建 api设置跨域

    1.egg简述 Egg.js,为企业级框架和应用而生,是阿里开源的企业级 Node.js 框架. 2.特点 Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,团队内部采用这种方式可以减少开 ...

  3. Innodb的redo log block

  4. 杀死Linux中的defunct进程(僵尸进程)的方法指南

    杀死Linux中的defunct进程(僵尸进程)的方法指南_LINUX_操作系统_脚本之家https://www.jb51.net/LINUXjishu/457748.html 这样能看到僵尸进程.

  5. asp.net core 获取appsettings.json里的配置

    public GoodsController(IConfiguration configuration) { Configuration = configuration; UpFileDir = Co ...

  6. Eclipse新项目检出后报错第一步:导入lib中的jar包【我】

    新检出项目报错,第一步,先看项目 web-info下的 lib目录里的包是不是都添加到项目构建中了,可以全选先添加到项目构建中,看项目是否还在报错.

  7. docker安装并运行mongo

    拉镜像: [mall@VM_0_7_centos ~]$ sudo docker pull mongo:3.2 [sudo] password for mall: 3.2: Pulling from ...

  8. Pytest单元测试框架-Pytest环境安装

    unittest是python自带的单元测试框架,它封装好了一些校验返回的结果方法和一些用例执行前的初始化操作,使得单元测试易于开展,因为它的易用性,很多同学也拿它来做功能测试和接口测试,只需简单开发 ...

  9. [LeetCode] 127. Word Ladder 单词阶梯

    Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest t ...

  10. python文件夹操作

    1.遍历文件夹下所有文件2.将后缀为.DCM的文件复制到指定文件夹 import os import shutil def all_path(dirname): result = []#所有的文件 f ...