1. 程式人生 > >PHP使用SnowFlake演算法生成唯一ID

PHP使用SnowFlake演算法生成唯一ID

namespace App\Services;
abstract class Particle {
    const EPOCH = 1479533469598;
    const max12bit = 4095;
    const max41bit = 1099511627775;

    static $machineId = null;

    public static function machineId($mId = 0) {
        self::$machineId = $mId;
    }

    public static function generateParticle
() {
/* * Time - 42 bits */ $time = floor(microtime(true) * 1000); /* * Substract custom epoch from current time */ $time -= self::EPOCH; /* * Create a base and add time to it */ $base = decbin(self::max41bit + $time
); /* * Configured machine id - 10 bits - up to 1024 machines */ if(!self::$machineId) { $machineid = self::$machineId; } else { $machineid = str_pad(decbin(self::$machineId), 10, "0", STR_PAD_LEFT); } /* * sequence number - 12 bits - up to 4096 random numbers per machine */
$random = str_pad(decbin(mt_rand(0, self::max12bit)), 12, "0", STR_PAD_LEFT); /* * Pack */ $base = $base.$machineid.$random; /* * Return unique time id no */ return bindec($base); } public static function timeFromParticle($particle) { /* * Return time */ return bindec(substr(decbin($particle),0,41)) - self::max41bit + self::EPOCH; } }

呼叫方法如下

Particle::generateParticle($machineId);//生成ID
Particle::timeFromParticle($particle);//反向計算時間戳

以上for 5000+重複率為5%

以下for100000次重複率0

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=0, $datacenterId=0){
        //機器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;
    }

}