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 相关文章推荐
第十一节--重载
Nov 16 PHP
把1316这个数表示成两个数的和,其中一个为13的倍数,另一个是11的倍数,求这两个数。
Jun 24 PHP
PHP中extract()函数的妙用分析
Jul 11 PHP
destoon常用的安全设置概述
Jun 21 PHP
PHP中preg_match正则匹配中的/u、/i、/s含义
Apr 17 PHP
10款实用的PHP开源工具
Oct 23 PHP
在WordPress中实现发送http请求的相关函数解析
Dec 29 PHP
Zend Framework教程之Autoloading用法详解
Mar 08 PHP
yii2带搜索功能的下拉框实例详解
May 12 PHP
微信自定义分享php代码分析
Nov 24 PHP
php curl 模拟登录并获取数据实例详解
Dec 22 PHP
给大家分享几个常用的PHP函数
Jan 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
让PHP显示Facebook的粉丝数量方法
2014/01/08 PHP
PHP实现RSA签名生成订单功能【支付宝示例】
2017/06/06 PHP
浅谈PHP发送HTTP请求的几种方式
2017/07/25 PHP
Yii2框架类自动加载机制实例分析
2018/05/02 PHP
使两个iframe的高度与内容自适应,且相等
2006/11/20 Javascript
JS查看对象功能代码
2008/04/25 Javascript
把input初始值不写value的具体实现方法
2013/07/04 Javascript
深入理解JavaScript系列(47):对象创建模式(上篇)
2015/03/04 Javascript
使用Chart.js图表库制作漂亮的响应式表单
2015/10/28 Javascript
AngularJS 避繁就简的路由
2016/07/01 Javascript
通过AngularJS实现图片上传及缩略图展示示例
2017/01/03 Javascript
bootstrap datetimepicker日期插件超详细使用方法介绍
2017/02/23 Javascript
BootStrap Table 后台数据绑定、特殊列处理、排序功能
2017/05/27 Javascript
详解vue服务端渲染(SSR)初探
2017/06/19 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
深入理解vue $refs的基本用法
2017/07/13 Javascript
JS排序算法之希尔排序与快速排序实现方法
2017/12/12 Javascript
vue引入新版 vue-awesome-swiper插件填坑问题
2018/01/25 Javascript
解决vue项目刷新后,导航菜单高亮显示的位置不对问题
2019/11/01 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
在Vue中使用antv的示例代码
2020/06/29 Javascript
Vue2.0 $set()的正确使用详解
2020/07/28 Javascript
跟老齐学Python之使用Python查询更新数据库
2014/11/25 Python
Python中的单行、多行、中文注释方法
2018/07/19 Python
numpy向空的二维数组中添加元素的方法
2018/11/01 Python
python实现多层感知器MLP(基于双月数据集)
2019/01/18 Python
opencv python 图像轮廓/检测轮廓/绘制轮廓的方法
2019/07/03 Python
使用Python文件读写,自定义分隔符(custom delimiter)
2020/07/05 Python
美国著名首饰网站:BaubleBar
2016/08/29 全球购物
葡萄牙鞋子品牌:Fair
2016/12/10 全球购物
美国社交购物市场:MassGenie
2019/02/18 全球购物
工程概预算专业毕业生求职信
2013/10/04 职场文书
企业文化口号
2014/06/12 职场文书
党委班子纠正“四风”问题整改措施
2014/10/28 职场文书
自查自纠整改报告
2014/11/06 职场文书
2014年预算员工作总结
2014/12/05 职场文书