Featured image of post 分布式id生成器

分布式id生成器

描述内容描述内容

本文阅读量

分布式id生成器

特点

  • 全局唯一性:不能岀现有重复的D标识,这是基本要求。
  • 递增性:确保生成ID对于用户或业务是递增的。
  • 高可用性:确保任何时候都能生成正确的ID。
  • 高性能性∶在高并发的环境下依然表现良好

不仅仅是用于用户ID,实际互联网中有很多场景需要能够生成类似 MySQLI自增ID这样不断增大,同时又不会重复的id。以支持业务中的高并发场景。

比较典型的场景有:电商促销时短时间内会有大量的订单涌入到系统,比如每秒10w+;明星出轨时微溥短时间内会产生大量的相关微博转发和评论消息。在这些业务场景下将数据插入数据库之前,我们需要给这些订单和消息先分配一个唯一ID,然后再保存到数据库中。对这个id的要求是希望其中能带有些时间信息,这样即使我们后端的系统对消息进行了分库分表,也能够以时间顺序对这些消息进行排序。

方案(雪花算法)

雪花算法,它是 Twitter开源的由64位整数组成分布式D,性能较高,并且在单机上递增。

  1. 第一位占用1bit,其值始终是0,没有实际作用
  2. 时间戳占用41bit,单位为毫秒,总共可以容纳约69年的时间。当然,我们的时间毫秒计数不会真的从1970年开始记,那样我们的系统跑到2039/9/723:47:35就不能用了,所以这里的时间戳只是相对于某个时间的增量,比如我们的系统上线是202007-01,那么我们完全可以把这个 timestamp当作是从2020-07-0100:00:00.000的偏移量。
  3. 工作机器id占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点
  4. 序列号占用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)
}
使用 Hugo 构建
主题 StackJimmy 设计