PHP实现Snowflake生成分布式唯一ID的方法示例


Posted in PHP onAugust 30, 2020

前言

Twitter 的 snowflake 在分布式生成唯一 UUID 应用还是蛮广泛的,基于 snowflake 的一些变种的算法网上也有不少。使用 snowflake 生成 UUID 很多都是在分布式场景下使用,我看了下网上有其中有几篇 PHP 实现的都没有考虑到线程安全。现在 PHP 有了 Swoole 的锁和协程的加持,对于我们开发线程安全和高并发模拟还是很方便的,这里用 PHP 结合 Swoole 来学习下实现最简单的 snowflake。

先来看以下 snowflake 的结构:

PHP实现Snowflake生成分布式唯一ID的方法示例

生成的数值是 64 位,分成 4 个部分:

  • 第一个 bit 为符号位,最高位为 0 表示正数
  • 第二部分 41 个 bit 用于记录生成 ID 时候的时间戳,单位为毫秒,所以该部分表示的数值范围为 2^41 - 1(69 年),它是相对于某一时间的偏移量
  • 第三部分的 10 个 bit 表示工作节点的 ID,表示数值范围为 2^10 - 1,相当于支持 1024 个节点
  • 第四部分 12 个 bit 表示每个工作节点没毫秒生成的循环自增 id,最多可以生成 2^12 -1 个 id,超出归零等待下一毫秒重新自增。
<?php

class Snowflake
{
  const EPOCH = 1543223810238;  // 起始时间戳,毫秒

  const SEQUENCE_BITS = 12;  //序号部分12位
  const SEQUENCE_MAX = -1 ^ (-1 << self::SEQUENCE_BITS); // 序号最大值

  const WORKER_BITS = 10; // 节点部分10位
  const WORKER_MAX = -1 ^ (-1 << self::WORKER_BITS); // 节点最大数值

  const TIME_SHIFT = self::WORKER_BITS + self::SEQUENCE_BITS; // 时间戳部分左偏移量
  const WORKER_SHIFT = self::SEQUENCE_BITS;  // 节点部分左偏移量

  protected $timestamp;  // 上次ID生成时间戳
  protected $workerId;  // 节点ID
  protected $sequence;  // 序号
  protected $lock;    // Swoole 互斥锁

  public function __construct($workerId)
  {
    if ($workerId < 0 || $workerId > self::WORKER_MAX) {
      trigger_error("Worker ID 超出范围");
      exit(0);
    }

    $this->timestamp = 0;
    $this->workerId = $workerId;
    $this->sequence = 0;
    $this->lock = new swoole_lock(SWOOLE_MUTEX);
  }

  /**
   * 生成ID
   * @return int
   */
  public function getId()
  {
    $this->lock->lock();  // 这里一定要记得加锁
    $now = $this->now();
    if ($this->timestamp == $now) {
      $this->sequence++;

      if ($this->sequence > self::SEQUENCE_MAX) {
        // 当前毫秒内生成的序号已经超出最大范围,等待下一毫秒重新生成
        while ($now <= $this->timestamp) {
          $now = $this->now();
        }
      }
    } else {
      $this->sequence = 0;
    }

    $this->timestamp = $now;  // 更新ID生时间戳

    $id = (($now - self::EPOCH) << self::TIME_SHIFT) | ($this->workerId << self::WORKER_SHIFT) | $this->sequence;
    $this->lock->unlock(); //解锁

    return $id;
  }

  /**
   * 获取当前毫秒
   * @return string
   */
  public function now()
  {
    return sprintf("%.0f", microtime(true) * 1000);
  }

}

其实逻辑并不复杂,解释一下代码中的位运算:

-1 ^ (-1 << self::SEQUENCE_BITS)
就是-1的二进制表示为1的补码,其实等同于 :
2**self::SEQUENCE_BITS - 1

最后部分左移后或运算:

(($now - self::EPOCH) << self::TIME_SHIFT) | ($this->workerId << self::WORKER_SHIFT) | $this->sequence;

这里主要是对除了第一位符号位以外的三个部分进行左移相应的偏移量使其归位,并通过或运算重新整合成上面 snowflake 的结构,比如我们用 3 部分 4 位来演示一下该归并操作:

