Repository navigation
Expand file tree
/
Copy pathSnowflake.php
More file actions
145 lines (130 loc) · 4.74 KB
/
Copy pathSnowflake.php
File metadata and controls
145 lines (130 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php
/**
* Class IdWorker snowflake算法生成unique id
* @package Snowflake
*/
class Snowflake
{
//开始时间
const EPOCH_OFFSET = 1293811200000;
//首位
const SIGN_BITS = 1;
//时间戳相减占用的位数
const TIMESTAMP_BITS = 41;
//数据中心位数
const DATA_CENTER_BITS = 5;
//机器位数
const MACHINE_ID_BITS = 5;
//同一毫秒内自增的位数
const SEQUENCE_BITS = 12;
/**
* @var mixed
*/
protected $dataCenterId;
/**
* @var mixed
*/
protected $machineId;
/**
* @var null|int
*/
protected $lastTimestamp = null;
/**
* @var int
*/
protected $sequence = 1;
//标志位需要位移的长度
protected $signLeftShift = self::TIMESTAMP_BITS + self::DATA_CENTER_BITS + self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
//时间差值需要位移的长度
protected $timestampLeftShift = self::DATA_CENTER_BITS + self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
//数据中心需要位移的长度
protected $dataCenterLeftShift = self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
//机器需要位移的长度
protected $machineLeftShift = self::SEQUENCE_BITS;
//最大自增数
//下边语法执行效果 等于 (1 << 12) - 1;
//-1 << self::SEQUENCE_BITS = 1111111111111111111111111111111111111111111111111111000000000000
protected $maxSequenceId = ~ (-1 << self::SEQUENCE_BITS);
//最大机器数
protected $maxMachineId = ~ (-1 << self::MACHINE_ID_BITS);
//最大数据中心机器数
protected $maxDataCenterId = ~ (-1 << self::DATA_CENTER_BITS);
/**
* IdWorker constructor.
* @param $dataCenter_id
* @param $machine_id
* @throws Exception
*/
public function __construct($dataCenter_id, $machine_id)
{
if ($dataCenter_id > $this->maxDataCenterId) {
throw new \Exception('data center id should between 0 and ' . $this->maxDataCenterId);
}
if ($machine_id > $this->maxMachineId) {
throw new \Exception('machine id should between 0 and ' . $this->maxMachineId);
}
$this->dataCenterId = $dataCenter_id;
$this->machineId = $machine_id;
}
/**
* @return string
* @throws Exception
*/
public function id()
{
$sign = 0;
$timestamp = $this->getUnixTimestamp();
if ($timestamp < $this->lastTimestamp) {
throw new \Exception('Clock moved backwards!');
}
if ($this->lastTimestamp == $timestamp) {
$this->sequence++;
if ($this->sequence > $this->maxSequenceId) {
return $this->id();
}
} else {
$this->sequence = 1;
}
$this->lastTimestamp = $timestamp;
//算出时间差值
$time = (int)($timestamp - self::EPOCH_OFFSET);
//($sign << $this->signLeftShift) =》 1 左移 63 位 : 1000000000000000000000000000000000000000000000000000000000000000
//($time << $this->timestampLeftShift) 时间左移 22 位
// ($this->dataCenterId << $this->dataCenterLeftShift) 数据中心 左移 17位
// ($this->machineId << $this->machineLeftShift) 机器中心左移 12 位
$id = ($sign << $this->signLeftShift) | ($time << $this->timestampLeftShift) | ($this->dataCenterId << $this->dataCenterLeftShift) | ($this->machineId << $this->machineLeftShift) | $this->sequence;
return (string)$id;
}
/**
* 解析自增ID
* @param $id
* @return array
*/
public function parse($id)
{
//转化为二进制字符串
$binUuid = decbin($id);
$len = strlen($binUuid);
$sequenceStart = $len - self::SEQUENCE_BITS;
//截取自增的二进制字符串
$sequence = substr($binUuid, $sequenceStart, self::SEQUENCE_BITS);
$machineIdStart = $len - self::MACHINE_ID_BITS - self::SEQUENCE_BITS;
$machineId = substr($binUuid, $machineIdStart, self::MACHINE_ID_BITS);
$dataCenterIdStart = $len - self::DATA_CENTER_BITS - self::MACHINE_ID_BITS - self::SEQUENCE_BITS;
$dataCenterId = substr($binUuid, $dataCenterIdStart, self::DATA_CENTER_BITS);
$timestamp = substr($binUuid, 0, $dataCenterIdStart);
$realTimestamp = bindec($timestamp) + self::EPOCH_OFFSET;
$timestamp = substr($realTimestamp, 0, -3);
$microSecond = substr($realTimestamp, -3);
return [
'timestamp' => date('Y-m-d H:i:s', $timestamp) . '.' . $microSecond,
'dataCenterId' => bindec($dataCenterId),
'machineId' => bindec($machineId),
'sequence' => bindec($sequence),
];
}
private function getUnixTimestamp()
{
return floor(microtime(true) * 1000);
}
}