Twitter的分布式自增ID算法Snowflake的PHP实现,Snowflake PHP版本,高并发唯一id,全局唯一id,不重复id

如果还没了解Twitter-Snowflake算法的,麻烦自行百度下!

下面是一张相关图:(图片来自网络,出处不清楚)


Twitter的分布式自增ID算法Snowflake的PHP实现,Snowflake PHP版本,高并发唯一id,全局唯一id,不重复id_第1张图片


偶尔写下 PHP,关于唯一ID的生成,以前我写的一些小系统都是直接用“微秒数”。。。

最近在写个小系统,发现我这招“微秒数”会存在问题了,所以决定寻找更好的方法。。。

经过一番了解决定使用Twitter的Snowflake这方案;


Snowflake的PHP代码网上可以搜索到,开始我也打算直接用网友写好的,

在某博客上正好看到有一代码,可能排版问题看起来有点乱(可能是我对PHP不熟悉),

然后不小心看到评论上有人说“同学,PHP版本根本就是错的嘛。仔细看看把!”。。。

好吧,这么一说我都不敢用了,再去搜索了一下看有没其它的,发现几乎都是这篇代码;


懒得找了,对着说明自己写一份呗,发到博客上,有需要的朋友可参考下!

先说明下,我并不是phper,对php也不太熟悉,我这人就是边查文档边写代码的。。。

我对比了java的运行结果,我写的这份结果应该正确,只是性能上就请自行调优下!

如果发现有问题的,麻烦各位指正下!!


(注意:有些朋友反映结果不正确,原因应该是你用了32位的php,虽然你是用64位windows系统,但如果wamp,phpstudy之类的集成的可能是32位php,我使用的是64位Linux运行正常,

请用64位的php,这算法就是64bit 的嘛)


/**
 * Created by PhpStorm.
 * User: envon
 * Date: 2016/10/27
 * Time: 17:17
 */

class IdWork
{

    //开始时间,固定一个小于当前时间的毫秒数即可
    const twepoch =  1474992000000;//2016/9/28 0:0:0

    //机器标识占的位数
    const workerIdBits = 10;

    //毫秒内自增数点的位数
    const sequenceBits = 12;

    protected $workId = 0;

    //要用静态变量
    static $lastTimestamp = -1;
    static $sequence = 0;


    function __construct($workId){
        //机器ID范围判断
        $maxWorkerId = -1 ^ (-1 << self::workerIdBits);
        if($workId > $maxWorkerId || $workId< 0){
            throw new Exception("workerId can't be greater than ".$this->maxWorkerId." or less than 0");
        }
        //赋值
        $this->workId = $workId;
    }

    //生成一个ID
    public function nextId(){
        $timestamp = $this->timeGen();
        $lastTimestamp = self::$lastTimestamp;
        //判断时钟是否正常
        if ($timestamp < $lastTimestamp) {
            throw new Exception("Clock moved backwards.  Refusing to generate id for %d milliseconds", ($lastTimestamp - $timestamp));
        }
        //生成唯一序列
        if ($lastTimestamp == $timestamp) {
            $sequenceMask = -1 ^ (-1 << self::sequenceBits);
            self::$sequence = (self::$sequence + 1) & $sequenceMask;
            if (self::$sequence == 0) {
                $timestamp = $this->tilNextMillis($lastTimestamp);
            }
        } else {
            self::$sequence = 0;
        }
        self::$lastTimestamp = $timestamp;
        //
        //时间毫秒/数据中心ID/机器ID,要左移的位数
        $timestampLeftShift = self::sequenceBits + self::workerIdBits;
        $workerIdShift = self::sequenceBits;
        //组合3段数据返回: 时间戳.工作机器.序列
        $nextId = (($timestamp - self::twepoch) << $timestampLeftShift) | ($this->workId << $workerIdShift) | self::$sequence;
        return $nextId;
    }

    //取当前时间毫秒
    protected function timeGen(){
        $timestramp = (float)sprintf("%.0f", microtime(true) * 1000);
        return  $timestramp;
    }

    //取下一毫秒
    protected function tilNextMillis($lastTimestamp) {
        $timestamp = $this->timeGen();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->timeGen();
        }
        return $timestamp;
    }

}


//调用
header("Content-Type: text/html; charset=utf-8");
$work = new IdWork(1023);
for($i=0; $i<10;$i++) {
    $id = $work->nextId();
    echo $id . "
"; }


下面这段也差不多是一样,把那个10位的机器标识折成两段,(workId、 datacenterId);


class IdWork
{

    //开始时间,固定一个小于当前时间的毫秒数即可
    const twepoch =  1474992000000;//2016/9/28 0:0:0

    //机器标识占的位数
    const workerIdBits = 5;

    //数据中心标识占的位数
    const datacenterIdBits = 5;

    //毫秒内自增数点的位数
    const sequenceBits = 12;

    protected $workId = 0;
    protected $datacenterId = 0;

    static $lastTimestamp = -1;
    static $sequence = 0;


    function __construct($workId, $datacenterId){
        //机器ID范围判断
        $maxWorkerId = -1 ^ (-1 << self::workerIdBits);
        if($workId > $maxWorkerId || $workId< 0){
            throw new Exception("workerId can't be greater than ".$this->maxWorkerId." or less than 0");
        }
        //数据中心ID范围判断
        $maxDatacenterId = -1 ^ (-1 << self::datacenterIdBits);
        if ($datacenterId > $maxDatacenterId || $datacenterId < 0) {
            throw new Exception("datacenter Id can't be greater than ".$maxDatacenterId." or less than 0");
        }
        //赋值
        $this->workId = $workId;
        $this->datacenterId = $datacenterId;
    }

    //生成一个ID
    public function nextId(){
        $timestamp = $this->timeGen();
        $lastTimestamp = self::$lastTimestamp;
        //判断时钟是否正常
        if ($timestamp < $lastTimestamp) {
            throw new Exception("Clock moved backwards.  Refusing to generate id for %d milliseconds", ($lastTimestamp - $timestamp));
        }
        //生成唯一序列
        if ($lastTimestamp == $timestamp) {
            $sequenceMask = -1 ^ (-1 << self::sequenceBits);
            self::$sequence = (self::$sequence + 1) & $sequenceMask;
            if (self::$sequence == 0) {
                $timestamp = $this->tilNextMillis($lastTimestamp);
            }
        } else {
            self::$sequence = 0;
        }
        self::$lastTimestamp = $timestamp;
        //
        //时间毫秒/数据中心ID/机器ID,要左移的位数
        $timestampLeftShift = self::sequenceBits + self::workerIdBits + self::datacenterIdBits;
        $datacenterIdShift = self::sequenceBits + self::workerIdBits;
        $workerIdShift = self::sequenceBits;
        //组合4段数据返回: 时间戳.数据标识.工作机器.序列
        $nextId = (($timestamp - self::twepoch) << $timestampLeftShift) |
            ($this->datacenterId << $datacenterIdShift) |
            ($this->workId << $workerIdShift) | self::$sequence;
        return $nextId;
    }

    //取当前时间毫秒
    protected function timeGen(){
        $timestramp = (float)sprintf("%.0f", microtime(true) * 1000);
        return  $timestramp;
    }

    //取下一毫秒
    protected function tilNextMillis($lastTimestamp) {
        $timestamp = $this->timeGen();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->timeGen();
        }
        return $timestamp;
    }

}


//调用
header("Content-Type: text/html; charset=utf-8");
$work = new IdWork(31,31);
for($i=0; $i<10;$i++) {
    $id = $work->nextId();
    echo $id . "
"; }





------END


你可能感兴趣的:(PHP)