PHP使用SnowFlake演算法生成唯一ID
阿新 • • 發佈:2019-02-08
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;
}
}