PHP实现Snowflake生成分布式唯一ID的方法示例

前言

Twitter 的 snowflake 在分布式生成唯一 UUID 应用还是蛮广泛的,基于 snowflake 的一些变种的算法网上也有不少。使用 snowflake 生成 UUID 很多都是在分布式场景下使用,我看了下网上有其中有几篇 PHP 实现的都没有考虑到线程安全。现在 PHP 有了 Swoole 的锁和协程的加持,对于我们开发线程安全和高并发模拟还是很方便的,这里用 PHP 结合 Swoole 来学习下实现最简单的 snowflake。

先来看以下 snowflake 的结构:

生成的数值是 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • PHP生成唯一ID之SnowFlake算法

    前言:最近需要做一套CMS系统,由于功能比较单一,而且要求灵活,所以放弃了WP这样的成熟系统,自己做一套相对简单一点的.文章的详情页URL想要做成url伪静态的格式即xxx.html 其中xxx考虑过直接用自增主键,但是感觉这样有点暴露文章数量,有同学说可以把初始值设高一点,可是还是可以通过ID差算出一段时间内的文章数量,所以需要一种可以生成唯一ID的算法. 考虑过的方法有 直接用时间戳,或者以此衍生的一系列方法 Mysql自带的uuid 以上两种方法都可以查到就不多做解释了 最终选择了Twit

  • PHP实现Snowflake生成分布式唯一ID的方法示例

    前言 Twitter 的 snowflake 在分布式生成唯一 UUID 应用还是蛮广泛的,基于 snowflake 的一些变种的算法网上也有不少.使用 snowflake 生成 UUID 很多都是在分布式场景下使用,我看了下网上有其中有几篇 PHP 实现的都没有考虑到线程安全.现在 PHP 有了 Swoole 的锁和协程的加持,对于我们开发线程安全和高并发模拟还是很方便的,这里用 PHP 结合 Swoole 来学习下实现最简单的 snowflake. 先来看以下 snowflake 的结构:

  • 基于Java代码实现游戏服务器生成全局唯一ID的方法汇总

    在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使用全局的唯一id,在游戏服务器中,全局唯一的id可以用于将来合服方便,不会出现键冲突.也可以将来在业务增长的情况下,实现分库分表,比如某一个用户的物品要放在同一个分片内,而这个分片段可能是根据用户id的范围值来确定的,比如用户id大于1000小于100000的用户在一个分片内.目前常用的有以下几种:

  • Java中生成唯一ID的方法示例

    有时我们不依赖于数据库中自动递增的字段产生唯一ID,比如多表同一字段需要统一一个唯一ID,这时就需要用程序来生成一个唯一的全局ID. UUID 从Java 5开始, UUID 类提供了一种生成唯一ID的简单方法.UUID是通用唯一识别码 (Universally Unique Identifier)的缩写,UUID来源于OSF(Open Software Foundation,开源软件基金会)的DCE(Distributed Computing Environment,分布式计算环境)规范.UU

  • Python用UUID库生成唯一ID的方法示例

    UUID介绍 UUID是128位的全局唯一标识符,通常由32字节的字符串表示.它可以保证时间和空间的唯一性,也称为GUID,全称为:UUID -- Universally Unique IDentifier,Python 中叫 UUID. 它通过MAC地址.时间戳.命名空间.随机数.伪随机数来保证生成ID的唯一性. UUID主要有五个算法,也就是五种方法来实现. uuid1()--基于时间戳.由MAC地址.当前时间戳.随机数生成.可以保证全球范围内的唯一性,但MAC的使用同时带来安全性问题,局域

  • Go实现分布式唯一ID的生成之雪花算法

    目录 背景: 特性: 雪花算法: 分布式唯一ID的生成 背景: 在分布式架构下,唯一序列号生成是我们在设计一个尤其是数据库使用分库分表的时候会常见的一个问题 特性: 全局唯一,这是基本要求,不能出现重复数字类型,趋势递增,后面的ID必须比前面的大长度短,能够提高查询效率,这也是从MySQL数据库规范出发的,尤其是ID作为主键时**信息安全,**如果ID连续生成,势必会泄露业务信息,所以需要无规则不规则高可用低延时,ID生成快,能够扛住高并发,延时足够低不至于成为业务瓶颈. 雪花算法: ​ sno

  • Go开源项目分布式唯一ID生成系统

    目录 前言 项目背景 项目使用 HTTP 方式 gRPC 方式 本地开发 项目架构 前言 今天跟大家介绍一个开源项目:id-maker,主要功能是用来在分布式环境下生成唯一 ID.上周停更了一周,也是用来开发和测试这个项目的相关代码. 美团有一个开源项目叫 Leaf,使用 Java 开发.本项目就是在此思路的基础上,使用 Go 开发实现的. 项目整体代码量并不多,不管是想要在实际生产环境中使用,还是想找个项目练手,我觉得都是一个不错的选择. 项目背景 在大部分系统中,全局唯一 ID 都是一个强需

  • 一种简单的ID生成策略: Mysql表生成全局唯一ID的实现

    生成全局ID的方法很多, 这里记录下一种简单的方案: 利用mysql的自增id生成全局唯一ID. 1. 创建一张只需要两个字段的表: CREATE TABLE `guid` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `stub` char(1) NOT NULL DEFAULT '' COMMENT '桩字段,占坑的', PRIMARY KEY (`id`), UNIQUE KEY `uk_stub` (`stub`) -- 将 st

  • Redis生成全局唯一ID的实现方法

    目录 简介: 特性: 生成规则: ID生成类: 测试类: 简介: 全局唯一ID生成器是一种在分布式系统下用来生成全局唯一ID的工具 特性: 唯一性 高性能 安全性 高可用 递增性 生成规则: 有时为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其他信息 ID组成部分: 符号位:1bit,永远为0 时间戳:31bit,以秒为单位,可以使用69年 序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID ID生成类: package com.example.util

  • php实现Mongodb自定义方式生成自增ID的方法

    本文实例讲述了php实现Mongodb自定义方式生成自增ID的方法.分享给大家供大家参考.具体分析如下: 复制代码 代码如下: //首先创建一个自动增长id集合 ids >db.ids.save({name:"user", id:0}); //可以查看一下是否成功 > db.ids.find(); { "_id" : ObjectId("4c637dbd900f00000000686c"), "name" : &q

  • JavaScript生成一个不重复的ID的方法示例

    本文介绍了JavaScript生成一个不重复的ID的方法示例,分享给大家,具体如下: /** * 生成一个用不重复的ID */ function GenNonDuplicateID():String{ } 先看看下面的几个方法 1.生成[0,1)的随机数的Math.random,例如 //我这次运行生成的是:0.5834165740043102 Math.random() 2.获取当前时间戳Date.now //现在时间戳是1482645606622 Date.now() = 152100930

随机推荐