8-3.分布式id 库snowflake和sonyflake

分布式ID的特点

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

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

  • 电商促销时短时间内会有大量的订单涌入到系统,比如每秒10w+;
  • 明星出轨时微博短时间内会产生大量的相关微博转发和评论消息。

在这些业务场景下将数据插入数据库之前,我们需要给这些订单和消息先分配一个唯一ID,然后再保存到数据库中。对这个id的要求是希望其中能带有一些时间信息,这样即使我们后端的系统对消息进行了分库分表,也能够以时间顺序对这些消息进行排序。

snowflake

1. 原理

  • Twitter 的 Snowflake 算法规范

    • 整个ID是存储在int64中的63位整数
    • 41位用于存储收到请求的时间戳 - 单位毫秒
    • 10位用于存储节点 - 范围从0到1023
    • 12位用于存储序列号 - 范围从0到4095

    image-20220427111710044

  1. 由于最高位是标识位,为1表示为负数,因此最高位不使用。
  2. 41bit 保存时间戳,精确到毫秒。也就是说最大可以使用的年限是69年。
  3. 10bit 的机器位,能部属在1024台机器节点来生成ID。
  4. 12bit 的序列号,一毫秒最大生成惟一ID的数量为4096个。
  • 1 bit:不用,为啥呢?因为二进制里第一个bit为如果是1,那么都是负数,但是我们生成的id都是正数,所以第一个bit统一都是0

  • 41 bit:41 bit可以表示的数字多达2^41 - 1,也就是可以标识2 ^ 41 - 1个毫秒值,换算成年就是表示69年的时间。

  • 10 bit:代表的是这个服务最多可以部署在2^10台机器上哪,也就是1024台机器。但是10 bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2 ^ 5个机房(32个机房),每个机房里可以代表2 ^ 5个机器(32台机器)。

  • 12 bit:这个是用来记录同一个毫秒内产生的不同id,12 bit可以代表的最大正整数是2 ^ 12 - 1 = 4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id

同一台机器上,同一毫秒内可以产生4096个id,一秒共400w个id,理论完全够用。

2. demo

 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
package main

import (
	"fmt"
	"github.com/bwmarrin/snowflake"
	"log"
	"time"
)

func BuildSnowFlakeId(starTime string, machineID int64) (id int64, err error) {
	var st time.Time
	st, err = time.Parse("2006-01-02", starTime)
	if err != nil {
		return 0, err
	}
	snowflake.Epoch = st.UnixNano() / 1000000
	node, err := snowflake.NewNode(machineID)
	if err != nil {
		return 0, err
	}
	id = node.Generate().Int64()
	return id, err
}

func main() {
	startTime := time.Now().Format("2006-01-02")
	snowFlakeId, err := BuildSnowFlakeId(startTime, 1)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(snowFlakeId)

}

sonyflake

1. 原理

  • Snowflake算法是相当灵活的,我们可以根据自己的业务需要,对63 bit的的各个部分进行增减。索尼公司的Sonyflake对原生的Snowflake进行改进,重新分配了各部分的bit位:

    image-20220426204201273

  1. 由于最高位是标识位,为1表示为负数,因此最高位不使用.
  2. 39bit 来保存时间戳,与原生的Snowflake不同的地方是,Sonyflake是以10毫秒为单位来保存时间的。这样的话,可以使用的年限为 174年 比Snowflake长太多了。
  3. 8bit 做为序列号,每10毫最大生成256个,1秒最多生成25600个,比原生的Snowflake少好多,如果感觉不够用,目前的解决方案是跑多个实例生成同一业务的ID来弥补。
  4. 16bit 做为机器号,默认的是当前机器的私有IP的最后两位

2. demo

 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
package main

import (
	"fmt"
	"time"

	"github.com/sony/sonyflake"
)

func BuildSonyFlakeID(starTime string, machineID uint16) (id int64, err error) {
	var sf *sonyflake.Sonyflake
	var st sonyflake.Settings
	st.MachineID = func() (uint16, error) {
		return machineID, nil
	}
	starT, err := time.Parse("2006-01-02", starTime)
	if err != nil {
		return 0, err
	}
	st.StartTime = starT
	sf = sonyflake.NewSonyflake(st)
	if sf == nil {
		panic("sonyflake not created")
	}
	idUint64, err := sf.NextID()
	if err != nil {
		return 0, err
	}
	return int64(idUint64), err
}

func main() {
	id, err := BuildSonyFlakeID(time.Now().Format("2006-01-02"), 1)
	if err != nil {
		panic(err)
	}
	fmt.Println(id)
}
Buy me a coffee~
Fred 支付宝支付宝
Fred 微信微信
0%