hujiao-gui/bootstrap/Snowflake.php

78 lines
2.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
//namespace anerg\helper;
/**
* Twitter的Snowflake生成全局唯一ID的PHP实现
*
* 可实现分布式全局唯一ID的生成
*
* 因为PHP不是常驻内存运行所以无法实现原版的队列编号采用随机数代替
* 可以最大程度避免并发请求时生成相同的ID
* 理论上当MAX_RANDOM_BIT为12也就是随机数最大值为4095时
* 每毫秒需生成的id小于1000即可以安全的生成不冲突的id
*
* @author Coeus <r.anerg@gmail.com>
*/
class Snowflake
{
//开始时间戳,固定小于当前时间的毫秒,用于缩减时间位数
const EPOCH = 1575388800000;
//最大机器标识位数8->255 10->1023
const MAX_MACHINE_BIT = 8;
//最大随机数位数12->4095
const MAX_RANDOM_BIT = 12;
/**
* 获取一个10进制唯一ID
*
* 如果$machineId为0则会忽略掉机器标识位
*
* @param integer $machineId
* @param array $options 数组形式只包含epochmaxMachineBit和maxRandomBit
* @return integer
*/
public static function nextId($machineId = 1, $options = [])
{
$epoch = isset($options['epoch']) ? $options['epoch'] : self::EPOCH;
$maxMachineBit = isset($options['maxMachineBit']) ? $options['maxMachineBit'] : self::MAX_MACHINE_BIT;
$maxRandomBit = isset($options['maxRandomBit']) ? $options['maxRandomBit'] : self::MAX_RANDOM_BIT;
//机器标识范围判断
$maxMachineId = ~(-1 << $maxMachineBit);
if ($machineId > $maxMachineId || $machineId < 0) {
throw new \Exception("MachineId can't be less than " . $maxMachineId . " or greater than 0");
}
//毫秒时间戳
$time = \floor(\microtime(true) * 1000);
$time -= $epoch;
//生成随机数因为php不是内存持久化的在并发时无法做到维护一个唯一的序列所以用随机数替代
$random = \mt_rand(0, ~(-1 << $maxRandomBit));
//组合数据 {时间戳差值|机器标识|随机数}
//机器标识为0则抛弃掉机器标识
if ($machineId == 0) {
//时间戳要左移的位数
$timeLeftShift = $maxRandomBit;
$nextId = ($time << $timeLeftShift) | $random;
} else {
//时间戳要左移的位数
$timeLeftShift = $maxRandomBit + $maxMachineBit;
$nextId = ($time << $timeLeftShift) | ($machineId << $maxRandomBit) | $random;
}
return $nextId;
}
/**
* 获取一个36进制的唯一ID
*
* @param integer $machineId
* @return string
*/
public static function nextHash($machineId = 0, $options = [])
{
return \strtoupper(\base_convert(self::nextId($machineId, $options), 10, 36));
}
}