分布式id生成器
特点
- 全局唯一性:不能岀现有重复的D标识,这是基本要求。
- 递增性:确保生成ID对于用户或业务是递增的。
- 高可用性:确保任何时候都能生成正确的ID。
- 高性能性∶在高并发的环境下依然表现良好
不仅仅是用于用户ID,实际互联网中有很多场景需要能够生成类似 MySQLI自增ID这样不断增大,同时又不会重复的id。以支持业务中的高并发场景。
比较典型的场景有:电商促销时短时间内会有大量的订单涌入到系统,比如每秒10w+;明星出轨时微溥短时间内会产生大量的相关微博转发和评论消息。在这些业务场景下将数据插入数据库之前,我们需要给这些订单和消息先分配一个唯一ID,然后再保存到数据库中。对这个id的要求是希望其中能带有些时间信息,这样即使我们后端的系统对消息进行了分库分表,也能够以时间顺序对这些消息进行排序。
方案(雪花算法)
雪花算法,它是 Twitter开源的由64位整数组成分布式D,性能较高,并且在单机上递增。

- 第一位占用1bit,其值始终是0,没有实际作用
- 时间戳占用41bit,单位为毫秒,总共可以容纳约69年的时间。当然,我们的时间毫秒计数不会真的从1970年开始记,那样我们的系统跑到2039/9/723:47:35就不能用了,所以这里的时间戳只是相对于某个时间的增量,比如我们的系统上线是202007-01,那么我们完全可以把这个 timestamp当作是从2020-07-0100:00:00.000的偏移量。
- 工作机器id占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点
- 序列号占用12bit,用来记录同毫秒内产生的不同id。每个节点每毫秒0开始不断累加,最多可以累加到4095,同一毫米哟共可以产生4096个id
同一毫秒的id数量= 1024*4096 = 4194304
go语言实现
https://github.com/bwmarrin/snowflake

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
|
package main
import (
"fmt"
"time"
"github.com/bwmarrin/snowflake"
)
var node *snowflake.Node
func Init(startTime string,machineId int64) (err error){
var st time.Time
st,err = time.Parse("2006-01-02",startTime)
if err != nil{
return
}
snowflake.Epoch = st.UnixNano()/1000000
node, err = snowflake.NewNode(machineId)
return
}
func GenId() int64{
return node.Generate().Int64()
}
func main() {
if err:=Init("2020-09-14",1);err != nil{
fmt.Printf("init failed,err:%v\n",err)
return
}
id := GenId()
fmt.Println(id)
}
|
https://github.com/sony/sonyflake

这里的时间只用了39个bt,但时间的单位变成了10ms,所以理论上比41位表示的时间还要久(174年)。
Sequence ID
和之前的定义一致, Machine ID
其实就是节点id. sonyfake
库有以下配置参数
1
2
3
4
5
|
type Settings struct{
StartTime time.Time
MachineID func() (uint16,error)
CheckMachineID func(uint16) bool
}
|
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
|
package main
import (
"fmt"
"time"
"github.com/sony/sonyflake"
)
var (
snoyFlake *sonyflake.Sonyflake
sonyMachineID uint16
)
func getMchineID() (uint16,error){
return sonyMachineID,nil
}
func Init(startTime string,machineId uint16) (err error){
sonyMachineID = machineId
var st time.Time
st,err = time.Parse("2006-01-02",startTime)
if err != nil{
return err
}
settings := sonyflake.Settings{
StartTime: st,
MachineID: getMchineID,
CheckMachineID: nil,
}
snoyFlake = sonyflake.NewSonyflake(settings)
return
}
func GenId() (id uint64,err error){
if snoyFlake == nil {
err = fmt.Errorf("snoy flake not inited")
return
}
id,err = snoyFlake.NextID()
return
}
func main() {
if err:=Init("2020-09-14",1);err != nil{
fmt.Printf("init failed,err:%v\n",err)
return
}
id,_ := GenId()
fmt.Println(id)
}
|