如果需要NodeJS 版本,看这里:NodeJS 实现 Snowflake 算法, TypeScript 实现 Snowflake, 分布式唯一ID,Twitter Snowflake,高并发唯一id,全局唯一id,不重复id-CSDN博客
如果还没了解Twitter-Snowflake算法的,麻烦自行百度下!
下面是一张相关图:(图片来自网络,出处不清楚)
偶尔写下 PHP,关于唯一ID的生成,以前我写的一些小系统都是直接用“毫秒数”。。。
最近在写个小系统,发现我这招“毫秒数”会存在问题了,所以决定寻找更好的方法。。。
经过一番了解决定使用Twitter的Snowflake这方案;Snowflake的PHP代码网上可以搜索到,找到好多份我个人都感觉有点问题,所以我对着说明写了一份。
(
注意:有些朋友反映结果不正确,可能是你用了32位的php,虽然你是用64位windows系统,但如果wamp,phpstudy之类的集成的可能是32位php。
另外,如果发现有重复,可能是你用法错了,这个是要单独部署的,不是直接放到网站源码一起的,使用命令行方式运行,自己还要实现一些额外代码让程序不退出,以服务方式对外提供接口。
)
/**
* 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;
}
}
//调用
$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;
}
}
//调用
$work = new IdWork(31,31);
for($i=0; $i<10;$i++) {
$id = $work->nextId();
echo $id;
}
------END