Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。
Snowflake算法核心
把时间戳,工作机器id,序列号组合在一起。
除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。下文会具体分析。
结构为:
0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---0000000000 00
在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
**
* ID 生成策略
* 毫秒级时间41位+机器ID 10位+毫秒内序列12位。
* 0 41 51 64
+———–+——+——+
|time |pc |inc |
+———–+——+——+
* 最高位bit标记为不可用
* 前41bits是以微秒为单位的timestamp。
* 接着10bits是事先配置好的机器ID。
* 最后12bits是累加计数器。
* macheine id(10bits)标明最多只能有1024台机器同时产生ID,sequence number(12bits)也标明1台机器1ms中最多产生4096个ID,
*
*/
class SnowFlake{
private static
epoch=1462264156000;publicfunctioncreateID(
e
p
o
c
h
=
1462264156000
;
p
u
b
l
i
c
f
u
n
c
t
i
o
n
c
r
e
a
t
e
I
D
(
machineId){
/*
* Time - 41 bits
*/
time=floor(microtime(true)∗1000);/∗∗Substractcustomepochfromcurrenttime∗/
t
i
m
e
=
f
l
o
o
r
(
m
i
c
r
o
t
i
m
e
(
t
r
u
e
)
∗
1000
)
;
/
∗
∗
S
u
b
s
t
r
a
c
t
c
u
s
t
o
m
e
p
o
c
h
f
r
o
m
c
u
r
r
e
n
t
t
i
m
e
∗
/
time -= SnowFlake::
epoch;/∗∗flagnumber−1bits−cannotchange,beacauseisshoulebeapositivenumber∗/
e
p
o
c
h
;
/
∗
∗
f
l
a
g
n
u
m
b
e
r
−
1
b
i
t
s
−
c
a
n
n
o
t
c
h
a
n
g
e
,
b
e
a
c
a
u
s
e
i
s
s
h
o
u
l
e
b
e
a
p
o
s
i
t
i
v
e
n
u
m
b
e
r
∗
/
suffix = 0;
/*
* Create a base and add time to it
*/
base=decbin(pow(2,40)−1+
b
a
s
e
=
d
e
c
b
i
n
(
p
o
w
(
2
,
40
)
−
1
+
time);
//
base=sprintf("
b
a
s
e
=
s
p
r
i
n
t
f
(
"
time));
/*
* Configured machine id - 10 bits - up to 512 machines
*/
machineid=decbin(pow(2,9)−1+
m
a
c
h
i
n
e
i
d
=
d
e
c
b
i
n
(
p
o
w
(
2
,
9
)
−
1
+
machineId);
//
machineid=sprintf("
m
a
c
h
i
n
e
i
d
=
s
p
r
i
n
t
f
(
"
machineId));
/*
* sequence number - 12 bits - up to 2048 random numbers per machine
*/
random=mtrand(1,pow(2,11)−1);
r
a
n
d
o
m
=
m
t
r
a
n
d
(
1
,
p
o
w
(
2
,
11
)
−
1
)
;
random = decbin(pow(2,11)-1 + $random);
//
random=mtrand(1,pow(2,12)−1);//
r
a
n
d
o
m
=
m
t
r
a
n
d
(
1
,
p
o
w
(
2
,
12
)
−
1
)
;
/
/
random = sprintf("%012s", decbin(
random));/∗∗拼装
r
a
n
d
o
m
)
)
;
/
∗
∗
拼
装
base
*/
base=
b
a
s
e
=
suffix.
base.
b
a
s
e
.
machineid.
random;/∗∗讲二进制的base转换成long∗/
r
a
n
d
o
m
;
/
∗
∗
讲
二
进
制
的
b
a
s
e
转
换
成
l
o
n
g
∗
/
base = bindec(
base);
b
a
s
e
)
;
id = sprintf('%.0f',
base);return
b
a
s
e
)
;
r
e
t
u
r
n
id;
}
}
我这里的PHP代码序列号是随机产生的,因为我们的业务还不达不到需要有序生成的必要, 而且对于PHP来说要序列化生成必须把这个序列存储到缓存里面去。
详细代码可参考:https://github.com/twitter/snowflake
</div>