0000 0000 0010  --左移0位--> 0000 0000 0010
0000 0000 0100  --左移4位--> 0000 0100 0000 --或操作-->1000 0100 0010
0000 0000 1000  --左移8位--> 1000 0000 0000

总结

到此这篇关于PHP实现Snowflake生成分布式唯一ID的文章就介绍到这了,更多相关PHP Snowflake生成分布式唯一ID内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
PHP5.2中date()函数显示时间与北京时间相差8小时的解决办法
May 28 PHP
php模块memcache和memcached区别分析
Jun 14 PHP
PHP乱码问题,UTF-8乱码常见问题小结
Apr 09 PHP
php基础教程 php内置函数实例教程
Aug 21 PHP
探讨Smarty中如何获取数组的长度以及smarty调用php函数的详解
Jun 20 PHP
PHP格式化MYSQL返回float类型的方法
Mar 30 PHP
php文件上传类完整实例
May 14 PHP
Yii2.0中的COOKIE和SESSION用法
Aug 12 PHP
详解PHP函数 strip_tags 处理字符串缺陷bug
Jun 11 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
Feb 06 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
Nov 14 PHP
PHP中类与对象功能、用法实例解读
Mar 27 PHP
Yii实现微信公众号场景二维码的方法实例
Aug 30 #PHP
Swoole源码中如何查询Websocket的连接问题详解
Aug 30 #PHP
PHP常用header头定义代码示例汇总
Aug 29 #PHP
PHP isset()及empty()用法区别详解
Aug 29 #PHP
PHP实现简单日历类编写
Aug 28 #PHP
PHP实现文件上传与下载
Aug 28 #PHP
PHP实现计算器小功能
Aug 28 #PHP
You might like
PHP中查询SQL Server或Sybase时TEXT字段被截断的解决方法
2009/03/10 PHP
编写PHP程序检查字符串中的中文字符个数的实例分享
2016/03/17 PHP
PHP+ajax实现获取新闻数据简单示例
2018/05/08 PHP
PHP获取真实IP及IP模拟方法解析
2020/11/24 PHP
FireFox与IE 下js兼容触发click事件的代码
2008/11/20 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
2012/10/11 Javascript
JS数组去重与取重的示例代码
2014/01/24 Javascript
JavaScript使用Range调色及透明度实例
2016/09/25 Javascript
JS中关于事件处理函数名后面是否带括号的问题
2016/11/16 Javascript
JavaScript中Promise的使用详解
2017/02/26 Javascript
详解nodejs微信公众号开发——4.自动回复各种消息
2017/04/11 NodeJs
Vue动态控制input的disabled属性的方法
2018/06/26 Javascript
js实现下拉框二级联动
2018/12/04 Javascript
浅谈Javascript常用正则表达式应用
2019/03/08 Javascript
js编写简易的计算器
2020/07/29 Javascript
[04:07]显微镜下的DOTA2第八期——英雄复活动作
2014/06/24 DOTA
python基础之包的导入和__init__.py的介绍
2018/01/08 Python
Python实现识别手写数字 Python图片读入与处理
2020/03/23 Python
Python 一键制作微信好友图片墙的方法
2019/05/16 Python
PyQt5显示GIF图片的方法
2019/06/17 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
2019/08/05 Python
使用Python测试Ping主机IP和某端口是否开放的实例
2019/12/17 Python
详解Python3 中的字符串格式化语法
2020/01/15 Python
Python如何使用paramiko模块连接linux
2020/03/18 Python
解决Jupyter无法导入已安装的 module问题
2020/04/17 Python
Python virtualenv虚拟环境实现过程解析
2020/04/18 Python
python对接ihuyi实现短信验证码发送
2020/05/10 Python
python中线程和进程有何区别
2020/06/17 Python
采用专利算法搜索最廉价的机票:CheapAir
2016/09/10 全球购物
俄罗斯优惠券网站:BIGLION
2017/05/21 全球购物
捷克街头、运动和滑板一站式商店:BoardStar.cz
2019/10/06 全球购物
如何利用cmp命令比较文件
2016/04/11 面试题
大学生求职工作的自我评价
2014/02/13 职场文书
寻衅滋事罪辩护词
2015/05/21 职场文书
高中军训感想
2015/08/07 职场文书
靠谱准确的求职信
2019/04/02 职场文书