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 相关文章推荐
用php和MySql来与ODBC数据连接
Oct 09 PHP
PHP array_flip() 删除重复数组元素专用函数
May 16 PHP
PHP文件打开、关闭、写入的判断与执行代码
May 24 PHP
php高级编程-函数-郑阿奇
Jul 04 PHP
PHP面向对象法则
Feb 23 PHP
基于php导出到Excel或CSV的详解(附utf8、gbk 编码转换)
Jun 25 PHP
php顺序查找和二分查找示例
Mar 27 PHP
php实现文件下载功能的几个代码分享
May 10 PHP
php实现统计网站在线人数的方法
May 12 PHP
PHP实现的浏览器检查类
Apr 11 PHP
浅谈Laravel核心解读之Console内核
Dec 02 PHP
PHP的mysqli_set_charset()函数讲解
Jan 23 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
在普通HTTP上安全地传输密码
2007/07/21 PHP
使用PHP实现二分查找算法代码分享
2011/06/24 PHP
phpnow php探针环境检测代码
2014/11/04 PHP
thinkphp数据查询和遍历数组实例
2014/11/28 PHP
php实现倒计时效果
2015/12/19 PHP
yii权限控制的方法(三种方法)
2015/12/28 PHP
php 三元运算符实例详细介绍
2016/12/15 PHP
PHP的介绍以及优势详细分析
2019/09/05 PHP
在Javascript中 声明时用&quot;var&quot;与不用&quot;var&quot;的区别
2013/04/15 Javascript
JavaScript加强之自定义callback示例
2013/09/21 Javascript
使用JavaScript实现旋转的彩圈特效
2015/06/23 Javascript
jquery validate demo 基础
2015/10/29 Javascript
Angular CLI 安装和使用教程
2017/09/13 Javascript
nodejs二进制与Buffer的介绍与使用
2019/07/11 NodeJs
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
2021/01/05 Vue.js
[55:16]Mski vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
50行代码实现贪吃蛇(具体思路及代码)
2013/04/27 Python
python函数形参用法实例分析
2015/08/04 Python
Python实现的矩阵转置与矩阵相乘运算示例
2019/03/26 Python
基于python的socket实现单机五子棋到双人对战
2020/03/24 Python
django解决订单并发问题【推荐】
2019/07/31 Python
基于python实现把图片转换成素描
2019/11/13 Python
Python 实现黑客帝国中的字符雨的示例代码
2020/02/20 Python
python语音识别指南终极版(有这一篇足矣)
2020/09/09 Python
利用Python将多张图片合成视频的实现
2020/11/23 Python
详解CSS3中字体平滑处理和抗锯齿渲染
2017/03/29 HTML / CSS
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
历史系毕业生自荐信
2013/10/28 职场文书
精细化工应届生求职信
2013/11/17 职场文书
生产文员岗位职责
2014/04/05 职场文书
抵押贷款承诺书
2014/05/30 职场文书
大学生实习证明范本
2014/09/19 职场文书
六一活动主持词
2015/06/30 职场文书
Nginx源码编译安装过程记录
2021/11/17 Servers
Python中第三方库Faker的使用详解
2022/04/02 Python
win11无线投屏在哪设置? win11无线投屏功能的使用方法
2022/04/08 数码科技