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.01发布
Oct 09 PHP
PHP5中的this,self和parent关键字详解教程
Mar 19 PHP
phpmyadmin 常用选项设置详解版
Mar 07 PHP
PHP Memcached + APC + 文件缓存封装实现代码
Mar 11 PHP
PHP关联链接常用代码
Nov 05 PHP
PHP英文字母大小写转换函数小结
May 03 PHP
帝国cms目录结构分享
Jul 06 PHP
PHP程序中使用adodb连接不同数据库的代码实例
Dec 19 PHP
10个对初学者非常有用的PHP技巧
Apr 06 PHP
yii2 modal弹窗之ActiveForm ajax表单异步验证
Jun 13 PHP
PHP+AJAX 投票器功能
Nov 11 PHP
PHP strripos函数用法总结
Feb 11 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
phpwind中的数据库操作类
2007/01/02 PHP
php获取数组元素中头一个数组元素值的实现方法
2014/12/20 PHP
php中异常处理方法小结
2015/01/09 PHP
php的扩展写法总结
2019/05/14 PHP
arguments对象
2006/11/20 Javascript
ext for eclipse插件安装方法
2008/04/27 Javascript
JQuery中的ready函数冲突的解决方法
2010/05/17 Javascript
离开页面时检测表单元素是否被修改,提示保存的js代码
2010/08/25 Javascript
通过jQuery源码学习javascript(一)
2012/12/27 Javascript
解析Javascript中大括号“{}”的多义性
2013/12/02 Javascript
JS自定义对象实现Java中Map对象功能的方法
2015/01/20 Javascript
jquery计算鼠标和指定元素之间距离的方法
2015/06/26 Javascript
js实现汉字排序的方法
2015/07/23 Javascript
Vue.js系列之vue-router(上)(3)
2017/01/03 Javascript
js实现股票实时刷新数据案例
2017/05/14 Javascript
微信小程序开发之toast等弹框提示使用教程
2017/06/08 Javascript
基于Vue生产环境部署详解
2017/09/15 Javascript
用p5.js制作烟花特效的示例代码
2018/03/21 Javascript
vue基础之模板和过滤器用法实例分析
2019/03/12 Javascript
详解关于JSON.parse()和JSON.stringify()的性能小测试
2019/03/14 Javascript
javascript验证form表单数据的案例详解
2019/03/25 Javascript
解决小程序无法触发SESSION问题
2020/02/03 Javascript
在vue项目中引用Antv G2,以饼图为例讲解
2020/10/28 Javascript
Ruby元编程基础学习笔记整理
2016/07/02 Python
Python中模块string.py详解
2017/03/12 Python
Python3 Tkinter选择路径功能的实现方法
2019/06/14 Python
手把手教你Python yLab的绘制折线图的画法
2019/10/23 Python
python分布式爬虫中消息队列知识点详解
2020/11/26 Python
Html5适配iphoneX刘海屏的简单实现
2019/04/09 HTML / CSS
小学校园活动策划
2014/01/30 职场文书
电视购物广告词
2014/03/19 职场文书
大学生职业生涯规划书汇总
2014/03/20 职场文书
考试作弊检讨书
2015/01/27 职场文书
超级礼物观后感
2015/06/15 职场文书
nginx实现动静分离的方法示例
2021/11/07 Servers
js判断两个数组相等的5种方法
2022/05/06 Javascript