gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 30 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章PHP实现Snowflake生成分布式唯一ID的方法示例由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

前言 。

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,超出归零等待下一毫秒重新自增。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?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
2
3
-1 ^ (-1 << self::SEQUENCE_BITS)
就是-1的二进制表示为1的补码,其实等同于 :
2**self::SEQUENCE_BITS - 1

最后部分左移后或运算:

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

原文链接:https://segmentfault.com/a/1190000023789652 。

最后此篇关于PHP实现Snowflake生成分布式唯一ID的方法示例的文章就讲到这里了,如果你想了解更多关于PHP实现Snowflake生成分布式唯一ID的方法示例的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com