当前位置: 首页 > 后端技术 > PHP

PHP实现Snowflake生成分布式唯一ID

时间:2023-03-29 15:26:46 PHP

Twitter的snowflake广泛用于分布式生成唯一UUID,网上也有很多基于snowflake的一些变种的算法。snowflake生成的UUID很多都是在分布式场景下使用的。在网上看了一些,里面有几个没有考虑线程安全的PHP实现。现在PHP有了Swoole锁和协程的支持,对于我们开发线程安全和高并发的模拟是非常方便的。这里我们使用PHP结合Swoole来学习实现最简单的snowflake(好久没写PHP了,感觉没有IDE,不会再写PHP了)。先看snowflake的如下结构:生成的值是64位,分为4部分:第一位是符号位,最高位是0,表示正数。第二部分,41位,用于记录生成ID时的时间戳,单位是毫秒,所以这部分代表的取值范围是2^41-1(69年),是相对于a的偏移量一定时间。第三部分的10位表示工作节点的ID,表示取值范围为2^10-1,相当于支持1024个节点。第四部分的12位表示每个工作节点每毫秒产生的循环自增id,最多可以产生2^12-1个id。以毫秒为单位重新递增,先粘贴代码:self::WORKER_MAX){trigger_error("WorkerID超出范围");退出(0);}$this->timestamp=0;$this->workerId=$workerId;$this->序列=0;$this->lock=newswoole_lock(SWOOLE_MUTEX);}/***生成ID*@returnint*/publicfunctiongetId(){$this->lock->lock();//记得在这里锁定$now=$this->now();如果($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)<workerId<序列;$this->锁定->解锁();//解锁return$id;}/***获取当前毫秒数*@returnstring*/publicfunctionnow(){returnsprintf("%.0f",microtime(true)*1000);}}其实逻辑并不复杂,解释一下代码中的位操作:-1^(-1<workerId<序列;这里将除第一个符号位外的三部分向左移动相应的偏移量,使其回到原来的位置,通过或运算重新整合到上面的雪花结构中。例如我们用3部分4位来演示合并操作:000000000010--左移0位-->000000000010000000000100--左移4位-->000001000000--或者操作-->100001000010000000001000--左移8位-->100000000000我们用Swoole的协程和channel来暴力测试生成的ID会不会重复:$snowflake=newSnowflake(1);$chan=newchan(100000);$n=100000;for($i=0;$i<$n;$i++){go(function()use($snowflake,$chan){$id=$snowflake->getId();$chan->push($id);});}去(function()使用($chan,$n){$arr=[];对于($i=0;$i<$n;$i++){$id=$chan->pop();if(in_array($id,$arr)){exit("ID已经存在");}array_push($arr,$id);}});$chan->close();回声“好”;过一段时间,就不会出现重复ID了。顺便说一下,我还用Golang实现了snowflake,并在协同程序模式下运行了相同的测试。PHP的执行时间大约是12秒,而Golang只需要1秒。文中如有错误请指正,谢谢。转载请注明:转载自瑞安是菜鸟|LNMP技术栈笔记