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 相关文章推荐
ASP知识讲座四
Oct 09 PHP
可定制的PHP缩略图生成程式(需要GD库支持)
Mar 06 PHP
从手册去理解分析PHP session机制
Jul 17 PHP
如何用phpmyadmin设置mysql数据库用户的权限
Jan 09 PHP
怎样给PHP源代码加密?PHP二进制加密与解密的解决办法
Apr 22 PHP
PHP获取时间排除周六、周日的两个方法
Jun 30 PHP
PHP加密解密函数详解
Oct 28 PHP
Yii中的relations数据关联查询及统计功能用法详解
Jul 14 PHP
php框架CodeIgniter使用redis的方法分析
Apr 13 PHP
针对PHP开发安全问题的相关总结
Mar 22 PHP
PHP设计模式概论【概念、分类、原则等】
May 01 PHP
Yii框架安装简明教程
May 15 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
如何将数据从文本导入到mysql
2006/10/09 PHP
php数组函数序列之each() - 获取数组当前内部指针所指向元素的键名和键值,并将指针移到下一位
2011/10/31 PHP
PHP中文分词 自动获取关键词介绍
2012/11/13 PHP
ThinkPHP中pathinfo的访问模式、路径访问模式及URL重写总结
2014/08/23 PHP
php强制更新图片缓存的方法
2015/02/11 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
jquery键盘事件介绍
2011/01/31 Javascript
JS获取文本框,下拉框,单选框的值的简单实例
2014/02/26 Javascript
jQuery之字体大小的设置方法
2014/02/27 Javascript
jQuery使用andSelf()来包含之前的选择集
2014/05/19 Javascript
JavaScript判断按钮被点击的方法
2015/12/13 Javascript
基于Bootstrap实现的下拉菜单手机端不能选择菜单项的原因附解决办法
2016/07/22 Javascript
D3.js实现折线图的方法详解
2016/09/21 Javascript
JavaScript中校验银行卡号的实现代码
2016/12/19 Javascript
简单理解Vue中的nextTick方法
2018/01/30 Javascript
vue 之 css module的使用方法
2018/12/04 Javascript
Vue CLI3.0中使用jQuery和Bootstrap的方法
2019/02/28 jQuery
微信小程序仿今日头条导航栏滚动解析
2019/08/20 Javascript
JavaScript图像放大镜效果实现方法详解
2020/06/28 Javascript
详细分析JavaScript中的深浅拷贝
2020/09/17 Javascript
Python安装Imaging报错:The _imaging C module is not installed问题解决方法
2014/08/22 Python
Python中利用xpath解析HTML的方法
2018/05/14 Python
python+pandas+时间、日期以及时间序列处理方法
2018/07/10 Python
Python如何调用JS文件中的函数
2019/08/16 Python
Pytorch实现GoogLeNet的方法
2019/08/18 Python
Python模块future用法原理详解
2020/01/20 Python
Python线程threading模块用法详解
2020/02/26 Python
匡威俄罗斯官网:Converse俄罗斯
2020/05/09 全球购物
什么是跨站脚本攻击
2014/12/11 面试题
历史专业个人求职信分享
2013/12/20 职场文书
毕业生求职自荐书范文
2014/03/27 职场文书
社会实践活动总结报告
2014/04/29 职场文书
省级优秀毕业生主要事迹
2014/05/29 职场文书
《司马光》教学反思
2016/02/22 职场文书
2016年“11.11”光棍节活动总结
2016/04/05 职场文书
redis实现共同好友的思路详解
2021/05/26 Redis