安装包 go get -u github.com/bwmarrin/snowflake
总体而言,snowflake生成的ID是一个 int64 的整形,它由一下几个部分组成。
1、unused 1bit
2、time 41bit 毫秒
3、datacenter_id 5bit 数据中心ID
4、work_id 5bit 机器ID
5、sequence_id 12bit 循环自增ID,到达最大值 1111 1111 1111 后归零,如果时间来到了下一毫秒也会归零。
work_id-5bit
|
|
0|0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0|0000 0|0000 0|0000 0000 0000
| | | |
| | | |
unused-1bit time_ms-41bit center_id-5bit sequence_id-12bit
1、数据中心ID加机器ID共有10位,因此最大值为1024,当然也可以合并一起叫做Node。
2、同一个Node上在同一毫秒内可以产生 2^12 = 4096
条,每秒就是 409.6
万条。
3、表示 timestamp 的 41 位,可以支持我们使用 69 年。当然,我们的时间毫秒计数不会真的从 1970 年开始记,那样我们的系统跑到 2039/9/7 23:47:35
就不能用了,所以这里的 timestamp 只是相对于某个时间的增量,比如我们的系统上线是 2018-08-01
,那么我们可以把这个 timestamp 当作是从 2018-08-01 00:00:00.000
的偏移量,这个后面看源码。
4、timestamp , datacenter_id , worker_id 和 sequence_id 这四个字段中,timestamp 和 sequence_id 是由程序在运行期生成的。但 datacenter_id 和worker_id 需要我们在部署阶段就能够获取得到,并且一旦程序启动之后,就是不可更改的了(想想,如果可以随意更改,可能被不慎修改,造成最终生成的 id 有冲突)。
5、一般不同数据中心的机器,会提供对应的获取数据中心 id 的 API,所以datacenter_id 我们可以在部署阶段轻松地获取到。而 worker_id 是我们逻辑上给机器分配的一个 id,这个要怎么办呢?比较简单的想法是由能够提供这种自增 id 功能的工具来支持。获取到 worker_id 之后,就把这个 worker_id 直接持久化到本地,以避免每次上线时都需要获取新的 worker_id 。让单实例的 worker_id 可以始终保持不变。
6、一个可行的方案是,预先在一个数据库中设置好,服务器IP对应的datacenter_id和worker_id,在启动的时候调一次API拿到自己的参数。
7、考虑到集群中即使有单个 id 生成服务的实例挂了,也就是损失一段时间的一部分id,其他服务依然可以生成自己的ID。
8、Node参数要节省着使用,如果有幸你的公司活过了69年,那么剩下的Node值就派上用场了。
package main
import (
"fmt"
"github.com/bwmarrin/snowflake"
)
func main() {
node, err := snowflake.NewNode(1)
if err != nil {
fmt.Println(err)
return
}
id := node.Generate()
// Print out the ID in a few different ways.
fmt.Printf("Bit ID: %b\n", id) //1010111110111111110111011110101111101000000000001000000000000
fmt.Printf("Int64 ID: %d\n", id) //1583010585308565504
fmt.Printf("String ID: %s\n", id) //1583010585308565504
fmt.Printf("Base2 ID: %s\n", id.Base2()) //1010111110111111110111011110101111101000000000001000000000000
fmt.Printf("Base64 ID: %s\n", id.Base64()) //MTU4MzAxMDU4NTMwODU2NTUwNA==
// Print out the ID's timestamp
fmt.Printf("ID Time : %d\n", id.Time()) //1666254109109
// Print out the ID's node number
fmt.Printf("ID Node : %d\n", id.Node()) //1
// Print out the ID's sequence number
fmt.Printf("ID Step : %d\n", id.Step()) //0
// Generate and print, all in one.
fmt.Printf("ID : %d\n", node.Generate().Int64()) //1583010585312759808
}
不够64位的高位补0,因此没打印出来。
设置初始时间,默认值为1288834974657
,接收一个毫秒的时间戳,timestamp 部分存储的就是增量的毫秒时间
// Epoch is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds
// You may customize this to set a different epoch for your application.
Epoch int64 = 1288834974657
实例化节点
// NewNode returns a new snowflake node that can be used to generate snowflake
// IDs
func NewNode(node int64) (*Node, error) {
// re-calc in case custom NodeBits or StepBits were set
// DEPRECATED: the below block will be removed in a future release.
mu.Lock()
nodeMax = -1 ^ (-1 << NodeBits)
nodeMask = nodeMax << StepBits
stepMask = -1 ^ (-1 << StepBits)
timeShift = NodeBits + StepBits
nodeShift = StepBits
mu.Unlock()
n := Node{}
n.node = node
n.nodeMax = -1 ^ (-1 << NodeBits)
n.nodeMask = n.nodeMax << StepBits
n.stepMask = -1 ^ (-1 << StepBits)
n.timeShift = NodeBits + StepBits
n.nodeShift = StepBits
if n.node < 0 || n.node > n.nodeMax {
return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10))
}
var curTime = time.Now()
// add time.Duration to curTime to make sure we use the monotonic clock if available
n.epoch = curTime.Add(time.Unix(Epoch/1000, (Epoch%1000)*1000000).Sub(curTime))
return &n, nil
}
重点看生成ID的方法
// Generate creates and returns a unique snowflake ID
// To help guarantee uniqueness
// - Make sure your system is keeping accurate system time
// - Make sure you never have multiple nodes running with the same node ID
func (n *Node) Generate() ID {
// 互斥锁
n.mu.Lock()
// 增量的时间
now := time.Since(n.epoch).Nanoseconds() / 1000000
// 同一个毫秒的时候,sequence_id 自增
if now == n.time {
// sequence_id自增,如果达到了最大值,就会回到0
n.step = (n.step + 1) & n.stepMask
if n.step == 0 {
for now <= n.time {
now = time.Since(n.epoch).Nanoseconds() / 1000000
}
}
} else {
n.step = 0
}
n.time = now
// 拼接
r := ID((now)<<n.timeShift |
(n.node << n.nodeShift) |
(n.step),
)
n.mu.Unlock()
return r
}