暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

雪花算法如何生成用户ID?有什么高明之处?

了凡银河系 2021-07-29
1422

资源系列

Go语言学习资料汇总

基础篇

Go语言基础篇

Go语言的结构体你了解多少?

你真的知道函数和方法么?

Go语言中的反射和接口是什么?

进阶篇

十分钟!零基础让你迅速了解go语言gin框架


前言

雪花算法生成用户ID

分布式ID生成器

分布式ID的特点

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

snowflake算法介绍

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

设计思想

  1. 第一位 占用1bit,其值始终是0,没有实际作用。

  2. 时间戳 占用41bit,单位为毫秒,总共可以容纳约69年的时间。当然,我们的时间毫秒计数不会真的从1970年开始记,那样我们的系统跑到 2039/9/7 23:47:35 就不能用了,所以这里的时间戳只是相对于某个时间的增量,比如我们的系统上线是2020-07-01,那么我们完全可以把这个timestamp当作是从 2020-07-01 00:00:00.000 的偏移量。

  3. 工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点。

  4. 序列号 占用12bit,用来记录同毫秒内产生的不同id。每个节点每毫秒O开始不断累加,最多可以累加到4095,同一毫秒一共可以产生4096个ID。

SnowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢?

同一毫秒的ID数量=1024 X 4096= 4194304

snowflake的Go实现

Twitter

链接https://github.com/bwmarrin/snowflake

github.com/bwmarrin/snowflake 是一个相当轻量化的snowflake的Go实现。

import (
"fmt"
_ "github.com/onsi/gomega"
"time"

sf "github.com/bwmarrin/snowflake"
)

var node *sf.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
}
sf.Epoch = st.UnixNano() / 1000000
node, err = sf.NewNode(machineID)
return
}
func GenID() int64 {
return node.Generate().Int64()
}

func main() {
if err := Init("2020-07-10",1); err != nil {
fmt.Printf("init failed, err:%v\n", err)
return
}
id := GenID()
fmt.Println(id)
}

索尼

链接https://github.com/sony/snowflake

sonyflake是Sony公司的一个开源项目,基本思路和snowflake差不多,不过位分配上稍有不同:

这里的时间只用了39个bit,但时间的单位变成了10ms,所以理论上比41位表示的时间还要久(174年)。

Sequence ID 和之前的定义一致,Machine ID 其实就是节点id。

sonyflake 库有以下配置参数:

type Settings struct {
StartTime time.Time
MachineID func() (uint16, error)
CheckMachineID func(uint16) bool
}

其中:

  • StartTime 选项和我们之前的 Epoch 差不多,如果不设置的话,默认是从 2014-09-0100 :00:00 +0000 UTC 开始。
  • MachineID 可以由用户自定义的函数,如果用户不定义的话,会默认将本机IP的低16位作为 machine id。
  • CheckMachineID 是由用户提供的检查 MachineID 是否冲突的函数。

使用示例:

import (
"fmt"
_ "github.com/onsi/gomega"
"time"

"github.com/sony/sonyflake"
)

var (
sonyFlake *sonyflake.Sonyflake
sonyMachineID uint16
)

func getMachineID() (uint16, error) {
return sonyMachineID, nil
}

// 需传入当前的机器ID
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: getMachineID,
}
sonyFlake = sonyflake.NewSonyflake(settings)
return
}


// GenID 生成id
func GenID() (id uint16, err error) {
if sonyFlake == nil {
err = fmt.Errorf("sony flake not inited")
return
}
id, err = sonyFlake.NextID()
return
}


文章转载自了凡银河系,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论