snowflake ID生成器
背景
Snowflake 是 Twitter 内部的一个 ID 生算法,
可以通过一些简单的规则保证在大规模分布式情况下生成唯一的 ID 号码。
其组成为:
第一个 bit 为未使用的符号位。
第二部分由 41 位的时间戳(毫秒)构成,他的取值是当前时间相对于某一时间的偏移量。
第三部分和第四部分的 5 个 bit 位表示数据中心和机器 ID,其能表示的最大值为 2^5 -1 = 31;
最后部分由 12 个 bit 组成,其表示每个工作节点每毫秒生成的序列号 ID,同一毫秒内最多可生成 2^12 -1 即 4095 个 ID。
需要注意的是:
- 在分布式环境中,5 个 bit 位的 datacenter 和 worker 表示最多能部署 31 个数据中心,每个数据中心最多可部署 31 台节点。
41 位的二进制长度最多能表示 2^41 -1 毫秒即 69 年,所以雪花算法最多能正常使用 69 年,为了能最大限度的使用该算法,你应该为其指定一个开始时间。- 由上可知,雪花算法生成的 ID 并不能保证唯一,如当两个不同请求同一时刻进入相同的数据中心的相同节点时,而此时该节点生成的 sequence 又是相同时,就会导致生成的 ID 重复。
- 所以要想使用雪花算法生成唯一的 ID,就需要保证同一节点同一毫秒内生成的序列号是唯一的。基于此,可以有多种方式参考链接2:
RandomSequenceResolver(随机生成)
RedisSequenceResolver (基于 redis psetex 和 incrby 生成)
LaravelSequenceResolver(基于 laravel 生成)
SwooleSequenceResolver(基于 swoole_lock 锁)
不同的提供者只需要保证同一毫秒生成的序列号不同,就能得到唯一的 ID
代码
php实现
/**
- ID 生成策略
- 毫秒级时间41位+机器ID 10位+毫秒内序列12位。
- 0 1 41 46 51 63
- +-------+-----------+---------+-----------+-----------+
- |unused |timestamp |workId |machineId |sequence |
- +-------+-----------+---------+-----------+-----------+
- 1bit是 未使用的符号位
- 接着41bits是 微秒为单位的timestamp
- 接着5bits是 业务线ID
- 接着5bits是 事先配置好的机器ID
- 最后12bits是 累加计数器
- workerId (5bits) 最多只能有32个业务同时产生ID
- machineId (5bits) 最多只能有32台机器同时产生ID
- sequence (12bits) 1台机器1ms中最多产生4096个ID
*/
class Snowflake
{ const EPOCH = 1571829625238; // 起始时间戳,毫秒 const SEQUENCE_BITS = 12; // 序号部分 12位
const SEQUENCE_MAX = -1 ^ (-1 << self::SEQUENCE_BITS); // 序号最大值 const WORKER_BITS = 5; // 业务节点部分 5位
const WORKER_MAX = -1 ^ (-1 << self::WORKER_BITS); // 业务节点最大数值 const MACHINE_BITS = 5; // 机器部分 5位
const MACHINE_MAX = -1 ^ (-1 << self::MACHINE_BITS); // 机器数最大值 const TIME_SHIFT = self::WORKER_BITS + self::MACHINE_BITS + self::SEQUENCE_BITS; // 时间戳部分左偏移量
const WORKER_SHIFT = self::MACHINE_BITS + self::SEQUENCE_BITS; // 机器部分左偏移量
const MACHINE_SHIFT = self::SEQUENCE_BITS; // 业务节点部分左偏移量 protected $timestamp; // 上次ID生成时间戳
protected $workerId; // 节点ID
protected $machineId; // 机器ID
protected $sequence; // 序号 public function __construct($machineId = 1, \(workerId = 1)
{
if (\)machineId < 0 || \(machineId > self::MACHINE_MAX) {
throw new \Exception("machineId can't be greater than " .self::MACHINE_MAX. " or less than 0");
}
if (\)workerId < 0 || $workerId > self::WORKER_MAX) {
throw new \Exception("workerId can't be greater than " .self::WORKER_MAX. " or less than 0");
}$this->timestamp = 0;
$this->machineId = $machineId;
$this->workerId = $workerId;
$this->sequence = 0;
}
/**
- 生成ID
- @return int
*/
public function getId()
{
$now = \(this->getTimestampM();
if (\)this->timestamp == $now) {
\(this->sequence ++;
if (\)this->sequence > self::SEQUENCE_MAX) {
// 当前毫秒内生成的序号已经超出最大范围,等待下一毫秒重新生成
// 使用 usleep(1) 一样
while ($now <= $this->timestamp) {
$now = $this->getTimestampM();
}
}
} else {
$this->sequence = 0;
}
$this->timestamp = $now; // 更新ID生时间戳
\(id = ((\)now - self::EPOCH) << self::TIME_SHIFT) | (\(this->workerId << self::WORKER_SHIFT) | (\)this->machineId << self::MACHINE_SHIFT) | $this->sequence;
return $id;
}/**
- 返回id生成参数
- @param $id
- @return array
*/
public function restoreId($id)
{
\(binary = decbin(\)id);
return [
'timestamp' => bindec(substr(\(binary, 0, -self::TIME_SHIFT)) + self::EPOCH,
'workerId' => bindec(substr(\)binary, -self::TIME_SHIFT, self::WORKER_BITS)),
'machineId' => bindec(substr(\(binary, -self::WORKER_SHIFT, self::MACHINE_BITS)),
'sequence' => bindec(substr(\)binary, -self::SEQUENCE_BITS)),
];
}/**
- 获取当前毫秒时间戳
- @return string
*/
public function getTimestampM()
{
$time = explode(' ', microtime());
\(time2= substr(\)time[0], 2, 3);
return \(time[1].\)time2;
}
}
id的混淆
- 既然使用的是snowflake方式, 可以使用 原来总结的 进制转换的方式,转换为相应的 字符串表示方式
- 或者是 使用 hashids 现有库,hashids
补充知识
正数的二进制表示方式: 补码和原码相同
负数的二进制表示方式: 以其原码的补码形式表示
正数的补码是其二进制表示,与原码相同。
负数的补码,将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1。
-1 ^ (-1 << 4)
就是-1的二进制表示为-1的补码(其值为 位数上全是1, 11111111)
其实等同于: 2的4次方 - 1
参考链接
snowflake ID生成器的更多相关文章
- twitter的ID生成器的snowFlake算法的自造版
snowFlake算法在生成ID时特别高效,可参考:https://segmentfault.com/a/1190000011282426 SnowFlake算法生成id的结果是一个64bit大小的整 ...
- snowflake 分布式唯一ID生成器
本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 摘要: 原文参考运维生存和开源中国上的代码整理 我的环境是pytho ...
- id生成器,分布式ID自增算法(Snowflake 算法)
接口: /** * id生成器 */ public interface IdGenerator { String next(); } 实现类: /** * 分布式ID自增算法<br/> * ...
- 基于Spring Boot的可直接运行的分布式ID生成器的实现以及SnowFlake算法详解
背景 最近对snowflake比较感兴趣,就看了一些分布式唯一ID生成器(发号器)的开源项目的源码,例如百度的uid-generator,美团的leaf.大致看了一遍后感觉uid-generator代 ...
- 全局唯一ID生成器(Snowflake ID组成) 分析
Snowflake ID组成 Snowflake ID有64bits长,由以下三部分组成: time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000360024*365 ...
- 分布式的Id生成器
项目中需要一个分布式的Id生成器,twitter的Snowflake中这个既简单又高效,网上找的Java版本 package com.cqfc.id; import org.slf4j.Logger; ...
- 分布式ID生成器 zz
简介 这个是根据twitter的snowflake来写的.这里有中文的介绍. 如上图所示,一个64位ID,除了最左边的符号位不用(固定为0,以保证生成的ID都是正数),还剩余63位可用. 下面的代码与 ...
- 业务系统需要什么样的ID生成器
业务系统需要什么样的ID生成器 ID 生成器在微博我们一直叫发号器,微博就是用这样的号来存储,而我微博里讨论的时候也都是以发号器为标签.它的主要目的确如平常大家理解的“为一个分布式系统的数据objec ...
- c#分布式ID生成器
c#分布式ID生成器 简介 这个是根据twitter的snowflake来写的.这里有中文的介绍. 如上图所示,一个64位ID,除了最左边的符号位不用(固定为0,以保证生成的ID都是正数),还剩余 ...
随机推荐
- WPF,ComboBox,取汉字首字母,extBoxBase.TextChanged
1取汉字汉语拼音首字母: private static string GetFirstLetterOfChineseString(string CnChar) { long iCnChar; byte ...
- C# 学习笔记 多态(二)抽象类
多态是类的三大特性之一,抽象类又是多态的实现方法之一.抽象类是什么呢,如果把虚方法比作一个盛有纯净水的杯子,那么此时的“纯净水”就是事先定义好的方法,我们可以根据不同的需求来改变杯子中所事先盛放的是“ ...
- apache主配置文件设置
主配置文件:httpd.conf #设置管理员邮箱地址ServerAdmin admin@example.com#定义apache安装根目录变量Define SRVROOT "F:\www\ ...
- mysql_innodb存储引擎的优化
采用innodb作为存储引擎时的优化 innodb_buffer_pool_size 如果用 Innodb,那么这是一个重要变量.相对于 MyISAM 来说,Innodb对于 buffer size ...
- AtCoder abc 141 F - Xor Sum 3(线性基)
传送门 题意: 给出\(n\)个数\(a_i\),现在要将其分为两堆,使得这两堆数的异或和相加最大. 思路: 考虑线性基贪心求解. 但直接上线性基求出一组的答案是行不通的,原因之后会说. 注意到如果二 ...
- seq命令的使用
标题:seq命令的使用 作用:seq命令用于以指定增量从首数开始打印数字到尾数,即产生从某个数到另外一个数之间的所有整数,并且可以对整数的格式.宽度.分割符号进行控制 语法: [1] seq [选项] ...
- shell 字符菜单管理
1.创建一个脚本func.sh 脚本如下func2.sh #!/bin/bash function menu(){ title="My Menu" url="www.la ...
- 201871010110-李华《面向对象程序设计(java)》第十一周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- 第十二周博客作业 <西北师范大学| 周安伟>
第十二周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10831971 ...
- 搭建pikachu平台及暴力破解
一.先将Pikachu文件放在网站根目录下 二.修改pikachu网站的配置文件 inc/config.inc.php define('DBUSER', 'user'); define('DBPW' ...