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 相关文章推荐
使用无限生命期Session的方法
Oct 09 PHP
Excel数据导入Mysql数据库的实现代码
Jun 05 PHP
php中serialize序列化与json性能测试的示例分析
Apr 27 PHP
PHP5中Cookie与 Session使用详解
Apr 30 PHP
php计算程序运行时间的简单例子分享
May 10 PHP
PHP操作文件的一些基本函数使用示例
Nov 18 PHP
php实现对两个数组进行减法操作的方法
Apr 17 PHP
详解WordPress开发中的get_post与get_posts函数使用
Jan 04 PHP
PHP实现根据数组的值进行分组的方法
Apr 20 PHP
Laravel框架模型的创建及模型对数据操作示例
May 07 PHP
通过PHP实现获取访问用户IP
May 09 PHP
PhpSpreadsheet中文文档 | Spreadsheet操作教程实例
Apr 01 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 第二节 数据类型之字符串类型
2012/04/28 PHP
PHP 9 大缓存技术总结
2015/09/17 PHP
php编译安装php-amq扩展简明教程
2016/06/25 PHP
PHP 实现人民币小写转换成大写的方法及大小写转换函数
2017/11/17 PHP
PHP+jQuery实现即点即改功能示例
2019/02/21 PHP
jQuery版Tab标签切换
2011/03/16 Javascript
JavaScript的常见兼容问题及相关解决方法(chrome/IE/firefox)
2013/12/31 Javascript
JavaScript中setFullYear()方法的使用详解
2015/06/11 Javascript
简介alert()与console.log()的不同
2015/08/26 Javascript
JavaScript中的数组遍历forEach()与map()方法以及兼容写法介绍
2016/05/19 Javascript
jQuery设置Easyui校验规则(推荐)
2016/11/21 Javascript
Ionic项目中Native Camera的使用方法
2017/06/07 Javascript
promise处理多个相互依赖的异步请求(实例讲解)
2017/08/03 Javascript
2种简单的js倒计时方式
2017/10/20 Javascript
使用js获取伪元素的content实例
2017/10/24 Javascript
parabola.js抛物线与加入购物车效果的示例代码
2017/10/25 Javascript
vue项目中使用ueditor的实例讲解
2018/03/05 Javascript
基于D3.js实现时钟效果
2018/07/17 Javascript
详解Angular6 热加载配置方案
2018/08/18 Javascript
JavaScript自动生成 年月范围 选择功能完整示例【基于jQuery插件】
2019/09/03 jQuery
js中!和!!的区别与用法
2020/05/09 Javascript
Python实现对比不同字体中的同一字符的显示效果
2015/04/23 Python
python 将print输出的内容保存到txt文件中
2018/07/17 Python
解决python中无法自动补全代码的问题
2018/12/04 Python
python离线安装外部依赖包的实现
2020/02/13 Python
keras训练曲线,混淆矩阵,CNN层输出可视化实例
2020/06/15 Python
基于python的opencv图像处理实现对斑马线的检测示例
2020/11/29 Python
巴西香水和化妆品购物网站:The Beauty Box
2019/09/03 全球购物
公司拓展活动方案
2014/02/13 职场文书
留学推荐信(中英文版)
2015/03/26 职场文书
党员公开承诺书(2016最新版)
2016/03/24 职场文书
python基础学习之递归函数知识总结
2021/05/26 Python
IDEA使用SpringAssistant插件创建SpringCloud项目
2021/06/23 Java/Android
Python NumPy灰度图像的压缩原理讲解
2021/08/04 Python
python编程学习使用管道Pipe编写优化代码
2021/11/20 Python
SQL Server携程核心系统无感迁移到MySQL实战
2022/06/01 SQL Server