雪花算法生成全局唯一ID
系统中某些场景少不了全局唯一ID的使用,来保证数据的唯一性。除了通过数据库自带的自增id来保证 id 的唯一性,通常为了保证的数据的可移植性会选择通过程序生成全局唯一 id。
百度了不少php相关的生成方式,得到的大多是随机字符串和内置的 uniqid()函数。不过经过 ab 测试,在并发情况下重复度是很高的。
偶然看到了一篇 Twitter的分布式自增ID算法 snowflake 的文章,得到的全局唯一都是纯数字。这一点对于数据库来说,在此列上创建索引并通过此字段关联查询的时候是比较好的。
需要值得注意的是:snowflake的结构是64位的,所以需要在64位的PHP上运行。可以通过 phpinfo() 查看PHP的位数。一般 Linux 上的 PHP都可以满足。
下面是相关代码:
<?php
/***
* Author:阿远
* Class SnowflakeIdWorker
*/
class SnowflakeIdWorker{
/** 开始时间截 (2018-01-01) */
const twepoch = 1514736000000;
/** 机器id所占的位数 */
const workerIdBits = 10;
//支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
const maxWorkerId = (-1 ^ (-1 << self::workerIdBits));
//序列在id中占的位数
const sequenceBits = 12;
//机器ID向左移12位
const workerIdShift = self::sequenceBits;
//时间截向左移22位(10+12)
const timestampLeftShift = self::workerIdBits + self::sequenceBits;
//序列号值的最大值,这里为4095 (0b111111111111=0xfff=4095)
const sequenceMask = (-1 ^ (-1 << self::sequenceBits));
//工作机器ID(0~1023):默认0
private $workerId = 0;
//毫秒内序列(0~4095):标识符,常驻内存
static $sequence = 0 ;
//上次生成ID的时间截
static $lastTimestamp = -1;
/***
* 构造函数:设置当前机器id
* SnowflakeIdWorker constructor.
* @param $workerId
*/
public function __construct($workerId)
{
//转换类型
$workerId = (int) $workerId;
//判断参数合法性
if($workerId < 0 || $workerId > self::maxWorkerId){
die('error...');
}
//设置当前机器id
$this->workerId = $workerId;
}
public function nextId(){
//获取当前毫秒时间戳
$timestamp = $this->timeGen();
//获取上一次生成id时的毫秒时间戳
$lastTimestamp = self::$lastTimestamp;
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if($timestamp < $lastTimestamp){
die('error...');
}
//如果是同一毫秒内生成的,则进行毫秒序列化
if($timestamp == $lastTimestamp){
//获取当前序列号值
self::$sequence = (self::$sequence + 1) & self::sequenceMask;
//毫秒序列化值溢出(就是超过了4095)
if(self::$sequence == 0){
//阻塞到下一秒,获得新的时间戳
$timestamp = $this->tilNextMillis($lastTimestamp);
}
}
//如果不是同一毫秒,那么重置毫秒序列化值
else{
self::$sequence = 0;
}
//重置上一次生成的时间戳
self::$lastTimestamp = $timestamp;
//移位并通过或运算拼到一起组成64位的ID
return
//时间戳左移 22 位
(($timestamp - self::twepoch) << self::timestampLeftShift) |
//机器id左移 12 位
($this->workerId << self::workerIdShift) |
//或运算序列号值
self::$sequence;
}
/****
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param $lastTimestamp 上次生成ID的时间截
* @return float 当前毫秒时间戳
*/
private function tilNextMillis($lastTimestamp){
//重新获取当前时间戳
$timestamp = $this->timeGen();
//如果等于上一次获取的时间戳,仍然重新获取
while($timestamp <= $lastTimestamp){
$timestamp = $this->timeGen();
}
//返回新的时间戳
return $timestamp;
}
/***
* 返回当前毫秒时间戳
* @return float
*/
private function timeGen(){
return (float)sprintf("%.0f", microtime(true) * 1000);
}
}
//调用
header("Content-Type: text/html; charset=utf-8");
//
$work1 = new SnowflakeIdWorker(1);
for($i=0; $i<10;$i++) {
echo $i."--".$work1->nextId()."<br/>";
}
参考资料:Twitter的分布式自增ID算法snowflake
雪花算法生成全局唯一ID的更多相关文章
- 如何在高并发分布式系统中生成全局唯一Id
月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1. ...
- 如何在高并发分布式系统中生成全局唯一Id(转)
http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文, ...
- (转)如何在高并发分布式系统中生成全局唯一Id
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ...
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ...
- 面试官:如何在分布式场景下生成全局唯一 ID?
在分布式系统中,有一些场景需要使用全局唯一 ID ,可以和业务场景有关,比如支付流水号,也可以和业务场景无关,比如分库分表后需要有一个全局唯一 ID,或者用作事务版本号.分布式链路追踪等等,好的全局唯 ...
- 生成全局唯一ID
在实际业务处理中,有时需要生成全局唯一ID来区别同类型的不同事物,介绍一下几种方式及其C++实现 //获取全局唯一ID //server_id为服务的id,因当同一个服务部署在多个服务器上时,需要区别 ...
- 高并发分布式系统中生成全局唯一Id汇总
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
- 游戏服务器生成全局唯一ID的几种方法
在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...
- SnowFlake 生成全局唯一id
public class SnowFlakeUtil { private long workerId; private long datacenterId; private long sequence ...
随机推荐
- Spring中Bean的作用域差别
我觉得servlet和spring交叉起来,理解得快. Bean的作用域中,prototype和singleton作用域效果不一样,前者每次都会有新的实例,而后者始终一个实例 . 所以,java.ut ...
- jQuery和CSS3炫酷button点击波特效
这是一款效果很炫酷的jQuery和CSS3炫酷button点击波特效.该特效当用户在菜单button上点击的时候.从鼠标点击的点開始,会有一道光波以改点为原点向外辐射的动画效果,很绚丽. 在线演示:h ...
- cocos2d js ClippingNode 制作标题闪亮特效
1.效果图: 之前在<Android 高仿 IOS7 IPhone 解锁 Slide To Unlock>中制作了文字上闪亮移动的效果,这次我们来看下怎样在cocos2d js 中做出类似 ...
- Git使用SSH提交代码到server出现 permission denied (publickey).
在GitBush中向已经存在的Repository提交README.md改动. 命令例如以下: touch README.md git init git add README.md git commi ...
- Android 线程 Looper.prepare()、Looper.loop() 使用
优化项目过程中发现了一个非常Low的问题,整理一下.备忘: 说问题之前先看下HandlerThread的定义 一个封装了looper的线程: Looper用于封装了android线程中的消息循环. ...
- Web实际应用中的编码问题
一. JSP页面有关编码的介绍 ---->>假设不做不论什么设置,页面默认ISO-8859-1编码(Western European). ---->><%@ page c ...
- 每日算法之二十六:Substring with Concatenation of All Words
变相的字符串匹配 给定一个字符串,然后再给定一组同样长度的单词列表,要求在字符串中查找满足下面条件的起始位置: 1)从这个位置開始包括单词列表中全部的单词.且每一个单词仅且必须出现一次. 2)在出现的 ...
- bzoj5441: [Ceoi2018]Cloud computing
跟着大佬做题.. 这题也是有够神仙了.观察一下性质,c很小而f是一个限制条件(然而我并不会心态爆炸) %了一发,就是把电脑和订单一起做背包,订单的c视为负而电脑的v为负,f由大到小排序做背包 #inc ...
- How to do IF NOT EXISTS in SQLite
http://stackoverflow.com/questions/531035/how-to-do-if-not-exists-in-sqlite How about this? INSERT O ...
- Codeforces--633D--Fibonacci-ish(暴力搜索+去重)(map)
Fibonacci-ish Time Limit: 3000MS Memory Limit: 524288KB 64bit IO Format: %I64d & %I64u Submi ...